最近项目中需要用到滑动吸顶交互,参考了别人的demo实现,代码未封装,适合新手,简单易懂
实现原理图层
主要代码实现
外层UICollectionView
,需要自定义实现gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
方法
import UIKit
class NestedCollectionView: UICollectionView, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if let view = otherGestureRecognizer.view, view.isKind(of: NestedCollectionView.self) {
return true
} else {
if otherGestureRecognizer.isKind(of: UIPanGestureRecognizer.self),
let scrollView = otherGestureRecognizer.view as? UIScrollView {
// 解决scrollView横向滚动不能与其他scrollView纵向滚动互斥的问题
if (abs(scrollView.contentOffset.x) > 0 && abs(scrollView.contentOffset.y) == 0) { // 横向滚动
return false;
}
return true;
}
return false;
}
}
}
在外层视图控制器中,处理外层和内层手势冲突
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentSize.height <= 0 { return }
// 处理scrollView滑动冲突
let contentOffsetY = scrollView.contentOffset.y
// 吸顶临界点(此时的临界点不是视觉感官上导航栏的底部,而是当前屏幕的顶部相对scrollViewContentView的位置)
// 如果当前控制器底部存在TabBar/ToolBar/自定义的bottomBar, 还需要减去barHeight和SAFE_AREA_INSERTS_BOTTOM的高度
let criticalPointOffsetY = scrollView.contentSize.height - UIScreen.main.bounds.height
// 利用contentOffset处理内外层scrollView的滑动冲突问题
if contentOffsetY - criticalPointOffsetY >= 0 {
/*
* 到达临界点:
* 1.未吸顶状态 -> 吸顶状态
* 2.维持吸顶状态 (pageViewController.scrollView.contentOffsetY > 0)
*/
cannotScroll = true
scrollView.contentOffset = CGPoint(x: 0, y: criticalPointOffsetY)
segmentedViewController.makePageViewControllersScrollState(canScroll: true)
} else {
/*
* 未达到临界点:
* 1.维持吸顶状态 (pageViewController.scrollView.contentOffsetY > 0)
* 2.吸顶状态 -> 不吸顶状态
*/
if cannotScroll {
// “维持吸顶状态”
scrollView.contentOffset = CGPoint(x: 0, y: criticalPointOffsetY)
} else {
// 吸顶状态 -> 不吸顶状态
segmentedViewController.makePageViewControllersScrollToTop()
}
}
}
内层子页面都需要继承PageListViewController
控制器,处理子页面是否可以滚动
import UIKit
protocol PageListViewControllerDelegate: AnyObject {
func pageViewControllerLeaveTop()
}
extension PageListViewController {
static func == (lhs: PageListViewController, rhs:PageListViewController) -> Bool {
return lhs.identifier == rhs.identifier
}
}
class PageListViewController: UIViewController {
public var identifier: String?
public var canScroll: Bool = false {
didSet {
if !canScroll {
scrollView?.contentOffset = CGPoint.zero
}
}
}
public weak var delegate: PageListViewControllerDelegate?
private var scrollView: UIScrollView?
override func viewDidLoad() {
super.viewDidLoad()
}
public func scrollToTop() {
scrollView?.setContentOffset(CGPoint.zero, animated: true)
}
}
extension PageListViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.scrollView = scrollView
if canScroll {
if scrollView.contentOffset.y <= 0 {
canScroll = false
delegate?.pageViewControllerLeaveTop()
}
} else {
canScroll = false
}
}
}
动图效果见demo
Demo地址:吸顶demo
参考文章:
https://www.jianshu.com/p/8b87837d9e3a