#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()