In the MVC parlour, a binding can (up to a point) replace a Controller. You will probably not want to go that far, but at least for simple GUI's it is possible (as we shall see in this example).
To introduce the concept, lets look at a very simple example: Connecting a textfield to a String variable.
Since this is going to be a very simple example, I want to put the variable in the AppDelegate like this:
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var parameterId: String = "Any Name"
class StringWrapper {
var str: String
init(str: String) {
self.str = str
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var parameterId: StringWrapper = StringWrapper(str: "One")
class StringWrapper: NSObject {
var str: String
init(str: String) {
self.str = str
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var parameterId: StringWrapper = StringWrapper(str: "One")
Open up the MainMenu.xib file and make sure you display the object/property hierarchy.
Next pick a ObjectController and drop in in the "Objects", it should show up like this (the Object Controller is placed under the "Font Manager"):
Select the Object Controller and bring up the "Bindings Inspector". Here we have to set the "Content Object". Select the "Bind to" checkbox, and select the "Delegate" from the popup box after it. In the "Model Key Path" enter "self.parameterId". Once you entered "self." a popup should appear from which you can choose "parameterId". If that does not happen, check the spelling and the code in AppDelegate.swift before continuing.
Select the "Window" and drop the "NSTextField" in it. You can also add a label should you want to.
Now select the "Text Field":
and bring up the "Binding Inspector" again. This time we edit the "Value" binding as follows:
Click the "Bind to" check box, select the "Object Controller" and enter "self.str" in the Model Key Path. The "Controller Key" remains empty (will be filled in automatically later), and ignore the warning symbol. There is no popup selection box this time (or rather, it is empty). Maybe this will be fixed in a future version of xcode.
Now compile and run the app. You won't see the updates you'll make in the textfield, to see that the variable is actually updated, add an observer:
class StringWrapper: NSObject {
var str: String {
didSet {
print("New value = \(str)")
}
}
init(str: String) {
self.str = str
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var parameterId: StringWrapper = StringWrapper(str: "One") {
didSet {
print("Will never be called")
}
}
One more thing: When you want to update the value of parameterId.str do not simply assign the value, but use a KVC compliant method. Thus do not:
parameterId.str = "Will not update the view"
parameterId.setValue("Will update the view", forKey: "str")
Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH
We don't get the world we wish for... we get the world we pay for.
Hi. Is the wrapping and using ObjectController really necessary? I usually bind to File's owner. Can you also please give me a hint why you used wrapper? I can bind it directly. I might be missing something.
ReplyDeleteIf you mark your property/var as dynamic you don't have to call setValue forKey, assign is fine.
ReplyDeleteThe object controller was necessary for my real life app which involved an array. And I did not know that it is possible to bind directly to the file's owner.
ReplyDeleteThe wrapper is probably only needed if a controller is used.
I still prefer to use controllers as I also usually group properties that belong together. That makes for a nicer fit (but your milage may vary...)
Thanks for the tip about the 'dynamic' marking.