@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
787 lines • 34.2 kB
JavaScript
import { Font } from '../styling/font';
import { IOSHelper, View } from '../core/view';
import { TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty, tabTextColorProperty, tabTextFontSizeProperty, tabBackgroundColorProperty, selectedTabTextColorProperty, iosIconRenderingModeProperty, traceMissingIcon, iosBottomAccessoryProperty, iosTabBarMinimizeBehaviorProperty } from './tab-view-common';
import { Color } from '../../color';
import { Trace } from '../../trace';
import { fontInternalProperty } from '../styling/style-properties';
import { textTransformProperty, getTransformedText } from '../text-base';
import { ImageSource } from '../../image-source';
import { profile } from '../../profiling';
import { Frame } from '../frame';
import { layout } from '../../utils/layout-helper';
import { FONT_PREFIX, isFontIconURI, isSystemURI, SYSTEM_PREFIX } from '../../utils/common';
import { SDK_VERSION } from '../../utils/constants';
import { Device, Screen } from '../../platform';
export * from './tab-view-common';
var UITabBarControllerImpl = (function (_super) {
__extends(UITabBarControllerImpl, _super);
function UITabBarControllerImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
UITabBarControllerImpl.initWithOwner = function (owner) {
var handler = UITabBarControllerImpl.new();
handler._owner = owner;
return handler;
};
UITabBarControllerImpl.prototype.viewDidLoad = function () {
_super.prototype.viewDidLoad.call(this);
this.extendedLayoutIncludesOpaqueBars = true;
};
UITabBarControllerImpl.prototype.viewWillAppear = function (animated) {
var _a;
_super.prototype.viewWillAppear.call(this, animated);
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (!owner) {
return;
}
IOSHelper.updateAutoAdjustScrollInsets(this, owner);
if (!owner.parent) {
owner.callLoaded();
}
};
UITabBarControllerImpl.prototype.viewDidDisappear = function (animated) {
var _a;
_super.prototype.viewDidDisappear.call(this, animated);
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner && !owner.parent && owner.isLoaded && !this.presentedViewController) {
owner.callUnloaded();
}
};
UITabBarControllerImpl.prototype.viewWillTransitionToSizeWithTransitionCoordinator = function (size, coordinator) {
var _this = this;
_super.prototype.viewWillTransitionToSizeWithTransitionCoordinator.call(this, size, coordinator);
coordinator.animateAlongsideTransitionCompletion(null, function () {
var _a;
var owner = (_a = _this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner && owner.items) {
owner.items.forEach(function (tabItem) { return tabItem._updateTitleAndIconPositions(); });
}
});
};
UITabBarControllerImpl.prototype.traitCollectionDidChange = function (previousTraitCollection) {
var _a;
_super.prototype.traitCollectionDidChange.call(this, previousTraitCollection);
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner) {
if (SDK_VERSION >= 13) {
if (this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
owner.notify({
eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent,
object: owner,
});
}
}
if (this.traitCollection.layoutDirection !== previousTraitCollection.layoutDirection) {
owner.notify({
eventName: IOSHelper.traitCollectionLayoutDirectionChangedEvent,
object: owner,
});
}
}
};
__decorate([
profile
], UITabBarControllerImpl.prototype, "viewWillAppear", null);
__decorate([
profile
], UITabBarControllerImpl.prototype, "viewDidDisappear", null);
return UITabBarControllerImpl;
}(UITabBarController));
var UITabBarControllerDelegateImpl = (function (_super) {
__extends(UITabBarControllerDelegateImpl, _super);
function UITabBarControllerDelegateImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
UITabBarControllerDelegateImpl.initWithOwner = function (owner) {
var delegate = UITabBarControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
};
UITabBarControllerDelegateImpl.prototype.tabBarControllerShouldSelectViewController = function (tabBarController, viewController) {
var _a;
if (Trace.isEnabled()) {
Trace.write("TabView.delegate.SHOULD_select(" + tabBarController + ", " + viewController + ");", Trace.categories.Debug);
}
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner) {
owner._handleTwoNavigationBars(false);
}
if (tabBarController.selectedViewController === viewController) {
return false;
}
return true;
};
UITabBarControllerDelegateImpl.prototype.tabBarControllerDidSelectViewController = function (tabBarController, viewController) {
var _a;
if (Trace.isEnabled()) {
Trace.write("TabView.delegate.DID_select(" + tabBarController + ", " + viewController + ");", Trace.categories.Debug);
}
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner) {
owner._onViewControllerShown(tabBarController, viewController);
}
};
UITabBarControllerDelegateImpl.ObjCProtocols = [UITabBarControllerDelegate];
return UITabBarControllerDelegateImpl;
}(NSObject));
var UINavigationControllerDelegateImpl = (function (_super) {
__extends(UINavigationControllerDelegateImpl, _super);
function UINavigationControllerDelegateImpl() {
return _super !== null && _super.apply(this, arguments) || this;
}
UINavigationControllerDelegateImpl.initWithOwner = function (owner) {
var delegate = UINavigationControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
};
UINavigationControllerDelegateImpl.prototype.navigationControllerWillShowViewControllerAnimated = function (navigationController, viewController, animated) {
var _a, _b, _c;
if (Trace.isEnabled()) {
Trace.write("TabView.moreNavigationController.WILL_show(" + navigationController + ", " + viewController + ", " + animated + ");", Trace.categories.Debug);
}
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner) {
var backToMoreWillBeVisible = (_c = (_b = navigationController.tabBarController) === null || _b === void 0 ? void 0 : _b.viewControllers) === null || _c === void 0 ? void 0 : _c.containsObject(viewController);
owner._handleTwoNavigationBars(backToMoreWillBeVisible);
}
};
UINavigationControllerDelegateImpl.prototype.navigationControllerDidShowViewControllerAnimated = function (navigationController, viewController, animated) {
var _a;
if (Trace.isEnabled()) {
Trace.write("TabView.moreNavigationController.DID_show(" + navigationController + ", " + viewController + ", " + animated + ");", Trace.categories.Debug);
}
navigationController.navigationBar.topItem.rightBarButtonItem = null;
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (owner) {
owner._onViewControllerShown(navigationController.tabBarController, viewController);
}
};
UINavigationControllerDelegateImpl.ObjCProtocols = [UINavigationControllerDelegate];
return UINavigationControllerDelegateImpl;
}(NSObject));
function updateTitleAndIconPositions(tabItem, tabBarItem, controller) {
if (!tabItem || !tabBarItem) {
return;
}
// For iOS <11 icon is *always* above the text.
// For iOS 11 icon is above the text *only* on phones in portrait mode.
const orientation = controller.interfaceOrientation;
const isPortrait = orientation !== 4 /* UIInterfaceOrientation.LandscapeLeft */ && orientation !== 3 /* UIInterfaceOrientation.LandscapeRight */;
const isIconAboveTitle = (!__VISIONOS__ && SDK_VERSION < 11) || (Device.deviceType === 'Phone' && isPortrait);
if (!tabItem.iconSource) {
if (isIconAboveTitle) {
tabBarItem.titlePositionAdjustment = {
horizontal: 0,
vertical: -20,
};
}
else {
tabBarItem.titlePositionAdjustment = { horizontal: 0, vertical: 0 };
}
}
if (!tabItem.title) {
if (isIconAboveTitle) {
tabBarItem.imageInsets = new UIEdgeInsets({
top: 6,
left: 0,
bottom: -6,
right: 0,
});
}
else {
tabBarItem.imageInsets = new UIEdgeInsets({
top: 0,
left: 0,
bottom: 0,
right: 0,
});
}
}
}
export class TabViewItem extends TabViewItemBase {
setViewController(controller, nativeView) {
this.__controller = controller;
this.setNativeView(nativeView);
}
disposeNativeView() {
this.__controller = undefined;
this.setNativeView(undefined);
}
loadView(view) {
const tabView = this.parent;
if (tabView && tabView.items) {
const index = tabView.items.indexOf(this);
if (index === tabView.selectedIndex) {
super.loadView(view);
}
}
}
_update() {
const parent = this.parent;
const controller = this.__controller;
if (parent && controller) {
const icon = parent._getIcon(this);
const index = parent.items.indexOf(this);
const title = getTransformedText(this.title, this.style.textTransform);
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(title, icon, index);
updateTitleAndIconPositions(this, tabBarItem, controller);
// There is no need to request title styles update here in newer versions as styling is handled by bar appearance instance
if (!__VISIONOS__ && SDK_VERSION < 15) {
// TODO: Repeating code. Make TabViewItemBase - ViewBase and move the colorProperty on tabViewItem.
// Delete the repeating code.
const states = getTitleAttributesForStates(parent);
applyStatesToItem(tabBarItem, states);
}
controller.tabBarItem = tabBarItem;
}
}
_updateTitleAndIconPositions() {
if (!this.__controller || !this.__controller.tabBarItem) {
return;
}
updateTitleAndIconPositions(this, this.__controller.tabBarItem, this.__controller);
}
[textTransformProperty.setNative](value) {
this._update();
}
}
export class TabView extends TabViewBase {
constructor() {
super();
this._iconsCache = {};
this.viewController = this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this));
}
createNativeView() {
// View controller can be disposed during view disposal, so make sure to create a new one if not defined
if (!this._ios) {
this.viewController = this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this));
}
return this._ios.view;
}
initNativeView() {
super.initNativeView();
this._delegate = UITabBarControllerDelegateImpl.initWithOwner(new WeakRef(this));
this._moreNavigationControllerDelegate = UINavigationControllerDelegateImpl.initWithOwner(new WeakRef(this));
}
disposeNativeView() {
this._delegate = null;
this._moreNavigationControllerDelegate = null;
this.viewController = null;
this._ios = null;
super.disposeNativeView();
}
onLoaded() {
super.onLoaded();
const selectedIndex = this.selectedIndex;
const selectedView = this.items && this.items[selectedIndex] && this.items[selectedIndex].view;
if (selectedView instanceof Frame) {
selectedView._pushInFrameStackRecursive();
}
if (this._ios) {
this._ios.delegate = this._delegate;
}
// Re-apply bottom accessory if set
if (this.iosBottomAccessory) {
this._applyBottomAccessory(this.iosBottomAccessory, false);
}
}
onUnloaded() {
if (this._ios) {
this._ios.delegate = null;
if (this._ios.moreNavigationController) {
this._ios.moreNavigationController.delegate = null;
}
}
// Avoid retaining custom view when unloading
this._applyBottomAccessory(null, false);
super.onUnloaded();
}
// @ts-ignore
get ios() {
return this._ios;
}
layoutNativeView(left, top, right, bottom) {
//
}
_setNativeViewFrame(nativeView, frame) {
//
}
onSelectedIndexChanged(oldIndex, newIndex) {
const items = this.items;
if (!items) {
return;
}
const oldItem = items[oldIndex];
if (oldItem) {
oldItem.unloadView(oldItem.view);
}
const newItem = items[newIndex];
if (newItem && this.isLoaded) {
const selectedView = items[newIndex].view;
if (selectedView instanceof Frame) {
selectedView._pushInFrameStackRecursive();
}
newItem.loadView(newItem.view);
}
super.onSelectedIndexChanged(oldIndex, newIndex);
}
onMeasure(widthMeasureSpec, heightMeasureSpec) {
const width = layout.getMeasureSpecSize(widthMeasureSpec);
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
const height = layout.getMeasureSpecSize(heightMeasureSpec);
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
const widthAndState = View.resolveSizeAndState(width, width, widthMode, 0);
const heightAndState = View.resolveSizeAndState(height, height, heightMode, 0);
this.setMeasuredDimension(widthAndState, heightAndState);
}
_onViewControllerShown(tabBarController, viewController) {
// This method could be called with the moreNavigationController or its list controller, so we have to check.
if (Trace.isEnabled()) {
Trace.write('TabView._onViewControllerShown(' + viewController + ');', Trace.categories.Debug);
}
if (tabBarController?.viewControllers && tabBarController.viewControllers.containsObject(viewController)) {
this.selectedIndex = tabBarController.viewControllers.indexOfObject(viewController);
}
else {
if (Trace.isEnabled()) {
Trace.write('TabView._onViewControllerShown: viewController is not one of our viewControllers', Trace.categories.Debug);
}
}
}
_handleTwoNavigationBars(backToMoreWillBeVisible) {
if (Trace.isEnabled()) {
Trace.write(`TabView._handleTwoNavigationBars(backToMoreWillBeVisible: ${backToMoreWillBeVisible})`, Trace.categories.Debug);
}
// The "< Back" and "< More" navigation bars should not be visible simultaneously.
let page = this.page || this._selectedView?.page;
if (!page && this._selectedView instanceof Frame) {
page = this._selectedView.currentPage;
}
if (!page || !page.frame) {
return;
}
const actionBarVisible = page.frame._getNavBarVisible(page);
if (backToMoreWillBeVisible && actionBarVisible) {
if (page.frame.ios) {
page.frame.ios._disableNavBarAnimation = true;
page.actionBarHidden = true;
page.frame.ios._disableNavBarAnimation = false;
}
else {
page.actionBarHidden = true;
}
this._actionBarHiddenByTabView = true;
if (Trace.isEnabled()) {
Trace.write(`TabView hid action bar`, Trace.categories.Debug);
}
return;
}
if (!backToMoreWillBeVisible && this._actionBarHiddenByTabView) {
if (page.frame.ios) {
page.frame.ios._disableNavBarAnimation = true;
page.actionBarHidden = false;
page.frame.ios._disableNavBarAnimation = false;
}
else {
page.actionBarHidden = false;
}
this._actionBarHiddenByTabView = undefined;
if (Trace.isEnabled()) {
Trace.write(`TabView restored action bar`, Trace.categories.Debug);
}
return;
}
}
getViewController(item) {
let newController = item.view ? item.view.viewController : null;
if (newController) {
item.setViewController(newController, newController.view);
return newController;
}
if (item.view.ios instanceof UIViewController) {
newController = item.view.ios;
item.setViewController(newController, newController.view);
}
else if (item.view.ios && item.view.ios.controller instanceof UIViewController) {
newController = item.view.ios.controller;
item.setViewController(newController, newController.view);
}
else {
newController = IOSHelper.UILayoutViewController.initWithOwner(new WeakRef(item.view));
newController.view.addSubview(item.view.nativeViewProtected);
item.view.viewController = newController;
item.setViewController(newController, item.view.nativeViewProtected);
}
return newController;
}
setViewControllers(items) {
const length = items ? items.length : 0;
if (length === 0) {
this._ios.viewControllers = null;
return;
}
const controllers = NSMutableArray.alloc().initWithCapacity(length);
const states = getTitleAttributesForStates(this);
items.forEach((item, i) => {
const controller = this.getViewController(item);
const icon = this._getIcon(item);
const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(item.title || '', icon, i);
updateTitleAndIconPositions(item, tabBarItem, controller);
if (!__VISIONOS__ && SDK_VERSION < 15) {
applyStatesToItem(tabBarItem, states);
}
controller.tabBarItem = tabBarItem;
controllers.addObject(controller);
item.canBeLoaded = true;
});
if (SDK_VERSION >= 15) {
this.updateBarItemAppearance(this._ios.tabBar, states);
}
this._ios.viewControllers = controllers;
this._ios.customizableViewControllers = null;
// When we set this._ios.viewControllers, someone is clearing the moreNavigationController.delegate, so we have to reassign it each time here.
this._ios.moreNavigationController.delegate = this._moreNavigationControllerDelegate;
}
_getIconRenderingMode() {
switch (this.iosIconRenderingMode) {
case 'alwaysOriginal':
return 1 /* UIImageRenderingMode.AlwaysOriginal */;
case 'alwaysTemplate':
return 2 /* UIImageRenderingMode.AlwaysTemplate */;
case 'automatic':
default:
return 0 /* UIImageRenderingMode.Automatic */;
}
}
_getIcon(item) {
if (!item || !item.iconSource) {
return null;
}
let image = this._iconsCache[item.iconSource];
if (!image) {
let is;
if (isSystemURI(item.iconSource)) {
is = ImageSource.fromSystemImageSync(item.iconSource.slice(SYSTEM_PREFIX.length));
}
else if (isFontIconURI(item.iconSource)) {
// Allow specifying a separate font family for the icon via style.iconFontFamily.
// If provided, construct a Font from the family and (optionally) size from fontInternal.
let iconFont = item.style.fontInternal;
const iconFontFamily = item.iconFontFamily || item.style.iconFontFamily;
if (iconFontFamily) {
// Preserve size/style from existing fontInternal if present.
const baseFont = item.style.fontInternal || Font.default;
iconFont = baseFont.withFontFamily(iconFontFamily);
}
is = ImageSource.fromFontIconCodeSync(item.iconSource.slice(FONT_PREFIX.length), iconFont, item.style.color);
}
else {
is = ImageSource.fromFileOrResourceSync(item.iconSource);
}
if (is && is.ios) {
const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode());
this._iconsCache[item.iconSource] = originalRenderedImage;
image = originalRenderedImage;
}
else {
traceMissingIcon(item.iconSource);
}
}
return image;
}
_updateIOSTabBarColorsAndFonts() {
if (!this.items) {
return;
}
const tabBar = this.ios.tabBar;
const states = getTitleAttributesForStates(this);
if (SDK_VERSION >= 15) {
this.updateBarItemAppearance(tabBar, states);
}
else {
for (let i = 0; i < tabBar.items.count; i++) {
applyStatesToItem(tabBar.items[i], states);
}
}
}
updateBarItemAppearance(tabBar, states) {
const appearance = this._getAppearance(tabBar);
const itemAppearances = ['stackedLayoutAppearance', 'inlineLayoutAppearance', 'compactInlineLayoutAppearance'];
for (const itemAppearance of itemAppearances) {
appearance[itemAppearance].normal.titleTextAttributes = states.normalState;
appearance[itemAppearance].selected.titleTextAttributes = states.selectedState;
}
this._updateAppearance(tabBar, appearance);
}
_getAppearance(tabBar) {
if (tabBar.standardAppearance == null) {
const appearance = UITabBarAppearance.new();
appearance.stackedLayoutAppearance = appearance.inlineLayoutAppearance = appearance.compactInlineLayoutAppearance = UITabBarItemAppearance.new();
return appearance;
}
return tabBar.standardAppearance;
}
_updateAppearance(tabBar, appearance) {
tabBar.standardAppearance = appearance;
if (SDK_VERSION >= 15) {
tabBar.scrollEdgeAppearance = appearance;
}
}
[selectedIndexProperty.setNative](value) {
if (Trace.isEnabled()) {
Trace.write('TabView._onSelectedIndexPropertyChangedSetNativeValue(' + value + ')', Trace.categories.Debug);
}
if (value > -1) {
this._ios.selectedIndex = value;
}
}
[itemsProperty.getDefault]() {
return null;
}
[itemsProperty.setNative](value) {
this.setViewControllers(value);
selectedIndexProperty.coerce(this);
}
[tabTextFontSizeProperty.getDefault]() {
return null;
}
[tabTextFontSizeProperty.setNative](value) {
this._updateIOSTabBarColorsAndFonts();
}
[tabTextColorProperty.getDefault]() {
return null;
}
[tabTextColorProperty.setNative](value) {
this._updateIOSTabBarColorsAndFonts();
}
[tabBackgroundColorProperty.getDefault]() {
return this._ios.tabBar.barTintColor;
}
[tabBackgroundColorProperty.setNative](value) {
if (SDK_VERSION >= 13) {
const appearance = this._getAppearance(this._ios.tabBar);
appearance.configureWithDefaultBackground();
appearance.backgroundColor = value instanceof Color ? value.ios : value;
this._updateAppearance(this._ios.tabBar, appearance);
}
else {
this._ios.tabBar.barTintColor = value instanceof Color ? value.ios : value;
}
}
[selectedTabTextColorProperty.getDefault]() {
return this._ios.tabBar.tintColor;
}
[selectedTabTextColorProperty.setNative](value) {
this._ios.tabBar.tintColor = value instanceof Color ? value.ios : value;
this._updateIOSTabBarColorsAndFonts();
}
// TODO: Move this to TabViewItem
[fontInternalProperty.getDefault]() {
return null;
}
[fontInternalProperty.setNative](value) {
this._updateIOSTabBarColorsAndFonts();
}
// TODO: Move this to TabViewItem
[iosIconRenderingModeProperty.getDefault]() {
return 'automatic';
}
[iosIconRenderingModeProperty.setNative](value) {
this._iconsCache = {};
const items = this.items;
if (items && items.length) {
for (let i = 0, length = items.length; i < length; i++) {
const item = items[i];
if (item.iconSource) {
item._update();
}
}
}
}
// iOS 26+: bottom accessory support
[iosBottomAccessoryProperty.getDefault]() {
return null;
}
[iosBottomAccessoryProperty.setNative](value) {
this._applyBottomAccessory(value, false);
}
// iOS 26+: tab bar minimize behavior
[iosTabBarMinimizeBehaviorProperty.getDefault]() {
return 'automatic';
}
[iosTabBarMinimizeBehaviorProperty.setNative](value) {
if (SDK_VERSION < 26) {
return;
}
let mapped;
switch (value) {
case 'never':
mapped = 1 /* UITabBarMinimizeBehavior.Never */;
break;
case 'onScrollDown':
mapped = 2 /* UITabBarMinimizeBehavior.OnScrollDown */;
break;
case 'onScrollUp':
mapped = 3 /* UITabBarMinimizeBehavior.OnScrollUp */;
break;
case 'automatic':
default:
mapped = 0 /* UITabBarMinimizeBehavior.Automatic */;
}
this._ios.tabBarMinimizeBehavior = mapped;
}
_applyBottomAccessory(value, animated) {
// Guard for platform availability
if (SDK_VERSION < 26) {
return;
}
const setAccessory = (accessory) => {
try {
this._ios.setBottomAccessoryAnimated(accessory, animated);
}
catch (err) {
// Fallback to property if needed
this._ios.bottomAccessory = accessory;
}
};
// Clear previous
if (!value) {
// Clear on controller
setAccessory(null);
// Tear down previously managed NS view
if (this._bottomAccessoryNsView) {
// Do not remove from a parent; we didn't add it to the NS view tree.
try {
this._bottomAccessoryNsView._tearDownUI(true);
}
catch (_) { }
this._bottomAccessoryNsView = null;
}
return;
}
// Ensure the NativeScript view has a native view
const nsView = value;
if (!nsView.nativeViewProtected) {
// mirror dialogs approach to setup UI for a detached view
nsView._setupUI({});
}
// Just mark it loaded, if not already, so measurement & styling are applied.
if (!nsView.isLoaded) {
// In detached scenarios we simply callLoaded after setup.
nsView.callLoaded();
}
const contentView = nsView.nativeViewProtected;
if (!contentView) {
return;
}
// Use frame-based sizing; keep autoresizing mask-based behavior enabled (no Auto Layout constraints added here).
contentView.translatesAutoresizingMaskIntoConstraints = true;
// Measure desired height with the tab bar width
let tabBarWidth = this._ios?.tabBar?.frame?.size?.width || Screen.mainScreen.screen.bounds.size.width;
// Account for safe area insets so accessory doesn't extend visually past rounded corners
if (this._ios?.tabBar?.safeAreaInsets) {
const insets = this._ios.tabBar.safeAreaInsets;
// Reduce usable width by left+right safe area (typically 0, but defensive)
const horizontalInsets = insets.left + insets.right;
if (horizontalInsets > 0 && horizontalInsets < tabBarWidth) {
tabBarWidth -= horizontalInsets;
}
}
const tabBarWidthPx = layout.toDevicePixels(tabBarWidth);
// Prefer flooring to avoid overshooting container by +1px due to FP rounding
const tabBarWidthPxRounded = Math.floor(tabBarWidthPx);
let measuredHeight = 0;
// Measure using device-pixel width; flooring prevents +1px expansion
const widthSpec = layout.makeMeasureSpec(tabBarWidthPxRounded, layout.EXACTLY);
const heightSpec = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
nsView.measure(widthSpec, heightSpec);
measuredHeight = layout.toDeviceIndependentPixels(nsView.getMeasuredHeight());
// Use a sensible minimum height (44pt button row) if measurement is tiny
const minHeight = 44;
const finalHeight = Math.max(minHeight, measuredHeight || 0);
// Rely on container height constraint (below) and frame-based layout inside container.
const container = NSTabAccessoryContainer.initWithOwner(new WeakRef(nsView));
container.translatesAutoresizingMaskIntoConstraints = true;
container.autoresizingMask = 2 /* UIViewAutoresizing.FlexibleWidth */ | 16 /* UIViewAutoresizing.FlexibleHeight */;
// Mask any subpixel spill just in case
container.clipsToBounds = true;
container.addSubview(contentView);
// Constrain the container height (not the content) so UIKit has a concrete size.
const containerHeight = container.heightAnchor.constraintEqualToConstant(finalHeight);
containerHeight.priority = 999;
NSLayoutConstraint.activateConstraints([containerHeight]);
const accessory = UITabAccessory.alloc().initWithContentView(container);
setAccessory(accessory);
// Keep references for later teardown
this._bottomAccessoryNsView = nsView;
}
}
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TabView.prototype, "onLoaded", null);
var NSTabAccessoryContainer = (function (_super) {
__extends(NSTabAccessoryContainer, _super);
function NSTabAccessoryContainer() {
return _super !== null && _super.apply(this, arguments) || this;
}
NSTabAccessoryContainer.initWithOwner = function (owner) {
var v = NSTabAccessoryContainer.new();
v._owner = owner;
return v;
};
NSTabAccessoryContainer.prototype.layoutSubviews = function () {
var _a;
_super.prototype.layoutSubviews.call(this);
var owner = (_a = this._owner) === null || _a === void 0 ? void 0 : _a.deref();
if (!(owner === null || owner === void 0 ? void 0 : owner.nativeViewProtected))
return;
owner.nativeViewProtected.frame = this.bounds;
var w = this.bounds.size.width;
var h = this.bounds.size.height;
try {
var wp = Math.floor(layout.toDevicePixels(w));
var hp = Math.floor(layout.toDevicePixels(h));
var containerPxWidth = Math.floor(layout.toDevicePixels(this.bounds.size.width));
if (wp > containerPxWidth) {
wp = containerPxWidth;
}
var widthSpec = layout.makeMeasureSpec(wp, layout.EXACTLY);
var heightSpec = layout.makeMeasureSpec(hp, layout.EXACTLY);
owner.measure(widthSpec, heightSpec);
owner.layout(0, 0, wp, hp);
}
catch (_) { }
};
return NSTabAccessoryContainer;
}(UIView));
function getTitleAttributesForStates(tabView) {
const result = {
normalState: NSMutableDictionary.new(),
selectedState: NSMutableDictionary.new(),
};
const titleFontSize = tabView.style.tabTextFontSize;
let font = tabView.style.fontInternal || Font.default;
if (titleFontSize != null) {
font = font.withFontSize(titleFontSize);
}
const nativeFont = font.getUIFont(UIFont.systemFontOfSize(UIFont.labelFontSize));
result.normalState.setValueForKey(nativeFont, NSFontAttributeName);
result.selectedState.setValueForKey(nativeFont, NSFontAttributeName);
const titleColor = tabView.style.tabTextColor;
if (titleColor instanceof Color) {
result.normalState.setValueForKey(titleColor.ios, UITextAttributeTextColor);
}
const selectedTitleColor = tabView.style.selectedTabTextColor;
if (selectedTitleColor instanceof Color) {
result.selectedState.setValueForKey(selectedTitleColor.ios, UITextAttributeTextColor);
}
return result;
}
function applyStatesToItem(item, states) {
item.setTitleTextAttributesForState(states.normalState, 0 /* UIControlState.Normal */);
item.setTitleTextAttributesForState(states.selectedState, 4 /* UIControlState.Selected */);
}
//# sourceMappingURL=index.ios.js.map