无法在swift中创建程序化的Cocoa绑定

问题描述 投票:0回答:1

我正在使用swift开发macOS应用程序。我的应用程序中有一个可以启用或禁用的Alarm对象。我已经从警报的启用属性创建了一个Cocoa绑定到用户界面中的复选框。为了验证绑定是否有效,我在Alarm的启用状态的didSet属性观察者方法中添加了一个print语句,当复选框切换时,该消息确实打印出来。

我现在想编写一个单元测试来确保在启用属性切换时实际发生的操作,并且我在单元测试中以编程方式创建绑定时遇到困难,这将模仿Interface Builder中创建的绑定会发生什么。

测试看起来像这样:

func testAlarmTogglesEnabledState() {
    let alarm = Alarm()
    let enabler = AlarmEnabler(alarm)

    enabler.enable()

    XCTAssertTrue(alarm.enabled)
}

测试实际上比这更复杂,因为警报实际上需要一些动作来启用或禁用它自己,但是,这有望用于基本上说明正在发生的事情。

AlarmEnabled类如下所示:

@objc private class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        let name = NSBindingName(rawValue: "enabled")
        bind(name, to: alarm, withKeyPath: "enabled", options: nil)
    }

    func enable() {
        enabled = true
    }

    @objc dynamic var enabled = false
}

并且Alarm类具有以下形式:

@objc class Alarm: NSObject {
    …

    @objc private(set) var enabled = true {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }

    …
}

测试运行时,从不打印didSet属性观察器中的消息,因此编程绑定显然不起作用。

这是我试图编写的一个操场,基本上完成了单元测试试图完成的操作:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc dynamic var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        self.alarm = alarm
        super.init()
        let name = NSBindingName(rawValue: #keyPath(AlarmEnabler.enabled))
        bind(name, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
//      bind(name, to: alarm, withKeyPath: #keyPath(AlarmEnabler.enabled), options: nil)
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    deinit {

    }

    func enable() {
        enabled = true
        checkBox.state = .on
    }

    func disable() {
        enabled = false
        checkBox.state = .off
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
    @objc dynamic var enabled = false
    let alarm: Alarm
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()

它将警报绑定到正确状态,并尝试使用复选框模拟用户界面中发生的情况。这些似乎都不能让财产改变。什么可以让游乐场代码正常工作?

swift cocoa-bindings
1个回答
0
投票

好的,感谢Willeke,我能够创建一个有效的游乐场,我也能够编写一个单元测试并让它通过。这是一个可以正常工作的游乐场版本:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc private(set) var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    func enable() {
        checkBox.state = .on
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    func disable() {
        checkBox.state = .off
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()
© www.soinside.com 2019 - 2024. All rights reserved.