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

2024-02-26

我正在尝试构建一个复杂的分割视图容器控制器,该控制器有利于两个可变高度容器,每个容器都有自己的嵌套视图控制器。父控制器上有一个全局平移手势,允许用户拖动视图容器中的任意位置,并在视图之间上下滑动“分隔线”。它还具有一些智能位置阈值检测逻辑,可以扩展任一视图(或重置分隔线位置):

     

这很好用。还有很多代码可以构建这个,我很乐意分享,但我认为它们不相关,所以我暂时忽略它。

我现在尝试通过向底部视图添加集合视图来使事情变得复杂:

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

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

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

下面的动画演示了这种行为:

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

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


赏金猎人的更新:我在集合视图上创建委托方法并在拆分视图容器上调用它来设置属性时遇到了一些零碎的运气shouldScroll,通过它我使用一些平移方向和定位信息来确定滚动视图是否应该滚动。然后我返回这个值UIGestureRecognizerDelegate's 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:,但我还没到那儿。


如何使底部视图的子视图实际上占据整个屏幕并将集合视图的 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?()
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将父容器的平移手势交给嵌套的 UICollectionView 的相关文章

随机推荐

  • 如何使用mysql将查询结果存储在变量中

    SET v1 SELECT COUNT FROM user rating SELECT v1 当我执行此查询时set变量显示此错误 Error Code 1064 You have an error in your SQL syntax c
  • 如何简单地扩展 docker-compose 服务并将索引和计数传递给每个服务?

    我正在寻找一种扩展 docker compose 服务的方法并看到了 scale 选项 但找不到任何方法来获取每个容器内的索引和计数 这是一个简化的撰写文件 version 2 1 services my thing restart alw
  • Java OpenCV 绑定

    我正在寻找 OpenCV java 绑定 所有引用都指向处理库 我知道处理是java 但是没有独立的java库吗 或者应该只使用处理库 我刚刚找到了这个 OpenCV 的 java 包装器 https github com bytedeco
  • Visual C++:将传统 C 和 C++ 字符串代码迁移到 Unicode 世界

    我看到 Visual Studio 2008 及更高版本现在开始使用字符集设置为 Unicode 的新解决方案 我的旧 C 代码仅处理英文 ASCII 文本 并且充满了 文字字符串如 Hello World char type char 指
  • 防止画布对象发生碰撞或交叉

    我正在画布上绘制 n 个矩形 矩形是可拖动和可缩放的 我想防止它们重叠或相交 最好的情况是 如果它们只是相互咬合 我想办法检查一下十字路口 在我的示例中 我将触摸对象的不透明度设置为 0 1 巧合的是 在我尝试解决这个问题时 我的对象在接触
  • 为什么在 cmd 中安装任何 python 模块时会收到这些错误“警告:忽略无效的分发 -yproj ”

    警告 忽略无效的分发 yproj c users space junk appdata local programs python python310 lib site packages 警告 忽略无效的分发 yproj c users s
  • 无法在 chrome google.maps api 中加载资源

    我在 chrome 控制台中出现此错误 Failed to load resource net ERR BLOCKED BY CLIENT https maps google com maps api v3 api js 22 2 intl
  • 修复 Flash 的激进缓存

    我有一个闪存应用程序 可以加载大量变化的数据偶尔 所以我设置了我的服务器来发送Last Modified标头并回复304 Not Modified当客户端的版本没有过时时 它在每个浏览器中都能正常工作 但 Flash 完全忽略这一点并积极缓
  • 在矩形的联合中找到孔?

    我在单位正方形 红色 内和周围有许多随机矩形 黑色 需要提取单位正方形内未被任何矩形覆盖的所有多边形区域 看起来这可以用 Shapely 来完成 当我得到矩形 绿色 的并集时 我已经达到了这一点 但我不知道如何从单位正方形中减去它并检索多边
  • Symfony2 路由不支持的键

    我正在学习 Symfony 2 但遇到一些问题 使用教程 我在routing yml内捆绑 acme demo homepage path hello name defaults controller AcmeDemoBundle Defa
  • 处理 Powershell 函数中的管道和参数输入

    我对 在一个月的午餐中学习 PowerShell 一书中看到的内容感到困惑 在第 21 章中 当作者讨论通过参数绑定或管道接受输入的函数时 他给出了两种模式 第一个如下 function someworkerfunction do some
  • PHP 5.4:获取实例变量的完全限定类名

    我知道有静电classPHP 5 5 上的字段 但我必须坚持使用 PHP 5 4 是否可以从变量中获取完全限定的类名 Example namespace My Awesome Namespace class Foo 以及代码中的其他地方 p
  • 尽管我已在 Xcode 5 中添加,但应用程序图标未显示

    我已经添加了如下图所示的图标 但我仍然没有在我的设备或模拟器中看到该图标 这可能是 XCode 5 的错误 即添加的图标实际上并未包含在应用程序包中 但您可以通过转到 构建阶段 展开 复制包资源 然后按底部的 号手动添加来解决此问题图标文件
  • 嵌套哈希已定义?() [重复]

    这个问题在这里已经有答案了 确定是否最简洁的方法是什么 hash key1 key2 已定义 如果 hash or hash key1 为零 defined hash key1 key2 返回 True 如果 hash key1 存在 不判
  • eclipse中从方法定义跳转到方法调用【Java视角】

    日食中 我们有一个快捷键可以从调用方法的任何地方跳转到方法定义 即使用 F3 但是 如果我当前位于方法定义上并且我想要转到调用或使用该方法的位置 该怎么办 这有什么捷径吗 你可以试试Ctrl Shift G这相当于查找参考文献 or Ctr
  • 汇编代码不识别?和@数据

    这是我的第一个汇编程序 任何人都可以帮助使其成功运行 我看到下面的编译错误 为什么它不识别 and data 我正在尝试交换汇编中的两个变量 我正在执行以下命令 nasm f elf swap asm 但我收到这个错误 swap asm 6
  • WebLogic 10gR3 - 解析多部分/表单数据的 Content-Type 标头时出现 UnsupportedEncodingException

    我的应用程序在 WebLogic 10gR3 上通过 HTTP POST 接收多部分内容 每当客户指定charset属性beforeContent Type 标头上的其他属性 例如Content Type multipart form da
  • 乘以 keyup 上的输入值

    如何将一个输入的值乘以 2 并将其保存到另一个输入 那么当我输入 10 例如 时 第二个输入的值将是 20 Thanks 两个输入字段
  • 强制溢出到顶部而不是底部

    我希望我的内容溢出到 div 的顶部和左侧而不是右下角 目的是当展开 div 时 内容看起来像是从顶部而不是从底部进入 CSS 有什么办法可以做到这一点吗 很像你设置的方式vertical align对于文本 我想让 div 溢出到顶部而不
  • 将父容器的平移手势交给嵌套的 UICollectionView

    我正在尝试构建一个复杂的分割视图容器控制器 该控制器有利于两个可变高度容器 每个容器都有自己的嵌套视图控制器 父控制器上有一个全局平移手势 允许用户拖动视图容器中的任意位置 并在视图之间上下滑动 分隔线 它还具有一些智能位置阈值检测逻辑 可