有没有一种方法可以在Swift中创建可观察的枚举(KVO)?

7

我正在尝试在MVVM中使用RxSwift进行绑定。 我有一个枚举

enum Color : Int {
    case Red = 0, Green
}

测试的类

class Test : NSObject {
    var color: Color = .Red
    dynamic var test: String? {
        didSet {
            print("didSet \(test)")
        }
    }
}

我希望您能够观察到像以下这样的更改:

test.rx_observe(Color.self, "color").subscribeNext { (color) -> Void in
     print("Observer \(color)")
}.addDisposableTo(bag)

但是程序崩溃并显示以下错误信息:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<RDProject.Test 0x7ff373513020> addObserver:<RxCocoa.KVOObserver 0x7ff37351a420> forKeyPath:@"color" options:5 context:0x0] was sent to an object that is not KVC-compliant for the "color" property.'

以下是简单的 String 代码:

test.rx_observe(String.self, "test").subscribeNext { string in
     print("Observer \(string)")
}.addDisposableTo(bag)

test.test = "1"
test.test = "2"

我在这里发现,为了使继承自NSObject的类不再继承该类,我应该将其设置为dynamic。但我无法让Enum成为动态枚举。 有没有一种方法可以使Enum可观察?
4个回答

5
你不需要使用KVO来完成这个任务。 只需像这样使用BehaviorSubject:
创建一个私有字段,如下所示:
let colorSubject = BehaviorSubject<Color?>(value: nil)

然后您会有这样一个属性,它告知BehaviorSubject值已更改。
var color : Color? {
    didSet {
        colorSubject.onNext(color)
    }
}

如果要订阅任何更改,请使用与此相等的语句:

let disposable = colorSubject.subscribeNext { (color: Color?) -> Void in
    // Do something with it.
}

2

因为你的枚举类型是Int,所以可以通过标记@objc使其兼容Objective-C。这样做将使编译器可以将属性标记为dynamic。要使该属性符合KVO规范,还需要用@objc进行注释。

@objc enum Color : Int {
    case Red = 0, Green
}

class Test : NSObject {
    @objc dynamic var color: Color = .Red
    dynamic var test: String? {
        didSet {
            print("didSet \(test)")
        }
    }
}

嗨,我正在使用 Swift 5 并尝试了这个解决方案。不幸的是,它会崩溃,并提示 致命错误:无法从 KeyPath Swift.ReferenceWritableKeyPath 中提取字符串...。你能确认该解决方案仍然有效吗? - Jeroen
确实会崩溃,但是使用@objc注释该字段可以修复崩溃并使其再次正常运行。我更新了我的答案以反映这一点。希望能有所帮助 :) - tomahh
谢谢回复!这确实解决了问题。首先,它是一个类型为“Error”的枚举,将其更改为“Int”是第一步。然后,在枚举和属性上注释@objc是使其工作的最后一步。但我仍然有一个问题,即该属性是“MyEnumType?”,因此是可选的。这在Objective-C中无法表示。我认为这个问题无法解决。但你回答了我的问题,这很有效 :-) - Jeroen

0

0
如果您的应用程序的最低支持版本是iOS 13.0或更高版本,您可以使用Combine并使用属性@Published
class Test : NSObject {
    @Published var color: Color = .Red

    dynamic var test: String? {
        didSet {
            print("didSet \(test)")
        }
    }
}

并且像这样观察它:
let test = Test()
test.$color.sink(receiveValue: { color in
    print(color)
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接