Не удается создать программную привязку Cocoa в swift

#swift #cocoa-bindings

#swift #cocoa-привязки

Вопрос:

Я разрабатываю приложение для macOS с использованием swift. В моем приложении есть объект Alarm, который можно включить или отключить. Я создал привязку Cocoa из свойства alarm enabled к флажку в пользовательском интерфейсе. Чтобы проверить, работает ли привязка, я добавил инструкцию print к методу didSet property observer для состояния Alarm enabled, и это сообщение действительно распечатывается при переключении флажка.

Теперь я хочу написать модульный тест, чтобы убедиться, что правильные действия действительно выполняются при переключении свойства enabled, и у меня возникают трудности с программным созданием привязки в модульном тесте, которая будет имитировать то, что происходит с привязкой, созданной в 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()
  

Он правильно привязывает сигнал тревоги к a, а также пытается имитировать то, что происходит в пользовательском интерфейсе, с помощью флажка. Ни один из них, похоже, не может изменить свойство. Что могло бы заставить код playground работать должным образом?

Комментарии:

1. Привязка доступна только для чтения. enabled Свойство AlarmEnabler не является привязываемым, как NSEnabledBinding привязка NSButton . Привязки Cocoa используют KVC и KVO. Разве вы не можете использовать KVC и KVO для имитации привязки?

2. Вы привязали привязку значения или разрешенную привязку флажка? Или вы привязали свойство enabled сигнала тревоги к какому свойству флажка?

3. Я мог бы использовать KVC / KVO. Я бы подумал, что могу просто использовать привязку.

4. При построении интерфейса свойство value флажка привязывается к свойству enabled сигнала тревоги. На самом деле флажок находится в TableView и привязывается к сигналу тревоги, который находится в ArrayController

5. Если вы привязываете свойство к свойству, то привязка доступна только для чтения. NSValueBinding Привязка NSButton выполняется для чтения и записи. Добавить enabled привязку, чтобы AlarmEnabler посмотреть , как работают привязки? .

Ответ №1:

Хорошо, итак, благодаря 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()