I have a custom UIView (called GridView) that I initialize and then add to a ViewController (DetailViewController). GridView contains an Int property called numbers and I would like to know in DetailViewController when that property changes. Is it possible to use a property observer to solve this?
Swift provides you specific hooks, e.g. didSet which you can use for notifications when a property changes:
var x : Int { didSet { callAMethod() } }
You can then combine this with any other technique, e.g. delegation, callbacks, NSNotificationCenter and so on.
Related
So I have a custom view touchableView inside a ViewController.
touchableView informs ViewController of changes in its properties through a delegate protocol (ViewController being the delegate).
What is the best method to change properties of touchableView from ViewController (so the other way around)?
Is there a way to create a two-way delegate relationship between two classes?
Simply:
Assuming that you are already have touchableView instance in the ViewController, you should be able to set -or get-/call its properties and methods.
For instance, assume that you have the following method in touchableView class:
class func fromNib() -> TouchableView {
return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)![0] as! TouchableView
}
You should simply be able to:
let touchableView = TouchableView.fromNib()
thus:
// for example
touchableView.myProperty = "Hello"
I assumed that TouchableView has a string property called myProperty...
Remark:
For some reason, I suggest to implement some of property observers in TouchableView:
Property observers observe and respond to changes in a property’s
value. Property observers are called every time a property’s value is
set, even if the new value is the same as the property’s current
value.
They might be -somehow- useful to be implemented in your custom class. For clarity, let's consider -for instance- that if editing the value of myProperty should be effecting the background color of the view, it might be implemented as:
var myProperty: String = "Initial Value" {
willSet {
print("About to set value to: \(newValue)")
}
didSet {
backgroundColor = UIColor.red
}
}
Further Reading:
If you are looking for an advanced approach for two way bindings (Implementing MVVM), you might want to check this article.
I would also suggest to take a look at some frameworks that will be so helpful for such an approach, such as RxSwift, for a more simple framework, you might want to check ReactiveKit/Bond.
As a preface, this might be an incredibly simple and/or ignorant question.
In ReactiveCocoa 2.x, we were able to use RACObserve and RAC to observe properties of an object. From the documentation I can find in Reactive 3 and 4, we now use PropertyType to observe changes to an object property. I have so far been unsuccessful in being able to observe any property change when using MutableProperty or DynamicProperty.
class TempObject {
var property: String
}
let tempObject = TempObject()
let propertyObserver: MutableProperty<String> = MutableProperty(tempObject.property)
From what I understand, I should be able to use propertyObserver to view changes to tempObject.property. I tried adding a map function to the signal producer from propertyObserver to see if it was firing, but don't see anything when updating tempObject.property. Again, could be a trivial thing that I am missing, thanks so much.
Edit
NachoSoto nailed it - I needed to make my property KVO compliant. I also ended doing this:
let tempObjectSignal: MutableProperty<TempObject> = MutableProperty(tempObject)
let propertyObserver: MutableProperty<String> <~ tempObjectSignal.producer.map({ $0.property })
And whenever tempObject.property is updated I make sure to call
tempObjectSignal.value = tempObject
This fires off all the necessary signals. I don't know if this breaks any best practices, though. Let me know what you think!
MutableProperty(value) creates a mutable property but only with value that as the initial value.
What you want to use is DynamicProperty, which will use the Objective-C runtime and KVO to detect changes to an object's property:
let property = DynamicProperty(tempObject, "property")
For that reason, however, you need to make sure that the property you want to observe is part of the Objective-C runtime, by making the class a subclass of NSObject, and by either using the dynamic keyword:
class TempObject: NSObject {
dynamic var property: String
}
Or using #objc to ensure that it gets exported to the runtime:
class TempObject: NSObject {
#objc var property: String
}
Hi so I have a protocol called DTNavigationControllerDataSource which is defined as (leaving out the methods)
protocol DTNavigationControllerDataSource: NSObjectProtocol
Then, in my own custom UINavigationController subclass I have an instance variable of this protocol as follows.
var dataSource: DTNavigationControllerDataSource?
Basically I need to know when an object becomes the dataSource. i.e The dataSource property is set. So I can call the dataSource when it is initially set, to setup some state. I've thought about using KVO however NSObjectProtocol doesn't define any methods for doing so. I'm not sure if this is even possible or if there is an alternative way outside of using KVO to know when the dataSource property inside DTNavigationController is set. I've also tried didSet and willSet but Xcode threw errors at me, as I think that is only available for primitives.
The didSet approach should work fine actually, had no issue with the following code in a playground (Xcode 7 GM):
class DTNavigationController : UINavigationController {
var dataSource: DTNavigationControllerDataSource? {
didSet {
print("new data source: \(dataSource)")
}
}
}
I would like to set a willSet statement to isResting property. Whenever isResting is changed some function is called. This is what I mean:
starNode.physicsBody?.isResting: Bool = false{
willSet{
if newValue == true{
print("GOOGOO")
}else{
print("STOP")
}
}
}
This is not working. Can you assign a willSet or didSet to an already defined iOS property? If not, how do I get similar effect?
This is the documentation:
https://developer.apple.com/library/mac/documentation/SceneKit/Reference/SceneKit_Framework/index.html#//apple_ref/doc/uid/TP40012283
https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNPhysicsBody_Class/index.html#//apple_ref/occ/instp/SCNPhysicsBody/isResting
If not read-only:
One option would be to subclass the library object and override the property you'd like to set. Then, you can implement willSet within your subclass:
class Object {
var property: Float = 0.0
}
class ChildObject: Object {
override var property: Float {
willSet {
print("Set in child class")
}
}
}
This is the way that Apple recommends in the documentation (see "Overriding Property Observers"):
You can use property overriding to add property observers to an
inherited property. This enables you to be notified when the value of
an inherited property changes, regardless of how that property was
originally implemented.
If read-only:
You might be able to use Key-Value Observing, if the object inherits from NSObject, or if it has been marked dynamic in Swift.
Is this property an IBInspectable property?
In Objetive-C this is automatically if this is a property trough getter and setter, but in Swift it isn't. You can make this property as private and create methods for set this property and inside this method, add a call for another method.
func setResting(resting:Bool) {
self.isResting = resting
//call another method here
}
I'm trying to play a little bit with swift and iOS 8.
The question is, I'm trying to create this setter in a view controller:
- (void)setViewController:(UIViewController *)viewController
{
_viewController = viewController;
method do something
}
This code below is in Objective-C so I'd like to create it in swift.
I think, I should use willSet, but I know how do it if it's defined when you have your own variable, how can I define that willSet method if that property is a ViewController property.
Thanks
Something like this:
var viewController: UIViewController? {
willSet {
//method to do something
}
}
You can actually access what viewController will be set to with the variable "newValue" if you need that in the method call.
Note: I made the property optional here just to get rid of compiler warnings about needing initialization
An alternate way different from #Eric's answer is by using a computed property, which mimics objective-c properties more closely, consisting on defining a pseudo-private data member and a computed property implementing a getter and a setter:
class MyClass {
var _viewController: UIViewController?
var viewController : UIViewController? {
get {
return self._viewController
}
set {
self._viewController = newValue
// Do something else
}
}
}
I defined the property pseudo-private because swift doesn't have access modifiers (yet), so everything is public - but when I see an underscore prefixing a variable or property name, that to me means private (It's a convention I use it a lot in javascript for instance).