fix(ios): proper UITabBarAppearance handling

This commit is contained in:
Nathan Walker
2022-02-17 20:25:41 -08:00
parent b05650c416
commit 6c71ce21a2

View File

@ -14,180 +14,168 @@ import { profile } from '../../profiling';
import { Frame } from '../frame'; import { Frame } from '../frame';
import { layout, iOSNativeHelper } from '../../utils'; import { layout, iOSNativeHelper } from '../../utils';
import { Device } from '../../platform'; import { Device } from '../../platform';
import { Transparent } from 'color/known-colors';
export * from './tab-view-common'; export * from './tab-view-common';
const majorVersion = iOSNativeHelper.MajorVersion; const majorVersion = iOSNativeHelper.MajorVersion;
const isPhone = Device.deviceType === 'Phone'; const isPhone = Device.deviceType === 'Phone';
let UITabBarControllerImpl; @NativeClass
let UITabBarControllerDelegateImpl; class UITabBarControllerImpl extends UITabBarController {
let UINavigationControllerDelegateImpl; private _owner: WeakRef<TabView>;
const setupControllers = function () { public static initWithOwner(owner: WeakRef<TabView>): UITabBarControllerImpl {
if (typeof UITabBarControllerImpl === 'undefined') { const handler = <UITabBarControllerImpl>UITabBarControllerImpl.new();
@NativeClass handler._owner = owner;
class UITabBarControllerClass extends UITabBarController {
private _owner: WeakRef<TabView>;
public static initWithOwner(owner: WeakRef<TabView>): typeof UITabBarControllerImpl { return handler;
const handler = <typeof UITabBarControllerImpl>UITabBarControllerImpl.new(); }
handler._owner = owner;
return handler; public viewDidLoad(): void {
super.viewDidLoad();
// Unify translucent and opaque bars layout
// this.edgesForExtendedLayout = UIRectEdgeBottom;
this.extendedLayoutIncludesOpaqueBars = true;
}
@profile
public viewWillAppear(animated: boolean): void {
super.viewWillAppear(animated);
const owner = this._owner.get();
if (!owner) {
return;
}
IOSHelper.updateAutoAdjustScrollInsets(this, owner);
if (!owner.parent) {
owner.callLoaded();
}
}
@profile
public viewDidDisappear(animated: boolean): void {
super.viewDidDisappear(animated);
const owner = this._owner.get();
if (owner && !owner.parent && owner.isLoaded && !this.presentedViewController) {
owner.callUnloaded();
}
}
public viewWillTransitionToSizeWithTransitionCoordinator(size: CGSize, coordinator: UIViewControllerTransitionCoordinator): void {
super.viewWillTransitionToSizeWithTransitionCoordinator(size, coordinator);
coordinator.animateAlongsideTransitionCompletion(null, () => {
const owner = this._owner.get();
if (owner && owner.items) {
owner.items.forEach((tabItem) => tabItem._updateTitleAndIconPositions());
} }
});
}
public viewDidLoad(): void { // Mind implementation for other controllers
super.viewDidLoad(); public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void {
super.traitCollectionDidChange(previousTraitCollection);
// Unify translucent and opaque bars layout if (majorVersion >= 13) {
// this.edgesForExtendedLayout = UIRectEdgeBottom; const owner = this._owner.get();
this.extendedLayoutIncludesOpaqueBars = true; if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
} owner.notify({
eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent,
@profile object: owner,
public viewWillAppear(animated: boolean): void {
super.viewWillAppear(animated);
const owner = this._owner.get();
if (!owner) {
return;
}
IOSHelper.updateAutoAdjustScrollInsets(this, owner);
if (!owner.parent) {
owner.callLoaded();
}
}
@profile
public viewDidDisappear(animated: boolean): void {
super.viewDidDisappear(animated);
const owner = this._owner.get();
if (owner && !owner.parent && owner.isLoaded && !this.presentedViewController) {
owner.callUnloaded();
}
}
public viewWillTransitionToSizeWithTransitionCoordinator(size: CGSize, coordinator: UIViewControllerTransitionCoordinator): void {
super.viewWillTransitionToSizeWithTransitionCoordinator(size, coordinator);
coordinator.animateAlongsideTransitionCompletion(null, () => {
const owner = this._owner.get();
if (owner && owner.items) {
owner.items.forEach((tabItem) => tabItem._updateTitleAndIconPositions());
}
}); });
} }
// Mind implementation for other controllers
public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void {
super.traitCollectionDidChange(previousTraitCollection);
if (majorVersion >= 13) {
const owner = this._owner.get();
if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
owner.notify({
eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent,
object: owner,
});
}
}
}
} }
UITabBarControllerImpl = UITabBarControllerClass;
@NativeClass
class UITabBarControllerDelegateClass extends NSObject implements UITabBarControllerDelegate {
public static ObjCProtocols = [UITabBarControllerDelegate];
private _owner: WeakRef<TabView>;
public static initWithOwner(owner: WeakRef<TabView>): typeof UITabBarControllerDelegateImpl {
const delegate = <typeof UITabBarControllerDelegateImpl>UITabBarControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
}
public tabBarControllerShouldSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): boolean {
if (Trace.isEnabled()) {
Trace.write('TabView.delegate.SHOULD_select(' + tabBarController + ', ' + viewController + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
// "< More" cannot be visible after clicking on the main tab bar buttons.
const backToMoreWillBeVisible = false;
owner._handleTwoNavigationBars(backToMoreWillBeVisible);
}
if ((<any>tabBarController).selectedViewController === viewController) {
return false;
}
(<any>tabBarController)._willSelectViewController = viewController;
return true;
}
public tabBarControllerDidSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): void {
if (Trace.isEnabled()) {
Trace.write('TabView.delegate.DID_select(' + tabBarController + ', ' + viewController + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
owner._onViewControllerShown(viewController);
}
(<any>tabBarController)._willSelectViewController = undefined;
}
}
UITabBarControllerDelegateImpl = UITabBarControllerDelegateClass;
@NativeClass
class UINavigationControllerDelegateClass extends NSObject implements UINavigationControllerDelegate {
public static ObjCProtocols = [UINavigationControllerDelegate];
private _owner: WeakRef<TabView>;
public static initWithOwner(owner: WeakRef<TabView>): typeof UINavigationControllerDelegateImpl {
const delegate = <typeof UINavigationControllerDelegateImpl>UINavigationControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
}
navigationControllerWillShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
if (Trace.isEnabled()) {
Trace.write('TabView.moreNavigationController.WILL_show(' + navigationController + ', ' + viewController + ', ' + animated + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
// If viewController is one of our tab item controllers, then "< More" will be visible shortly.
// Otherwise viewController is the UIMoreListController which shows the list of all tabs beyond the 4th tab.
const backToMoreWillBeVisible = owner._ios.viewControllers.containsObject(viewController);
owner._handleTwoNavigationBars(backToMoreWillBeVisible);
}
}
navigationControllerDidShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
if (Trace.isEnabled()) {
Trace.write('TabView.moreNavigationController.DID_show(' + navigationController + ', ' + viewController + ', ' + animated + ');', Trace.categories.Debug);
}
// We don't need Edit button in More screen.
navigationController.navigationBar.topItem.rightBarButtonItem = null;
const owner = this._owner.get();
if (owner) {
owner._onViewControllerShown(viewController);
}
}
}
UINavigationControllerDelegateImpl = UINavigationControllerDelegateClass;
} }
}; }
setupControllers(); @NativeClass
class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControllerDelegate {
public static ObjCProtocols = [UITabBarControllerDelegate];
private _owner: WeakRef<TabView>;
public static initWithOwner(owner: WeakRef<TabView>): UITabBarControllerDelegateImpl {
const delegate = <UITabBarControllerDelegateImpl>UITabBarControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
}
public tabBarControllerShouldSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): boolean {
if (Trace.isEnabled()) {
Trace.write('TabView.delegate.SHOULD_select(' + tabBarController + ', ' + viewController + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
// "< More" cannot be visible after clicking on the main tab bar buttons.
const backToMoreWillBeVisible = false;
owner._handleTwoNavigationBars(backToMoreWillBeVisible);
}
if ((<any>tabBarController).selectedViewController === viewController) {
return false;
}
(<any>tabBarController)._willSelectViewController = viewController;
return true;
}
public tabBarControllerDidSelectViewController(tabBarController: UITabBarController, viewController: UIViewController): void {
if (Trace.isEnabled()) {
Trace.write('TabView.delegate.DID_select(' + tabBarController + ', ' + viewController + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
owner._onViewControllerShown(viewController);
}
(<any>tabBarController)._willSelectViewController = undefined;
}
}
@NativeClass
class UINavigationControllerDelegateImpl extends NSObject implements UINavigationControllerDelegate {
public static ObjCProtocols = [UINavigationControllerDelegate];
private _owner: WeakRef<TabView>;
public static initWithOwner(owner: WeakRef<TabView>): UINavigationControllerDelegateImpl {
const delegate = <UINavigationControllerDelegateImpl>UINavigationControllerDelegateImpl.new();
delegate._owner = owner;
return delegate;
}
navigationControllerWillShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
if (Trace.isEnabled()) {
Trace.write('TabView.moreNavigationController.WILL_show(' + navigationController + ', ' + viewController + ', ' + animated + ');', Trace.categories.Debug);
}
const owner = this._owner.get();
if (owner) {
// If viewController is one of our tab item controllers, then "< More" will be visible shortly.
// Otherwise viewController is the UIMoreListController which shows the list of all tabs beyond the 4th tab.
const backToMoreWillBeVisible = owner._ios.viewControllers.containsObject(viewController);
owner._handleTwoNavigationBars(backToMoreWillBeVisible);
}
}
navigationControllerDidShowViewControllerAnimated(navigationController: UINavigationController, viewController: UIViewController, animated: boolean): void {
if (Trace.isEnabled()) {
Trace.write('TabView.moreNavigationController.DID_show(' + navigationController + ', ' + viewController + ', ' + animated + ');', Trace.categories.Debug);
}
// We don't need Edit button in More screen.
navigationController.navigationBar.topItem.rightBarButtonItem = null;
const owner = this._owner.get();
if (owner) {
owner._onViewControllerShown(viewController);
}
}
}
function updateTitleAndIconPositions(tabItem: TabViewItem, tabBarItem: UITabBarItem, controller: UIViewController) { function updateTitleAndIconPositions(tabItem: TabViewItem, tabBarItem: UITabBarItem, controller: UIViewController) {
if (!tabItem || !tabBarItem) { if (!tabItem || !tabBarItem) {
@ -286,11 +274,11 @@ export class TabViewItem extends TabViewItemBase {
} }
export class TabView extends TabViewBase { export class TabView extends TabViewBase {
public viewController: typeof UITabBarControllerImpl; public viewController: UITabBarControllerImpl;
public items: TabViewItem[]; public items: TabViewItem[];
public _ios: typeof UITabBarControllerImpl; public _ios: UITabBarControllerImpl;
private _delegate: typeof UITabBarControllerDelegateImpl; private _delegate: UITabBarControllerDelegateImpl;
private _moreNavigationControllerDelegate: typeof UINavigationControllerDelegateImpl; private _moreNavigationControllerDelegate: UINavigationControllerDelegateImpl;
private _iconsCache = {}; private _iconsCache = {};
constructor() { constructor() {
@ -534,6 +522,17 @@ export class TabView extends TabViewBase {
} }
} }
private _getAppearance(tabBar: UITabBar) {
return tabBar.standardAppearance ?? UITabBarAppearance.new();
}
private _updateAppearance(tabBar: UITabBar, appearance: UITabBarAppearance) {
tabBar.standardAppearance = appearance;
if (majorVersion >= 15) {
tabBar.scrollEdgeAppearance = appearance;
}
}
[selectedIndexProperty.setNative](value: number) { [selectedIndexProperty.setNative](value: number) {
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('TabView._onSelectedIndexPropertyChangedSetNativeValue(' + value + ')', Trace.categories.Debug); Trace.write('TabView._onSelectedIndexPropertyChangedSetNativeValue(' + value + ')', Trace.categories.Debug);
@ -571,12 +570,11 @@ export class TabView extends TabViewBase {
return this._ios.tabBar.barTintColor; return this._ios.tabBar.barTintColor;
} }
[tabBackgroundColorProperty.setNative](value: UIColor | Color) { [tabBackgroundColorProperty.setNative](value: UIColor | Color) {
if (majorVersion >= 15) { if (majorVersion >= 13) {
let appearance = UITabBarAppearance.new(); const appearance = this._getAppearance(this._ios.tabBar);
appearance.configureWithDefaultBackground();
appearance.backgroundColor = value instanceof Color ? value.ios : value; appearance.backgroundColor = value instanceof Color ? value.ios : value;
this._updateAppearance(this._ios.tabBar, appearance);
this._ios.tabBar.scrollEdgeAppearance = appearance;
this._ios.tabBar.standardAppearance = appearance;
} else { } else {
this._ios.tabBar.barTintColor = value instanceof Color ? value.ios : value; this._ios.tabBar.barTintColor = value instanceof Color ? value.ios : value;
} }