diff --git a/tns-core-modules/application/application-common.ts b/tns-core-modules/application/application-common.ts index c59774ea2..cdce188f5 100644 --- a/tns-core-modules/application/application-common.ts +++ b/tns-core-modules/application/application-common.ts @@ -42,7 +42,7 @@ import { } from "./application"; import { CLASS_PREFIX, pushToRootViewCssClasses, removeFromRootViewCssClasses } from "../css/system-classes"; -import { DeviceOrientation } from "../ui/enums/enums"; +import { DeviceOrientation, SystemAppearance } from "../ui/enums/enums"; export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData }; @@ -55,6 +55,7 @@ export const lowMemoryEvent = "lowMemory"; export const uncaughtErrorEvent = "uncaughtError"; export const discardedErrorEvent = "discardedError"; export const orientationChangedEvent = "orientationChanged"; +export const systemAppearanceChangedEvent = "systemAppearanceChanged"; const ORIENTATION_CSS_CLASSES = [ `${CLASS_PREFIX}${DeviceOrientation.portrait}`, @@ -62,6 +63,11 @@ const ORIENTATION_CSS_CLASSES = [ `${CLASS_PREFIX}${DeviceOrientation.unknown}` ]; +const SYSTEM_APPEARANCE_CSS_CLASSES = [ + `${CLASS_PREFIX}${SystemAppearance.light}`, + `${CLASS_PREFIX}${SystemAppearance.dark}` +]; + let cssFile: string = "./app.css"; let resources: any = {}; @@ -126,17 +132,30 @@ export function loadAppCss(): void { } } +function applyCssClass(rootView: View, cssClass: string) { + pushToRootViewCssClasses(cssClass); + rootView.cssClasses.add(cssClass); +} + +function removeCssClass(rootView: View, cssClass: string) { + removeFromRootViewCssClasses(cssClass); + rootView.cssClasses.delete(cssClass); +} + export function orientationChanged(rootView: View, newOrientation: "portrait" | "landscape" | "unknown"): void { const newOrientationCssClass = `${CLASS_PREFIX}${newOrientation}`; if (!rootView.cssClasses.has(newOrientationCssClass)) { - const removeCssClass = (c: string) => { - removeFromRootViewCssClasses(c); - rootView.cssClasses.delete(c); - }; + ORIENTATION_CSS_CLASSES.forEach(cssClass => removeCssClass(rootView, cssClass)); + applyCssClass(rootView, newOrientationCssClass); + rootView._onCssStateChange(); + } +} - ORIENTATION_CSS_CLASSES.forEach(c => removeCssClass(c)); - pushToRootViewCssClasses(newOrientationCssClass); - rootView.cssClasses.add(newOrientationCssClass); +export function systemAppearanceChanged(rootView: View, newSystemAppearance: "dark" | "light"): void { + const newSystemAppearanceCssClass = `${CLASS_PREFIX}${newSystemAppearance}`; + if (!rootView.cssClasses.has(newSystemAppearanceCssClass)) { + SYSTEM_APPEARANCE_CSS_CLASSES.forEach(cssClass => removeCssClass(rootView, cssClass)); + applyCssClass(rootView, newSystemAppearanceCssClass); rootView._onCssStateChange(); } } diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index 25e04594b..8f1b29dd0 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -9,12 +9,14 @@ import { AndroidApplication as AndroidApplicationDefinition, ApplicationEventData, CssChangedEventData, - OrientationChangedEventData + OrientationChangedEventData, + SystemAppearanceChangedEventData } from "."; import { displayedEvent, hasListeners, livesync, lowMemoryEvent, notify, Observable, on, - orientationChanged, orientationChangedEvent, setApplication, suspendEvent + orientationChanged, orientationChangedEvent, setApplication, suspendEvent, + systemAppearanceChanged, systemAppearanceChangedEvent } from "./application-common"; import { profile } from "../profiling"; @@ -51,6 +53,7 @@ export class AndroidApplication extends Observable implements AndroidApplication public static activityRequestPermissionsEvent = ActivityRequestPermissions; private _orientation: "portrait" | "landscape" | "unknown"; + private _systemAppearance: "light" | "dark"; public paused: boolean; public nativeApp: android.app.Application; public context: android.content.Context; @@ -93,9 +96,8 @@ export class AndroidApplication extends Observable implements AndroidApplication if (!this._orientation) { const resources = this.context.getResources(); const configuration = resources.getConfiguration(); - const orientation = configuration.orientation; - this._orientation = getOrientationValue(orientation); + this._orientation = getOrientationValue(configuration); } return this._orientation; @@ -105,6 +107,21 @@ export class AndroidApplication extends Observable implements AndroidApplication this._orientation = value; } + get systemAppearance(): "light" | "dark" { + if (!this._systemAppearance) { + const resources = this.context.getResources(); + const configuration = resources.getConfiguration(); + + this._systemAppearance = getSystemAppearanceValue(configuration); + } + + return this._systemAppearance; + } + + set systemAppearance(value: "light" | "dark") { + this._systemAppearance = value; + } + public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) { ensureBroadCastReceiverClass(); const that = this; @@ -131,6 +148,7 @@ export class AndroidApplication extends Observable implements AndroidApplication } } } + export interface AndroidApplication { on(eventNames: string, callback: (data: AndroidActivityEventData) => void, thisArg?: any); on(event: "activityCreated", callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any); @@ -252,12 +270,9 @@ export function orientation(): "portrait" | "landscape" | "unknown" { return androidApp.orientation; } -on(orientationChangedEvent, (args: OrientationChangedEventData) => { - const rootView = getRootView(); - if (rootView) { - orientationChanged(rootView, args.newValue); - } -}); +export function systemAppearance(): "dark" | "light" { + return androidApp.systemAppearance; +} global.__onLiveSync = function __onLiveSync(context?: ModuleContext) { if (androidApp && androidApp.paused) { @@ -268,7 +283,9 @@ global.__onLiveSync = function __onLiveSync(context?: ModuleContext) { livesync(rootView, context); }; -function getOrientationValue(orientation: number): "portrait" | "landscape" | "unknown" { +function getOrientationValue(configuration: android.content.res.Configuration): "portrait" | "landscape" | "unknown" { + const orientation = configuration.orientation; + switch (orientation) { case android.content.res.Configuration.ORIENTATION_LANDSCAPE: return "landscape"; @@ -279,6 +296,19 @@ function getOrientationValue(orientation: number): "portrait" | "landscape" | "u } } +// https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#configuration_changes +function getSystemAppearanceValue(configuration: android.content.res.Configuration): "dark" | "light" { + const systemAppearance = configuration.uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK; + + switch (systemAppearance) { + case android.content.res.Configuration.UI_MODE_NIGHT_YES: + return "dark"; + case android.content.res.Configuration.UI_MODE_NIGHT_NO: + case android.content.res.Configuration.UI_MODE_NIGHT_UNDEFINED: + return "light"; + } +} + function initLifecycleCallbacks() { const setThemeOnLaunch = profile("setThemeOnLaunch", (activity: androidx.appcompat.app.AppCompatActivity) => { // Set app theme after launch screen was used during startup @@ -380,12 +410,12 @@ function initComponentCallbacks() { // TODO: This is skipped for now, test carefully for OutOfMemory exceptions }), - onConfigurationChanged: profile("onConfigurationChanged", function (newConfig: android.content.res.Configuration) { - const newConfigOrientation = newConfig.orientation; - const newOrientation = getOrientationValue(newConfigOrientation); + onConfigurationChanged: profile("onConfigurationChanged", function (newConfiguration: android.content.res.Configuration) { + const newOrientation = getOrientationValue(newConfiguration); if (androidApp.orientation !== newOrientation) { androidApp.orientation = newOrientation; + orientationChanged(getRootView(), newOrientation); notify({ eventName: orientationChangedEvent, @@ -393,6 +423,22 @@ function initComponentCallbacks() { newValue: androidApp.orientation, object: androidApp }); + + return; + } + + const newSystemAppearance = getSystemAppearanceValue(newConfiguration); + + if (androidApp.systemAppearance !== newSystemAppearance) { + androidApp.systemAppearance = newSystemAppearance; + systemAppearanceChanged(getRootView(), newSystemAppearance); + + notify({ + eventName: systemAppearanceChangedEvent, + android: androidApp.nativeApp, + newValue: androidApp.systemAppearance, + object: androidApp + }); } }) }); diff --git a/tns-core-modules/application/application.d.ts b/tns-core-modules/application/application.d.ts index 48217c411..8573af625 100644 --- a/tns-core-modules/application/application.d.ts +++ b/tns-core-modules/application/application.d.ts @@ -101,6 +101,16 @@ export interface OrientationChangedEventData extends ApplicationEventData { newValue: "portrait" | "landscape" | "unknown"; } +/** + * Event data containing information for system appearance changed event. + */ +export interface SystemAppearanceChangedEventData extends ApplicationEventData { + /** + * New system appearance value. + */ + newValue: "light" | "dark"; +} + /** * Event data containing information about unhandled application errors. */ @@ -281,16 +291,29 @@ export function on(event: "uncaughtError", callback: (args: UnhandledErrorEventD export function on(event: "discardedError", callback: (args: DiscardedErrorEventData) => void, thisArg?: any); /** - * This event is raised the orientation of the current device has changed. + * This event is raised when the orientation of the application changes. */ export function on(event: "orientationChanged", callback: (args: OrientationChangedEventData) => void, thisArg?: any); +/** + * This event is raised when the operating system appearance changes + * between light and dark theme (for Android); + * between light and dark mode (for iOS) and vice versa. + */ +export function on(event: "systemAppearanceChanged", callback: (args: SystemAppearanceChangedEventData) => void, thisArg?: any); + /** * Gets the orientation of the application. * Available values: "portrait", "landscape", "unknown". */ export function orientation(): "portrait" | "landscape" | "unknown"; +/** + * Gets the operating system appearance. + * Available values: "dark", "light". + */ +export function systemAppearance(): "dark" | "light"; + /** * This is the Android-specific application object instance. * Encapsulates methods and properties specific to the Android platform. @@ -425,6 +448,12 @@ export class AndroidApplication extends Observable { */ orientation: "portrait" | "landscape" | "unknown"; + /** + * Gets the system appearance. + * Available values: "dark", "light". + */ + systemAppearance: "dark" | "light"; + /** * The name of the application package. */ @@ -604,6 +633,12 @@ export interface iOSApplication { */ orientation: "portrait" | "landscape" | "unknown"; + /** + * Gets the system appearance. + * Available values: "dark", "light". + */ + systemAppearance: "dark" | "light"; + /** * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html). */ @@ -627,7 +662,9 @@ export interface iOSApplication { removeNotificationObserver(observer: any, notificationName: string): void; } -/* tslint:disable */ +/** +* @deprecated +*/ export interface RootViewControllerImpl { contentController: any; } diff --git a/tns-core-modules/application/application.ios.ts b/tns-core-modules/application/application.ios.ts index 33220468a..722af4176 100644 --- a/tns-core-modules/application/application.ios.ts +++ b/tns-core-modules/application/application.ios.ts @@ -4,12 +4,14 @@ import { iOSApplication as IOSApplicationDefinition, LaunchEventData, LoadAppCSSEventData, - OrientationChangedEventData + OrientationChangedEventData, + SystemAppearanceChangedEventData } from "."; import { displayedEvent, exitEvent, getCssFileName, launchEvent, livesync, lowMemoryEvent, notify, on, - orientationChanged, orientationChangedEvent, resumeEvent, setApplication, suspendEvent + orientationChanged, orientationChangedEvent, resumeEvent, setApplication, suspendEvent, + systemAppearanceChanged, systemAppearanceChangedEvent } from "./application-common"; // First reexport so that app module is initialized. @@ -17,7 +19,12 @@ export * from "./application-common"; // TODO: Remove this and get it from global to decouple builder for angular import { createViewFromEntry } from "../ui/builder"; -import { CLASS_PREFIX, getRootViewCssClasses, pushToRootViewCssClasses } from "../css/system-classes"; +import { + CLASS_PREFIX, + getRootViewCssClasses, + pushToRootViewCssClasses, + resetRootViewCssClasses +} from "../css/system-classes"; import { ios as iosView, View } from "../ui/core/view"; import { Frame, NavigationEntry } from "../ui/frame"; import { device } from "../platform/platform"; @@ -88,6 +95,7 @@ class IOSApplication implements IOSApplicationDefinition { private _observers: Array; private _orientation: "portrait" | "landscape" | "unknown"; private _rootView: View; + private _systemAppearance: "light" | "dark"; constructor() { this._observers = new Array(); @@ -112,6 +120,15 @@ class IOSApplication implements IOSApplicationDefinition { return this._window.rootViewController; } + get systemAppearance(): "light" | "dark" { + if (!this._systemAppearance) { + const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle; + this._systemAppearance = getSystemAppearanceValue(userInterfaceStyle); + } + + return this._systemAppearance; + } + get nativeApp(): UIApplication { return UIApplication.sharedApplication; } @@ -217,6 +234,7 @@ class IOSApplication implements IOSApplicationDefinition { if (this._orientation !== newOrientation) { this._orientation = newOrientation; + orientationChanged(getRootView(), newOrientation); notify({ eventName: orientationChangedEvent, @@ -282,6 +300,24 @@ class IOSApplication implements IOSApplicationDefinition { if (!haveController) { this._window.makeKeyAndVisible(); } + + setupRootViewCssClasses(rootView); + rootView.on(iosView.traitCollectionColorAppearanceChangedEvent, () => { + const userInterfaceStyle = controller.traitCollection.userInterfaceStyle; + const newSystemAppearance = getSystemAppearanceValue(userInterfaceStyle); + + if (this._systemAppearance !== newSystemAppearance) { + this._systemAppearance = newSystemAppearance; + systemAppearanceChanged(rootView, newSystemAppearance); + + notify({ + eventName: systemAppearanceChangedEvent, + ios: this, + newValue: this._systemAppearance, + object: this + }); + } + }); } } @@ -296,6 +332,7 @@ setApplication(iosApp); }; let mainEntry: NavigationEntry; + function createRootView(v?: View) { let rootView = v; if (!rootView) { @@ -312,14 +349,6 @@ function createRootView(v?: View) { } } - const deviceType = device.deviceType.toLowerCase(); - pushToRootViewCssClasses(`${CLASS_PREFIX}${IOS_PLATFORM}`); - pushToRootViewCssClasses(`${CLASS_PREFIX}${deviceType}`); - pushToRootViewCssClasses(`${CLASS_PREFIX}${iosApp.orientation}`); - - const rootViewCssClasses = getRootViewCssClasses(); - rootViewCssClasses.forEach(c => rootView.cssClasses.add(c)); - return rootView; } @@ -359,6 +388,25 @@ export function _start(entry?: string | NavigationEntry) { let visibleVC = getVisibleViewController(rootController); visibleVC.presentViewControllerAnimatedCompletion(controller, true, null); } + + // Mind root view CSS classes in future work + // on embedding NativeScript applications + setupRootViewCssClasses(rootView); + rootView.on(iosView.traitCollectionColorAppearanceChangedEvent, () => { + const userInterfaceStyle = controller.traitCollection.userInterfaceStyle; + const newSystemAppearance = getSystemAppearanceValue(userInterfaceStyle); + + if (this._systemAppearance !== newSystemAppearance) { + this._systemAppearance = newSystemAppearance; + + notify({ + eventName: systemAppearanceChangedEvent, + ios: this, + newValue: this._systemAppearance, + object: this + }); + } + }); iosApp.notifyAppStarted(); } } @@ -389,18 +437,27 @@ export function getNativeApplication(): UIApplication { return iosApp.nativeApp; } -function getViewController(view: View): UIViewController { - let viewController: UIViewController = view.viewController || view.ios; - if (viewController instanceof UIViewController) { - return viewController; - } else { +function getSystemAppearanceValue(userInterfaceStyle: number): "dark" | "light" { + switch (userInterfaceStyle) { + case UIUserInterfaceStyle.Dark: + return "dark"; + case UIUserInterfaceStyle.Light: + case UIUserInterfaceStyle.Unspecified: + return "light"; + } +} + +function getViewController(rootView: View): UIViewController { + let viewController: UIViewController = rootView.viewController || rootView.ios; + + if (!(viewController instanceof UIViewController)) { // We set UILayoutViewController dynamically to the root view if it doesn't have a view controller // At the moment the root view doesn't have its native view created. We set it in the setViewControllerView func - viewController = iosView.UILayoutViewController.initWithOwner(new WeakRef(view)) as UIViewController; - view.viewController = viewController; - - return viewController; + viewController = iosView.UILayoutViewController.initWithOwner(new WeakRef(rootView)) as UIViewController; + rootView.viewController = viewController; } + + return viewController; } function setViewControllerView(view: View): void { @@ -416,16 +473,26 @@ function setViewControllerView(view: View): void { } } +function setupRootViewCssClasses(rootView: View): void { + resetRootViewCssClasses(); + + const deviceType = device.deviceType.toLowerCase(); + pushToRootViewCssClasses(`${CLASS_PREFIX}${IOS_PLATFORM}`); + pushToRootViewCssClasses(`${CLASS_PREFIX}${deviceType}`); + pushToRootViewCssClasses(`${CLASS_PREFIX}${iosApp.orientation}`); + pushToRootViewCssClasses(`${CLASS_PREFIX}${iosApp.systemAppearance}`); + + const rootViewCssClasses = getRootViewCssClasses(); + rootViewCssClasses.forEach(c => rootView.cssClasses.add(c)); +} + export function orientation(): "portrait" | "landscape" | "unknown" { return iosApp.orientation; } -on(orientationChangedEvent, (args: OrientationChangedEventData) => { - const rootView = getRootView(); - if (rootView) { - orientationChanged(rootView, args.newValue); - } -}); +export function systemAppearance(): "dark" | "light" { + return iosApp.systemAppearance; +} global.__onLiveSync = function __onLiveSync(context?: ModuleContext) { if (!started) { diff --git a/tns-core-modules/css/system-classes.d.ts b/tns-core-modules/css/system-classes.d.ts index 98f7ee59e..740854fec 100644 --- a/tns-core-modules/css/system-classes.d.ts +++ b/tns-core-modules/css/system-classes.d.ts @@ -18,7 +18,7 @@ export function getModalRootViewCssClass(): string; export function getRootViewCssClasses(): string[]; /** - * * Appends new CSS class to the system classes and returns the new length of the array. + * Appends new CSS class to the system classes and returns the new length of the array. * @param value New CSS system class. */ export function pushToRootViewCssClasses(value: string): number; @@ -28,3 +28,8 @@ export function pushToRootViewCssClasses(value: string): number; * @param value */ export function removeFromRootViewCssClasses(value: string): string; + +/** + * Resets CSS classes for root view. + */ +export function resetRootViewCssClasses(): string[]; diff --git a/tns-core-modules/css/system-classes.ts b/tns-core-modules/css/system-classes.ts index 6e2f7e80c..de763e55b 100644 --- a/tns-core-modules/css/system-classes.ts +++ b/tns-core-modules/css/system-classes.ts @@ -30,3 +30,8 @@ export function removeFromRootViewCssClasses(value: string): string { return removedElement; } + +export function resetRootViewCssClasses(): string[] { + // Preserve the default `ns-root` CSS class + return rootViewCssClasses.splice(1); +} diff --git a/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts b/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts index 2d2966105..817f6ab9d 100644 --- a/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts +++ b/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts @@ -79,6 +79,18 @@ class UITabBarControllerImpl extends UITabBarController { } }); } + + // 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(previousTraitCollection)) { + owner.notify({ eventName: iosView.traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControllerDelegate { diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index 2eafa9694..be60affc3 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -839,6 +839,11 @@ export const iosOverflowSafeAreaProperty: Property; export const iosOverflowSafeAreaEnabledProperty: InheritedProperty; export namespace ios { + /** + * String value used when hooking to traitCollectionColorAppearanceChangedEvent event. + */ + export const traitCollectionColorAppearanceChangedEvent: string; + /** * Returns a view with viewController or undefined if no such found along the view's parent chain. * @param view The view form which to start the search. diff --git a/tns-core-modules/ui/core/view/view.ios.ts b/tns-core-modules/ui/core/view/view.ios.ts index 6b1065352..83c5e4ce2 100644 --- a/tns-core-modules/ui/core/view/view.ios.ts +++ b/tns-core-modules/ui/core/view/view.ios.ts @@ -702,6 +702,8 @@ export class CustomLayoutView extends ContainerView { } export namespace ios { + export const traitCollectionColorAppearanceChangedEvent = "traitCollectionColorAppearanceChanged"; + export function getParentWithViewController(view: View): View { while (view && !view.viewController) { view = view.parent as View; @@ -1003,6 +1005,18 @@ export namespace ios { owner.callUnloaded(); } } + + // 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(previousTraitCollection)) { + owner.notify({ eventName: traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate { diff --git a/tns-core-modules/ui/enums/enums.d.ts b/tns-core-modules/ui/enums/enums.d.ts index 1dc7d1848..c4a435246 100644 --- a/tns-core-modules/ui/enums/enums.d.ts +++ b/tns-core-modules/ui/enums/enums.d.ts @@ -641,8 +641,10 @@ export module AnimationCurve { } /** -* Specifies the types of the status bar style. -*/ + * @deprecated use `UserInterfaceStyle` instead. + * + * Specifies the types of the status bar style. + */ export module StatusBarStyle { /** * The light style of the status bar - light background with dark letters. @@ -654,3 +656,19 @@ export module StatusBarStyle { */ export const dark: string; } + + +/** + * Specifies the types of the user interface style. + */ +export module UserInterfaceStyle { + /** + * The light style of the user interface. + */ + export const light: string; + + /** + * The dark style of the user interface. + */ + export const dark: string; +} diff --git a/tns-core-modules/ui/enums/enums.ts b/tns-core-modules/ui/enums/enums.ts index 22077aaf6..97558a2e2 100644 --- a/tns-core-modules/ui/enums/enums.ts +++ b/tns-core-modules/ui/enums/enums.ts @@ -187,3 +187,8 @@ export module StatusBarStyle { export const light = "light"; export const dark = "dark"; } + +export module SystemAppearance { + export const light = "light"; + export const dark = "dark"; +} diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index fc52f37b8..b35a2e844 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -1289,6 +1289,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { pushToRootViewCssClasses(`${CLASS_PREFIX}${ANDROID_PLATFORM}`); pushToRootViewCssClasses(`${CLASS_PREFIX}${deviceType}`); pushToRootViewCssClasses(`${CLASS_PREFIX}${application.android.orientation}`); + pushToRootViewCssClasses(`${CLASS_PREFIX}${application.android.systemAppearance}`); const rootViewCssClasses = getRootViewCssClasses(); rootViewCssClasses.forEach(c => this._rootView.cssClasses.add(c)); diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 7423e9cbb..145754bfb 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -2,6 +2,7 @@ import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from "."; +import { ios as iosView } from "../core/view"; import { Page } from "../page"; import { profile } from "../../profiling"; @@ -532,6 +533,18 @@ class UINavigationControllerImpl extends UINavigationController { return null; } + + // 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(previousTraitCollection)) { + owner.notify({ eventName: iosView.traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } function _getTransitionId(nativeTransition: UIViewAnimationTransition, transitionType: string): string { diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index d021b75a3..4fd9a4fc3 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -280,6 +280,18 @@ class UIViewControllerImpl extends UIViewController { iosView.layoutView(this, owner); } } + + // Mind implementation for other controllerss + public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void { + super.traitCollectionDidChange(previousTraitCollection); + + if (majorVersion >= 13) { + const owner = this._owner.get(); + if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) { + owner.notify({ eventName: iosView.traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } export class Page extends PageBase { diff --git a/tns-core-modules/ui/tab-view/tab-view.ios.ts b/tns-core-modules/ui/tab-view/tab-view.ios.ts index 5c0f939b2..94c12460b 100644 --- a/tns-core-modules/ui/tab-view/tab-view.ios.ts +++ b/tns-core-modules/ui/tab-view/tab-view.ios.ts @@ -71,6 +71,18 @@ class UITabBarControllerImpl extends UITabBarController { } }); } + + // 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(previousTraitCollection)) { + owner.notify({ eventName: iosView.traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControllerDelegate { diff --git a/tns-core-modules/ui/tabs/tabs.ios.ts b/tns-core-modules/ui/tabs/tabs.ios.ts index 7e7c947fd..befa5a202 100644 --- a/tns-core-modules/ui/tabs/tabs.ios.ts +++ b/tns-core-modules/ui/tabs/tabs.ios.ts @@ -200,6 +200,18 @@ class UIPageViewControllerImpl extends UIPageViewController { scrollView.frame = CGRectMake(0, scrollViewTop, this.view.bounds.size.width, scrollViewHeight); //this.view.bounds; } } + + // 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(previousTraitCollection)) { + owner.notify({ eventName: iosView.traitCollectionColorAppearanceChangedEvent, object: owner }); + } + } + } } class UIPageViewControllerDataSourceImpl extends NSObject implements UIPageViewControllerDataSource {