bra*_*ipt 5 iphone uiscrollview ios uipangesturerecognizer swift
我正在尝试构建一个复杂的拆分视图容器控制器,它有助于两个可变高度容器,每个容器都有自己的嵌套视图控制器.父控制器上有一个全局平移手势,允许用户在视图容器中的任何位置拖动,并在视图之间向上和向下滑动"分隔符".它还有一些智能位置阈值检测逻辑,可以扩展任一视图(或重置分频器位置):

这很好用.还有很多代码可以构建它,我很乐意与大家分享,但我认为它不相关,所以暂时我会省略它.
我现在试图通过向底部视图添加集合视图来使事情复杂化:

我已经能够解决这个问题,这样我就可以用一个决定性的平移手势滚动拆分视图,然后快速轻弹手指滚动收集视图(滑动手势,我想它是?),但是这个是一个非常低级别的体验:您无法平移视图并同时滚动集合视图,并期望用户始终如一地复制相似但不同的手势以控制视图对于交互来说太难了.
为了尝试解决这个问题,我尝试了几种委托/协议解决方案,其中我在拆分视图中检测分隔符的位置,canCancelTouchesInView并isUserInteractionEnable根据底部视图是否完全展开来启用/禁用和/或在集合视图上.这在某种程度上有效,但在以下两种情况下则不然:
这是一个说明此行为的动画:
鉴于此,我开始认为解决问题的唯一方法是在分割视图上创建一个委托方法,告诉集合视图何时底部视图处于最大高度,然后可以拦截父级的平移手势或向前相反,屏幕会触及集合视图吗?但是,我不知道该怎么做.如果我使用解决方案走在正确的轨道上,那么我的问题很简单:我如何将平移手势转发或移交到集合视图,并使集合视图的交互方式与通过触摸捕获的方式相同它在第一位?我可以做的东西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
}
}
}
Run Code Online (Sandbox Code Playgroud)
这在开始滚动时可以正常工作,但在集合视图上启用滚动后效果不佳,因为滚动手势几乎总是覆盖平移手势.我想知道我是否可以连线gestureRecognizer:shouldRecognizeSimultaneouslyWith:,但我还没有.
如何让底部视图的子视图实际上占据整个屏幕并将集合视图的 contentInset.top 设置为顶部视图高度。然后在底部视图上方添加另一个子视图控制器。然后你唯一需要做的就是让父视图控制器成为代理来监听底部视图的集合视图的滚动偏移并改变顶部视图的位置。没有复杂的手势识别器的东西。只有一个滚动视图(集合视图)
更新:试试这个!!
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?()
}
}
Run Code Online (Sandbox Code Playgroud)