ios 是否可以从父trait集合中更改自定义trait并观察所有视图控制器的更改?

np8igboo  于 2023-10-21  发布在  iOS
关注(0)|答案(1)|浏览(106)

我正在尝试创建一个自定义trait对象,并使用iOS 17中的一些新API将其放入UITraitCollection类中。举个例子,我想做的是创建多个应用主题trait,将它们放入trait集合类,并使所有当前可用的UIViewController对象观察主题trait的变化。我知道在iOS 17中,UIKit有一个新的方法,通过改变新的traitOverrides属性来覆盖trait定义。但是基于WWDC23 video session,Apple告诉我们仅将此属性用于特定的视图控制器或视图及其所有子视图/子视图。因此,这些更改不会反映到内存中当前可用的所有视图控制器。以下是我目前所做的:
创建一个新的自定义UITraitDefinition

enum ThemeTraitType: Int {
    case light
    case dark
    case monochrome
}

struct ThemeTrait: UITraitDefinition {
    typealias Value = ThemeTraitType
    
    static let defaultValue: ThemeTraitType = .light
    static var name: String = "Theme"
    static var affectsColorAppearance: Bool = true
}

使用扩展将其添加到UITraitCollection类中:

extension UITraitCollection {
    var currentTheme: ThemeTraitType { self[ThemeTrait.self] }
}

extension UIMutableTraits {
    var currentTheme: ThemeTraitType {
        get { self[ThemeTrait.self] }
        set { self[ThemeTrait.self] = newValue }
    }
}

注册trait changes observer:

final class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        registerForTraitChanges([ThemeTrait.self], handler: { (self: Self, previousTraitCollection: UITraitCollection) in
            // Do something here...
        })
    }

}

使用新的traitOverrides属性更改currentTheme trait:

traitOverrides.currentTheme = .dark

这和我改变trait的视图控制器预期的一样。但是,假设我使用UINavigationController,导航控制器中有5个视图控制器,我在最后一个视图控制器(堆栈中的第5个视图控制器)中更改了trait,当我返回到前一个视图控制器时,trait更改不会反映在所有之前的视图控制器上。我想做的是,我在任何视图控制器中所做的更改,也将反映到当前存在于内存中的所有可用的视图控制器中。这是否可能实施?谢谢

zvokhttg

zvokhttg1#

UIKit trait系统是一种通过应用层次结构的树结构传播数据的机制。你可以在你提到的WWDC video中看到trait层次结构的插图-但总体思路是它从每个UIWindowScene开始,包括连接到该窗口场景的所有UIWindow示例,然后在每个窗口中,它基于视图层次结构中的UIView示例。任何UIViewController,如果其view已被添加到视图层次结构(即view.window != nil),则是trait层次结构的一部分。
然而,一个对象(例如,视图、视图控制器等)在任何时间点都不一定是特性层次结构的一部分。例如,一个新创建的视图或视图控制器还没有被添加到层次结构中(例如,使用addSubview添加视图,或通过呈现视图控制器等)还不是特性层次结构的一部分。类似地,如果视图控制器将其视图从层次结构中移除(例如,表示被解除,或者另一视图控制器被推入导航堆栈,导致先前的视图控制器消失),则该视图控制器不再是特性层次结构的一部分,直到其视图稍后被放回该层次结构(如果有的话)。
数据只通过trait系统流向属于trait层次结构的对象。这是非常重要的,因为任何给定对象接收的确切值取决于它在层次结构中的 * 位置 *,因为trait是从父对象继承到子对象的。如果一个对象的父对象还没有被添加到层次结构中,所以它的父对象还不知道,那么就不可能确定地知道它应该接收哪个trait值。
因此,trait系统并不是设计来将值传播到不在层次结构中的对象的trait集合。如果/当对象稍后被添加到层次结构时,它将在那时接收更新的trait值。
你应该问自己的最重要的问题是:为什么需要不在层次结构中的对象来接收此数据?根据定义,不是层次结构的一部分的对象对用户不可见,因此它不需要更新,直到将来某个时候,如果/当它被添加到层次结构时(此时它将接收更新的trait值)。任何具有不正确或过时的trait值的对象在被放入层次结构中时总是有机会在用户看到它们之前进行更新。
如果你真的需要向内存中的所有对象广播相同的信息,那么trait系统可能不是用于该用例的正确基础设施选择。相反,您可以考虑通过通知中心发布通知。

相关问题