代码之家  ›  专栏  ›  技术社区  ›  brandonscript

将父容器的平移手势传递给嵌套的UICollectionView

  •  6
  • brandonscript  · 技术社区  · 8 年前

    这很好用。还有很多代码可以用来构造这个,我很乐意与大家分享,但我认为这与此无关,所以我暂时省略它。

    我已经能够解决这个问题,这样我就可以用决定性的平移手势向上滚动分割视图,并用手指快速滑动(我想是滑动手势吧?)来滚动集合视图,但这真的是一种低于标准的体验:你不能同时平移视图和滚动集合视图,而期望用户一致地复制相似但不同的手势来控制视图太难了。

    为了解决这个问题,我尝试了几种代理/协议解决方案,其中我检测分割器在分割视图中的位置并启用/禁用 canCancelTouchesInView isUserInteractionEnable 基于底视图是否完全展开的集合视图。这在一定程度上有效,但在以下两种情况下无效:

    1. 当分割视图分割器处于其默认位置时,如果用户向上平移到底视图完全展开的位置,然后继续向上平移,则集合视图应开始滚动,直到手势结束。
    2. 当拆分视图分隔器位于顶部(底部容器视图完全展开)且集合视图处于

    下面是一个演示此行为的动画:

    enter image description here

    有鉴于此,我开始认为解决这个问题的唯一方法是在分割视图上创建一个委托方法,当底视图处于最大高度时告诉集合视图,然后可以拦截父视图的平移手势或将屏幕触摸转发到集合视图?但是,我不知道该怎么做。如果我找到了正确的解决方案,那么我的问题很简单: 我如何将平移手势向前或传递给集合视图,并使集合视图与它在一开始捕捉到触摸时的交互方式相同? pointInside touches____ 方法?

    如果我不能这样做,我还能怎么解决这个问题?


    shouldScroll ,我使用一些平移方向和定位信息来确定滚动视图是否应该滚动。然后在中返回该值 UIGestureRecognizerDelegate gestureRecognizer:shouldReceive touch: 委托方法:

    // protocol delegate
    protocol GalleryCollectionViewDelegate {
        var shouldScroll: Bool? { get }
    }
    
    // shouldScroll property
    private var _shouldScroll: Bool? = nil
    var shouldScroll: Bool {
        get {
            // Will attempt to retrieve delegate value, or self set value, or return false
            return self.galleryDelegate?.shouldScroll ?? self._shouldScroll ?? false
        }
        set {
            self._shouldScroll = newValue
        }
    }
    
    // UIGestureRecognizerDelegate method
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        return shouldScroll
    }
    
    // ----------------
    // Delegate property/getter called on the split view controller and the logic:
    var shouldScroll: Bool? {
        get {
            return panTarget != self
        }
    }
    
    var panTarget: UIViewController! {
        get {
            // Use intelligent position detection to determine whether the pan should be
            // captured by the containing splitview or the gallery's collectionview
            switch (viewState.currentPosition,
                    viewState.pan?.directionTravelled,
                    galleryScene.galleryCollectionView.isScrolled) {
            case (.top, .up?, _), (.top, .down?, true): return galleryScene
            default: return self
            }
        }
    }
    

    gestureRecognizer:shouldRecognizeSimultaneouslyWith: ,但我还没到那里。

    3 回复  |  直到 8 年前
        1
  •  8
  •   HMHero    8 年前

    让子视图作为底部视图实际上占据了整个屏幕,并设置集合视图的contentInset。从上到上的视图高度。然后在底部视图上方添加另一个子视图控制器。然后,您需要做的唯一一件事是使父视图控制器成为代理,以侦听底部视图的集合视图的滚动偏移,并更改顶部视图的位置。没有复杂的手势识别器。只有一个滚动视图(集合视图)

    enter image description here

    更新:试试这个!!

    import Foundation
    import UIKit
    
    let topViewHeight: CGFloat = 250
    
    class SplitViewController: UIViewController, BottomViewControllerScrollDelegate {
    
        let topViewController: TopViewController = TopViewController()
        let bottomViewController: BottomViewController = BottomViewController()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            automaticallyAdjustsScrollViewInsets = false
    
            bottomViewController.delegate = self
            addViewController(bottomViewController, frame: view.bounds, completion: nil)
            addViewController(topViewController, frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: topViewHeight), completion: nil)
        }
    
        func bottomViewScrollViewDidScroll(_ scrollView: UIScrollView) {
            print("\(scrollView.contentOffset.y)")
    
            let offset = (scrollView.contentOffset.y + topViewHeight)
            if offset < 0 {
                topViewController.view.frame.origin.y = 0
                topViewController.view.frame.size.height = topViewHeight - offset
            } else {
                topViewController.view.frame.origin.y = -(scrollView.contentOffset.y + topViewHeight)
                topViewController.view.frame.size.height = topViewHeight
            }
        }
    }
    
    class TopViewController: UIViewController {
    
        let label = UILabel()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            automaticallyAdjustsScrollViewInsets = false
            view.backgroundColor = UIColor.red
    
            label.text = "Top View"
            view.addSubview(label)
        }
    
        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            label.sizeToFit()
            label.center = view.center
        }
    }
    
    protocol BottomViewControllerScrollDelegate: class {
        func bottomViewScrollViewDidScroll(_ scrollView: UIScrollView)
    }
    
    class BottomViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
    
        var collectionView: UICollectionView!
    
        weak var delegate: BottomViewControllerScrollDelegate?
    
        let cellPadding: CGFloat = 5
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            view.backgroundColor = UIColor.yellow
            automaticallyAdjustsScrollViewInsets = false
    
            let layout = UICollectionViewFlowLayout()
            layout.minimumInteritemSpacing = cellPadding
            layout.minimumLineSpacing = cellPadding
            layout.scrollDirection = .vertical
            layout.sectionInset = UIEdgeInsets(top: cellPadding, left: 0, bottom: cellPadding, right: 0)
    
            collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
            collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            collectionView.contentInset.top = topViewHeight
            collectionView.scrollIndicatorInsets.top = topViewHeight
            collectionView.alwaysBounceVertical = true
            collectionView.backgroundColor = .clear
            collectionView.dataSource = self
            collectionView.delegate = self
            collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: String(describing: UICollectionViewCell.self))
            view.addSubview(collectionView)
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 30
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: UICollectionViewCell.self), for: indexPath)
            cell.backgroundColor = UIColor.darkGray
            return cell
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            let width = floor((collectionView.frame.size.width - 2 * cellPadding) / 3)
            return CGSize(width: width, height: width)
        }
    
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            delegate?.bottomViewScrollViewDidScroll(scrollView)
        }
    }
    
    extension UIViewController {
    
        func addViewController(_ viewController: UIViewController, frame: CGRect, completion: (()-> Void)?) {
            viewController.willMove(toParentViewController: self)
            viewController.beginAppearanceTransition(true, animated: false)
            addChildViewController(viewController)
            viewController.view.frame = frame
            viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            view.addSubview(viewController.view)
            viewController.didMove(toParentViewController: self)
            viewController.endAppearanceTransition()
            completion?()
        }
    }
    
        2
  •  3
  •   matt    8 年前

    你不能“放弃”一个手势,因为手势识别器仍然是同一个对象及其 view

    然而,没有什么能阻止你告诉其他人对手势的反应。集合视图是一个滚动视图,因此您知道它是如何在每个时刻滚动的,并且可以并行执行其他操作。

        3
  •  1
  •   nicksweet    8 年前

    UICollectionViewDelegateFlowLayout . 如果您的顶视图需要任何特殊的滚动行为,例如视差,您仍然可以在单个集合视图中通过实现继承自的自定义布局对象来实现这一点 UICollectionViewLayout .

    使用 这种方法比实现自定义布局更简单,所以如果您想尝试一下,请尝试以下方法:

    • UICollectionViewCell 并在收藏视图中注册。

    • func register(_ viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String)

    • 让集合视图控制器符合 UICollectionViewDelegateFlowLayout ,创建布局对象作为的实例 UICollectionViewFlowLayout

    • 使生效 optional func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 在collecton视图控制器中返回每个不同视图的所需大小。

    推荐文章