我正在尝试构建一个复杂的拆分 View 容器 Controller ,它有助于两个可变高度的容器,每个容器都有自己的嵌套 View Controller 。父 Controller 上有一个全局平移手势,允许用户拖动 View 容器中的任何位置并在 View 之间上下滑动“分隔线”。它还具有一些智能位置阈值检测逻辑,可以扩展任一 View (或重置分隔器位置):
这工作正常。还有很多代码来构建这个,我很乐意分享,但我认为它不相关,所以我暂时省略它。
我现在试图通过在底部 View 中添加一个集合 View 来使事情复杂化:
我已经能够解决这个问题,以便我可以使用决定性的平移手势向上滚动拆分 View ,并通过快速滑动手指(滑动手势,我想是?)来滚动集合 View ,但是这个是一种非常差的体验:您不能同时平移 View 和滚动集合 View ,并且期望用户始终如一地复制相似但不同的手势以控制 View 对于交互来说太困难了。
为了尝试解决这个问题,我尝试了几种委托(delegate)/协议(protocol)解决方案,在这些解决方案中我检测分割 View 中分隔符的位置并启用/禁用 canCancelTouchesInView
和/或 isUserInteractionEnable
在基于底部 View 是否完全展开的集合 View 上。这在一定程度上有效,但不适用于以下两种情况:
这是说明此行为的动画:
鉴于此,我开始认为解决问题的唯一方法是在拆分 View 上创建一个委托(delegate)方法,该方法告诉集合 View 何时底部 View 处于最大高度,然后可以拦截父级的平移手势或向前屏幕触摸到集合 View ?但是,我不知道该怎么做。如果我的解决方案是正确的,那么我的问题很简单:如何将平移手势转发或传递给集合 View ,并使集合 View 以与首先捕获触摸的方式相同的方式进行交互? 我可以用
pointInside
做些什么吗?或 touches____
方法?如果我不能这样做,我还能如何解决这个问题?
赏金猎人更新:我在集合 View 上创建了一个委托(delegate)方法,并在拆分 View 容器上调用它以设置属性
shouldScroll
,通过它我使用一些平移方向和定位信息来确定 ScrollView 是否应该滚动。然后我在 UIGestureRecognizerDelegate
中返回这个值的 gestureRecognizer:shouldReceive touch:
委托(delegate)方法:// 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
}
}
}
当您开始滚动时,这可以正常工作,但一旦在集合 View 上启用滚动,则效果不佳,因为滚动手势几乎总是覆盖平移手势。我想知道我是否可以与
gestureRecognizer:shouldRecognizeSimultaneouslyWith:
联系起来,但我还没到。
最佳答案
如何让底部 View 的 subview 实际上占据整个屏幕并将集合 View 的 contentInset.top 设置为顶部 View 高度。然后在底部 View 上方添加另一个 subview Controller 。然后你唯一需要做的就是让父 View Controller 成为代理来监听底部 View 的集合 View 的滚动偏移并改变顶部 View 的位置。没有复杂的手势识别器的东西。只有一个 ScrollView (集合 View )
更新:试试这个!!
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?()
}
}
关于ios - 将父容器的平移手势传递给嵌套的 UICollectionView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46246763/