找出UIView进入(滚动到)屏幕的那一刻


  • UIView 是 MyViewControllerA的视图
  • 初始化 A 并将其视图添加为滚动视图的子视图, B的观点是这样的:
View Controller B
B View 
Scroll View
Image View
Container View
View Controller A view
A 的视图
  • 被初始化并作为子视图添加到 B 的视图中DidLoad 方法
  • 我无法控制 B,无法使用 UIScrollViewDelegate 的方法
  • A 的视图最初不在屏幕外,我们看不到它

需要以某种方式通知 A 的视图已"滚动到"屏幕。

由于您几乎没有控制权,因此最好的解决方案实际上可能是 KVO。不过我喜欢避免它。您可以创建完全无法访问任何内容的解决方案。下面将采用滚动视图的第一个超级视图,并尝试挂钩到它的内容偏移量,并在滚动视图上可见时显示,当它不可见时显示:

class MyView: UIView {
private weak var scrollView: UIScrollView?
private var observationContext = 0
private(set) var isCurrentlyVisible: Bool = false {
didSet {
if(oldValue != isCurrentlyVisible) {
print(isCurrentlyVisible ? "Visible" : "Not visisble")
}
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.findParentScrollView()
self.attachScrollViewObserving()
}
}
private func findParentScrollView() {
var scrollView: UIView? = self.superview
while scrollView != nil && !(scrollView is UIScrollView) {
scrollView = scrollView?.superview
}
self.scrollView = scrollView as? UIScrollView
}
private func attachScrollViewObserving() {
guard let scrollView = self.scrollView else { return }
scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset), options: [], context: &observationContext)
refreshIsVisibleInScrollView()
}
private func refreshIsVisibleInScrollView() {
guard let scrollView = self.scrollView else {
isCurrentlyVisible = false
return
}
let frameInScrollView = self.convert(bounds, to: scrollView)
let visibleFrame = CGRect(x: scrollView.contentOffset.x, y: scrollView.contentOffset.y, width: scrollView.bounds.width, height: scrollView.bounds.height)
isCurrentlyVisible = visibleFrame.intersects(frameInScrollView) // OR visibleFrame.contains(frameInScrollView)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if &observationContext == context {
refreshIsVisibleInScrollView()
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}

我希望你能想象这里的很多事情都是危险的,但如果你至少有一些你的观点,就可以避免:

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {

在这里,视图等待一秒钟,以确保它实际上已添加到视图层次结构中。更好的方法是在视图控制器中执行此操作,如下所示:

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myView.scrollView = self.parentScrollView
myView.attachScrollViewObserving()
}

findParentScrollView也很危险。如果你能以某种方式直接获取它,或者在创建视图控制器时分配它A你会提高稳定性。问题自然是在某些情况下,滚动视图中可能有滚动视图(请注意,表视图和集合视图也是滚动视图(。

我希望其余的应该很简单。但请注意,这还不是防弹实现。其他属性可能会影响视图的可见性,例如调整视图大小或重新定位自己的视图在其父视图中;更改内容插图;更改滚动视图本身的大小;转换。。。试图处理所有这些可能是矫枉过正,所以只需使用您认为可能会改变的内容。

最新更新