黑暗模式和多窗口/场景
Darkmode and multiwindows / scenes
我正在尝试在多场景应用程序中实现 ios13 暗模式。
不幸的是,当我关闭一个场景并将其拖过屏幕边缘时,方法 traitCollectionDidChange 被多次调用,但值总是不同,导致我的 UI 在暗模式和亮模式之间闪烁。
怎么了?
这是我的实现
func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
print("THEME instance: \(self)")
let currentTraitCollection = self.traitCollection
var hasUserInterfaceStyleChanged = false
hasUserInterfaceStyleChanged = previousTraitCollection.hasDifferentColorAppearanceCompared(to: currentTraitCollection)
print("THEME hasUserInterfaceStyleChanged = \(hasUserInterfaceStyleChanged ? "YES" : "NO")")
if hasUserInterfaceStyleChanged {
let userInterfaceStyle = currentTraitCollection.userInterfaceStyle // Either .unspecified, .light, or .dark
switch userInterfaceStyle {
case .unspecified:
print("THEME UIUserInterfaceStyleUnspecified")
case .light:
print("THEME UIUserInterfaceStyleLight")
case .dark:
print("THEME UIUserInterfaceStyleDark")
}
} else {
print("THEME NOT CHANGED")
}
}
这是控制台中记录的语句
当新场景出现时...
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
当添加的场景消失时...
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleDark
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = NO
THEME NOT CHANGED
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleDark
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
与此同时,我没有更改为暗模式(始终亮)...所以我希望主题不会更改。
我一直在努力解决同样的问题,我找到的解决方案在 SceneDelegate
.
一个UIScene
有多个状态:
.foregroundActive
.foregroundInactive
.background
.unattached
当您在幻灯片中调整 windows 大小时,或者在这种情况下,从幻灯片中删除一个幻灯片时,每个幻灯片都会调用 traitCollectionDidChange
。这意味着您正在为处于 .background
、.foregroundInactive
和 .unattached
状态的场景更新 userInterfaceStyle
。这就是导致闪烁的原因。
解决方案是不使用 traitCollectionDidChange
,而是使用 SceneDelegate
中名为 windowScene(_:didUpdate:interfaceOrientation:traitCollection:)
的委托方法。
根据 Apple's docs 这个方法:
Notifies you when the size, orientation, or traits of a scene change.
这样做的额外好处是我们可以在更新userInterfaceStyle
之前检查场景的.activationState
。
func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
let currentTraitCollection = windowScene.traitCollection
if windowScene.activationState == .foregroundActive {
if currentTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle {
if currentTraitCollection.userInterfaceStyle == .light {
//update to light theme
} else {
//update to dark theme
}
}
}
}
我正在尝试在多场景应用程序中实现 ios13 暗模式。
不幸的是,当我关闭一个场景并将其拖过屏幕边缘时,方法 traitCollectionDidChange 被多次调用,但值总是不同,导致我的 UI 在暗模式和亮模式之间闪烁。
怎么了?
这是我的实现
func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
print("THEME instance: \(self)")
let currentTraitCollection = self.traitCollection
var hasUserInterfaceStyleChanged = false
hasUserInterfaceStyleChanged = previousTraitCollection.hasDifferentColorAppearanceCompared(to: currentTraitCollection)
print("THEME hasUserInterfaceStyleChanged = \(hasUserInterfaceStyleChanged ? "YES" : "NO")")
if hasUserInterfaceStyleChanged {
let userInterfaceStyle = currentTraitCollection.userInterfaceStyle // Either .unspecified, .light, or .dark
switch userInterfaceStyle {
case .unspecified:
print("THEME UIUserInterfaceStyleUnspecified")
case .light:
print("THEME UIUserInterfaceStyleLight")
case .dark:
print("THEME UIUserInterfaceStyleDark")
}
} else {
print("THEME NOT CHANGED")
}
}
这是控制台中记录的语句
当新场景出现时...
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
当添加的场景消失时...
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleDark
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = NO
THEME NOT CHANGED
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleDark
THEME instance: <MainControllerViewController: 0x117e55910>
THEME hasUserInterfaceStyleChanged = YES
THEME UIUserInterfaceStyleLight
与此同时,我没有更改为暗模式(始终亮)...所以我希望主题不会更改。
我一直在努力解决同样的问题,我找到的解决方案在 SceneDelegate
.
一个UIScene
有多个状态:
.foregroundActive
.foregroundInactive
.background
.unattached
当您在幻灯片中调整 windows 大小时,或者在这种情况下,从幻灯片中删除一个幻灯片时,每个幻灯片都会调用 traitCollectionDidChange
。这意味着您正在为处于 .background
、.foregroundInactive
和 .unattached
状态的场景更新 userInterfaceStyle
。这就是导致闪烁的原因。
解决方案是不使用 traitCollectionDidChange
,而是使用 SceneDelegate
中名为 windowScene(_:didUpdate:interfaceOrientation:traitCollection:)
的委托方法。
根据 Apple's docs 这个方法:
Notifies you when the size, orientation, or traits of a scene change.
这样做的额外好处是我们可以在更新userInterfaceStyle
之前检查场景的.activationState
。
func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
let currentTraitCollection = windowScene.traitCollection
if windowScene.activationState == .foregroundActive {
if currentTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle {
if currentTraitCollection.userInterfaceStyle == .light {
//update to light theme
} else {
//update to dark theme
}
}
}
}