It's "easy" to KVO a value in Swift3, using the technique in this great post...
https://stackoverflow.com/a/25219216/294884
How do I KVO the alpha of a UIView?
Goal: imagine a screen with a small view V. Screen has a large table view. Each cell has a small view CV.
I want each CV to follow1 the alpha of V, actually during an animation of the alpha of V.
(The only real alternate I can think of is to CADisplayLink the cells and poll the alpha of V, which kind of sucks.)
1Reason for that? It's the only way to sync alpha changes across cells.
Note Just as Rob explains below, in fact the alpha of a layer does not animate. (It just sets "instantly" to the new value at the beginning of an animation.) In fact you have to follow the presentation layer. Here's an example of the converse process, pulling it from where you want every draw
let d = CADisplayLink(target: self, selector: #selector(ThisClassName.updateAlpha))
d.add(to: RunLoop.current, forMode: RunLoopMode.commonModes)
func updateAlpha() {
let a = leader.layer.presentation()?.value(forKey: "opacity") as! CGFloat
follower.alpha = a
}
This is not an ideal use-case of KVO. You'd probably prefer to go to the code that is changing the alpha
and have it do the necessary updates of the other views (or build some interface to do that more generically; something other than KVO).
Also note that if you're dealing with animations, the KVO of alpha
may only identify the initiation of the animation, not observing the changes mid-flight during the animation. (Those sorts of changes are generally captured with the presentation
/presentationLayer
in a CADisplayLink
or the like.)
Having said that, yes, you can observe alpha
property. So, in Swift 3, just define context:
private var observerContext = 0
And then add the observer:
observedView.addObserver(self, forKeyPath: "alpha", options: .new, context: &observerContext)
and then implement your observer:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// do what you want here
}
Note, remember to remove the observer. For example, you might do that in deinit
:
deinit {
observedView.removeObserver(self, forKeyPath: "alpha", context: &observerContext)
}
Note, in Swift 4, this process is simplified a bit. Define a token
property to keep track of your observer:
private var token: NSKeyValueObservation?
And then observe the alpha
property:
token = observedView.observe(\.alpha) { [weak self] object, change in
// do something
}
Note the use of [weak self]
to ensure the closure doesn't cause strong reference cycle. Obviously, if you're not referencing self
in that closure, that is not needed.
But the virtue of the block based pattern is that (a) when the the token
falls out of scope, the observer is automatically removed, so no special deinit
routine is needed; (b) the observable keys for observedView
are now strongly typed, avoiding simple typographical errors in the keys; and (c) because the observer is linked to this closure, we no longer have to do that check of the context of the observer and call self
if this wasn't our context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With