在Swift3中,KVO是UIView的alpha?

在Swift3中,KVO是UIView的alpha?,uiview,swift3,key-value-observing,Uiview,Swift3,Key Value Observing,使用这篇伟大的文章中的技术,在Swift3中KVO一个值是“容易的” 如何对UIView的alpha进行KVO 目标:想象一个屏幕有一个小视图,而屏幕有一个大的表视图。每个单元格都有一个小视图CV 我希望每个CV跟随V的alpha,实际上是在V的alpha的动画中 (我能想到的唯一真正的替代方法是将细胞连接起来,并对V的α进行投票,这太糟糕了。) 1原因是什么?这是跨单元格同步alpha更改的唯一方法 注意正如Rob在下面解释的那样,事实上,层的alpha不会设置动画。(它只是在动画开始时

使用这篇伟大的文章中的技术,在Swift3中KVO一个值是“容易的”

如何对UIView的alpha进行KVO

目标:想象一个屏幕有一个小视图,而屏幕有一个大的表视图。每个单元格都有一个小视图CV

我希望每个CV跟随V的alpha,实际上是在V的alpha的动画中

(我能想到的唯一真正的替代方法是将细胞连接起来,并对V的α进行投票,这太糟糕了。)


1原因是什么?这是跨单元格同步alpha更改的唯一方法


注意正如Rob在下面解释的那样,事实上,层的alpha不会设置动画。(它只是在动画开始时将“立即”设置为新值。)事实上,您必须遵循表示层。这里有一个逆向过程的例子,从你想画的地方画出来

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
  }

这不是KVO的理想用例。您可能更愿意转到正在更改
alpha
的代码,让它对其他视图进行必要的更新(或者构建一些更通用的界面,而不是KVO)

还请注意,如果您正在处理动画,则
alpha
的KVO可能仅识别动画的启动,而不在动画过程中观察飞行中的变化。(这些类型的更改通常通过
CADisplayLink
或类似内容中的
presentation
/
presentationLayer
捕获。)

话虽如此,您可以观察
alpha
属性。因此,在Swift 3中,只需定义上下文:

private var observerContext = 0  
然后添加观察者:

observedView.addObserver(self, forKeyPath: "alpha", options: .new, context: &observerContext)
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
}
private var token: NSKeyValueObservation?
然后实现您的观察者:

observedView.addObserver(self, forKeyPath: "alpha", options: .new, context: &observerContext)
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
}
private var token: NSKeyValueObservation?
注意,请记住移除观察者。例如,您可以在
deinit
中执行此操作:

deinit {
    observedView.removeObserver(self, forKeyPath: "alpha", context: &observerContext)
}

注意,在Swift 4中,这个过程稍微简化了一点。定义
标记
属性以跟踪观察者:

observedView.addObserver(self, forKeyPath: "alpha", options: .new, context: &observerContext)
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
}
private var token: NSKeyValueObservation?
然后观察
alpha
属性:

token = observedView.observe(\.alpha) { [weak self] object, change in
    // do something
}
注意使用
[weak self]
以确保关闭不会导致强引用循环。显然,如果在该闭包中没有引用
self
,则不需要这样做


但是基于块的模式的优点是:(a)当
标记
不在范围内时,观察者被自动移除,因此不需要特殊的
deinit
例程;(b)
observedView
的可观察键现在是强类型的,避免了键中的简单印刷错误;(c)因为观察者与这个闭包相关联,我们不再需要对观察者的上下文进行检查,如果这不是我们的上下文,就调用
self

!当然,它是
分叉路径:“alpha”
。非常感谢。@Rob有图幅/边界的键吗?是的。但是对于这一点,通常最好使用
layoutView
(对于
UIView
子类)或
viewdilayoutsubview
(对于
UIViewController
子类)。Swift 4方法在iOS 11和iOS 12上非常好,但我在iOS 10上看到了一些崩溃。。。进一步研究,看看我是不是做错了什么,还是真的是操作系统-related@Tim-iOS 12可以优雅地处理观察对象是否在您仍有观察者的情况下解除分配。iOS 10没有。在解除分配观察对象之前,需要删除观察者。也许这就是你现在的处境。这很难说,因为没有一个