gpt4 book ai didi

ios - 滚动 ScrollView 时更改触摸接收器

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:29:14 24 4
gpt4 key购买 nike

Apple 的 map 应用程序或 Google map 等应用程序使用可滚动的 Bottom Sheet 叠加层来呈现其他内容。虽然重建这种行为并不太难,但我很难实现一个重要功能:

当 Bottom Sheet 中嵌入 ScrollView 时,用户可以将其滚动到顶部,但随后——而不是从顶部弹起—— Bottom Sheet 开始向下滚动,而不是表格 View 。

这是我的意思的示例视频:

Example Video:

Bottom Sheet Example

这是一个很好的用户体验,因为滚动没有中断,这是我作为用户所期望的:就好像一旦内容 ScrollView 到达其顶部,手势接收器就会自动移交 super ScrollView 。


为了实现这种行为,我看到了三种不同的方法:

  1. 我在 ScrollView 的 scrollViewDidScroll(_:) 委托(delegate)方法中跟踪内容 ScrollView 的 contentOffset。那我做

    if contentScrollView.contentOffset.y < 0 {
    contentScrollView.contentOffset.y = 0
    }

    防止内容 ScrollView 滚动到其内容顶部之上。相反,我传递了它滚动到滚动整个 Bottom Sheet 的 super ScrollView 的 y 距离。

    1. 一旦内容 ScrollView 滚动到其顶部,我就找到了一种将滚动(平移)手势识别器的接收器从内容 ScrollView 更改为 super ScrollView 的方法。

    2. 我处理 super ScrollView 中的所有内容。它通过委托(delegate)协议(protocol)询问其内容 View Controller 是否要处理触摸,并且仅当它不想处理触摸时(因为其内容 ScrollView 已到达顶部), super ScrollView 才会自行滚动。


虽然我已经设法实现了第一个变体(这是您在视频中看到的),但我强烈更喜欢使用方法 2 或 3。这是一种让控制 Bottom Sheet 的 View Controller 管理的更简洁的方法所有滚动逻辑都不会暴露其内部结构。

不幸的是,我还没有找到一种方法以某种方式将平移手势分成两个部分(一个控制接收器 ScrollView ,一个控制另一个 ScrollView )

关于如何实现这种行为有什么想法吗?

最佳答案

我对这个问题非常感兴趣,我希望通过提供我将如何实现它,它不会扼杀可能显示如何真正绕过响应者的答案。我认为我在评论中提出的技巧是跟踪触摸。我忘记了 scrollview 是如何吞噬它们的,但你可以使用 UIPanGesture。看看这是否接近您要查找的内容。我遇到的唯一可能需要更多考虑的情况是使用滚动条关闭底部 View 。此代码的大部分设置为在 View 中获取工作 ScrollView 。我认为属性动画可能是让它变得可中断的最佳选择,甚至是我个人最喜欢的 Facebook Pop 动画。为了简单起见,我只使用了 UIView 动画。如果这能解决您正在寻找的问题,请告诉我。 代码在下面,这是结果 sogif . ScrollView 保持可滚动和事件状态。我为帧设置了动画,但更新约束也可以。

import UIKit

class ViewController: UIViewController{
//setup
var items : [Int] = []
lazy var tableView : UITableView = {
let tv = UITableView(frame: CGRect(x: 0, y: topViewHeight, width: self.view.frame.width, height: self.view.frame.height))
tv.autoresizingMask = [.flexibleWidth,.flexibleHeight]
tv.delegate = self
tv.dataSource = self
tv.layer.cornerRadius = 4
return tv
}()

lazy var topView : UIView = {
let v = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: topViewHeight))
v.backgroundColor = .green
v.autoresizingMask = [.flexibleWidth,.flexibleHeight]
return v
}()

let cellIdentifier = "ourCell"



//for animation
var isAnimating = false
var lastOffset : CGPoint = .zero
var startingTouch : CGPoint?
let topViewHeight : CGFloat = 500
var isShowing : Bool = false
let maxCollapse : CGFloat = 50




override func viewDidLoad() {
super.viewDidLoad()

for x in 0...100{
items.append(x)
}
// Do any additional setup after loading the view, typically from a nib.
self.view.addSubview(topView)
self.view.addSubview(tableView)
self.tableView.reloadData()

let pan = UIPanGestureRecognizer(target: self, action: #selector(moveFunction(pan:)))
pan.delegate = self
self.view.addGestureRecognizer(pan)
}

@objc func moveFunction(pan:UIPanGestureRecognizer) {
let point:CGPoint = pan.location(in: self.view)
switch pan.state {
case .began:
startingTouch = point
break
case .changed:
processMove(touchPoint:point.y)
break
default:
processEnding(currentPointY: point.y)
break
}
}

}

extension ViewController : UITableViewDelegate,UITableViewDataSource {

func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell : UITableViewCell!
cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
}
cell.textLabel?.text = "\(items[indexPath.row])"
return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 30
}
}

extension ViewController : UIScrollViewDelegate{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if isAnimating == true{
scrollView.contentOffset = lastOffset
return
}
lastOffset = scrollView.contentOffset
}
}

extension ViewController : UIGestureRecognizerDelegate{
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}

extension ViewController{
func processMove(touchPoint:CGFloat){
if let start = startingTouch{
if touchPoint <= topViewHeight && start.y > topViewHeight{
isAnimating = true
tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
return
}else if touchPoint >= self.maxCollapse && isShowing == true && start.y < self.maxCollapse{
isAnimating = true
tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
return
}else if isShowing == true && self.tableView.contentOffset.y <= 0{
//this is the only one i am slightly unsure about
isAnimating = true
tableView.frame = CGRect(x: 0, y:touchPoint, width: self.view.frame.width, height: self.view.frame.height)
return
}
}
self.isAnimating = false
}

func processEnding(currentPointY:CGFloat){
startingTouch = nil

if isAnimating{
if currentPointY < topViewHeight/2{
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: {
self.tableView.frame = CGRect(x: 0, y:self.maxCollapse, width: self.view.frame.width, height: self.view.frame.height)
}) { (finished) in
self.isShowing = true
}
}else{
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: .curveEaseInOut, animations: {
self.tableView.frame = CGRect(x: 0, y:self.topViewHeight, width: self.view.frame.width, height: self.view.frame.height)
}) { (finished) in
self.isShowing = false

}
}
}
self.isAnimating = false
}
}

关于ios - 滚动 ScrollView 时更改触摸接收器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50434643/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com