jac*_*zki 6 uiscrollview ios swift
我有一个具有以下结构的接口,括号中是重要元素的名称:
- UIViewController
- UIScrollView (ScrollViewA)
- UIViewController (ProfileOverviewViewController)
- UIViewController (ProfileDetailViewController)
- UICollectionView (ScrollViewB)
Run Code Online (Sandbox Code Playgroud)
所以本质上ScrollViewB在另一个垂直滚动视图 ( ScrollViewA) 中有一个垂直滚动视图 ( )。ProfileOverviewViewController和ProfileDetailViewController都是设备屏幕的大小,因此ScrollViewB只有ScrollViewA滚动到底部时才可见。
分页在 上启用ScrollViewA,因此会捕捉到ProfileOverviewViewController整个屏幕视图或ProfileDetailViewController整个屏幕视图。
我的问题是:
ScrollViewA,这样ProfileDetailViewController和ScrollViewB可见。ScrollViewB然后释放。ScrollViewB。ScrollViewB,ScrollViewB应该停止滚动并ScrollViewA开始向上滚动到ProfileOverviewViewController,所有这些都在用户的同一手指手势内。ScrollViewB由于bounces属性为真,此时 where将简单地扩展到负的 y 内容偏移量。
当 ScrollViewB 到达顶部时,如何将其滚动到 ScrollViewA?
提前致谢
这是一个很好的问题,我不得不深入挖掘才能找到合适的解决方案。这是注释的代码。这个想法是向 scrollViewB 添加自定义平移手势并将 ProfileDetailViewController 设置为其手势委托。当平移将 scrollViewB 带到其顶部时,ProfileOverviewViewController 会收到警告并开始滚动 scrollViewA。当用户松开手指 ProfileOverviewViewController 决定是滚动到内容的底部还是顶部。
希望能帮助到你 :)
ProfileDetailViewController :
//
// ProfileDetailViewController.swift
// Sandbox
//
// Created by Eric Blachère on 23/12/2018.
// Copyright © 2018 Eric Blachère. All rights reserved.
//
import UIKit
protocol OverflowDelegate: class {
func onOverflowEnded()
func onOverflow(delta: CGFloat)
}
/// State of overflow of scrollView
///
/// - on: The scrollview is overflowing : ScrollViewA should take the lead. We store the last trnaslation of the gesture
/// - off: No overflow detected
enum OverflowState {
case on(lastRecordedGestureTranslation: CGFloat)
case off
var isOn: Bool {
switch self {
case .on:
return true
case .off:
return false
}
}
}
class ProfileDetailViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate {
@IBOutlet weak var scrollviewB: UIScrollView!
weak var delegate: OverflowDelegate?
/// a pan gesture added on scrollView B
var customPanGesture: UIPanGestureRecognizer!
/// The state of the overflow
var overflowState = OverflowState.off
override func viewDidLoad() {
super.viewDidLoad()
// create a custom pan gesture recognizer added on scrollview B. This way we can be delegate of this gesture & follow the finger
customPanGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panRecognized(gesture:)))
scrollviewB.addGestureRecognizer(customPanGesture)
customPanGesture.delegate = self
scrollviewB.delegate = self
}
@objc func panRecognized(gesture: UIPanGestureRecognizer) {
switch overflowState {
case .on(let lastRecordedGestureTranslation):
// the user just released his finger
if gesture.state == .ended {
print("didEnd !!")
delegate?.onOverflowEnded() // warn delegate
overflowState = .off // end of overflow
scrollviewB.panGestureRecognizer.isEnabled = true // enable scroll again
return
}
// compute the translation delta & send it to delegate
let fullTranslationY = gesture.translation(in: view).y
let delta = fullTranslationY - lastRecordedGestureTranslation
overflowState = .on(lastRecordedGestureTranslation: fullTranslationY)
delegate?.onOverflow(delta: delta)
case .off:
return
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y <= 0 { // scrollview B is at the top
// if the overflow is starting : initilize
if !overflowState.isOn {
let translation = self.customPanGesture.translation(in: self.view)
self.overflowState = .on(lastRecordedGestureTranslation: translation.y)
// disable scroll as we don't scroll in this scrollView from now on
scrollView.panGestureRecognizer.isEnabled = false
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true // so that both the pan gestures on scrollview will be triggered
}
}
Run Code Online (Sandbox Code Playgroud)
全局视图控制器:
//
// GlobalViewController.swift
// Sandbox
//
// Created by Eric Blachère on 23/12/2018.
// Copyright © 2018 Eric Blachère. All rights reserved.
//
import UIKit
class GlobalViewController: UIViewController, OverflowDelegate {
@IBOutlet weak var scrollViewA: UIScrollView!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "secondSegue", let ctrl = segue.destination as? ProfileDetailViewController else {
return
}
ctrl.delegate = self
}
func onOverflowEnded() {
// scroll to top if at least one third of the overview is showed (you can change this fraction as you please ^^)
let shouldScrollToTop = (scrollViewA.contentOffset.y <= 2 * scrollViewA.frame.height / 3)
if shouldScrollToTop {
scrollViewA.scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: true)
} else {
scrollViewA.scrollRectToVisible(CGRect(x: 0, y: scrollViewA.contentSize.height - 1, width: 1, height: 1), animated: true)
}
}
func onOverflow(delta: CGFloat) {
// move the scrollview content
if scrollViewA.contentOffset.y - delta <= scrollViewA.contentSize.height - scrollViewA.frame.height {
scrollViewA.contentOffset.y -= delta
print("difference : \(delta)")
print("contentOffset : \(scrollViewA.contentOffset.y)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑: ProfileOverviewViewController 和 ProfileDetailViewController 是通过容器视图在故事板中的 GlobalViewController 中设置的,但如果在代码中设置,它应该也能正常工作;)
| 归档时间: |
|
| 查看次数: |
1300 次 |
| 最近记录: |