如何在 Swift 中获取 UIScrollView 垂直方向?
How to get UIScrollView vertical direction in Swift?
如何在 VC 中获取 up/down 的 scroll/swipe 方向?
我想在我的 VC 中添加一个 UIScrollView 或其他东西,它可以查看用户 swipes/scrolls 是向上还是向下然后 hide/show 一个 UIView
取决于是否这是一个 up/down 手势。
Swift 2-4 与 UISwipeGestureRecognizer
另一个选项是使用 UISwipeGestureRecognizer
来识别滑动到请求的方向(这将适用于所有视图,而不仅仅是 UIScrollView
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let upGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
let downGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
upGs.direction = .up
downGs.direction = .down
self.view.addGestureRecognizer(upGs)
self.view.addGestureRecognizer(downGs)
}
@objc func handleSwipes(sender:UISwipeGestureRecognizer) {
if (sender.direction == .up) {
print("Up")
}
if (sender.direction == .down) {
print("Down")
}
}
}
如果您使用 UIScrollView
,那么您可以利用 scrollViewDidScroll:
功能。您需要保存它的最后一个位置(contentOffset
)并按以下方式更新它:
// variable to save the last position visited, default to zero
private var lastContentOffset: CGFloat = 0
func scrollViewDidScroll(scrollView: UIScrollView!) {
if (self.lastContentOffset > scrollView.contentOffset.y) {
// move up
}
else if (self.lastContentOffset < scrollView.contentOffset.y) {
// move down
}
// update the new position acquired
self.lastContentOffset = scrollView.contentOffset.y
}
当然还有其他方法可以做到这一点。
希望对你有所帮助。
我使用了 Victor 的回答,稍作改进。当滚动到滚动的末尾或开始时,然后获得反弹效果。我通过计算scrollView.contentSize.height - scrollView.frame.height
然后限制scrollView.contentOffset.y
范围大于0或小于scrollView.contentSize.height - scrollView.frame.height
来添加约束,反弹时不做任何更改。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if lastContentOffset > scrollView.contentOffset.y && lastContentOffset < scrollView.contentSize.height - scrollView.frame.height {
// move up
} else if lastContentOffset < scrollView.contentOffset.y && scrollView.contentOffset.y > 0 {
// move down
}
// update the new position acquired
lastContentOffset = scrollView.contentOffset.y
}
对于Swift我觉得最简单最强大的就是像下面这样。
它允许您跟踪方向何时发生变化,并在方向发生变化时仅做出一次反应。
另外,如果您需要在任何其他阶段在您的代码中查阅它,您可以随时访问 .lastDirection
滚动 属性。
enum WMScrollDirection {
case Up, Down, None
}
class WMScrollView: UIScrollView {
var lastDirection: WMScrollDirection = .None {
didSet {
if oldValue != lastDirection {
// direction has changed, call your func here
}
}
}
override var contentOffset: CGPoint {
willSet {
if contentOffset.y > newValue.y {
lastDirection = .Down
}
else {
lastDirection = .Up
}
}
}
}
以上假定您只跟踪 up/down 滚动。
它可以通过枚举进行定制。您可以 add/change 到 .left
和 .right
来跟踪任何方向。
我希望这对某人有所帮助。
干杯
Victor 的回答很好,但它非常昂贵,因为您总是在比较和存储值。如果您的目标是在不进行昂贵计算的情况下立即确定滚动方向,请尝试 using Swift:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
if translation.y > 0 {
// swipes from top to bottom of screen -> down
} else {
// swipes from bottom to top of screen -> up
}
}
好了。同样,如果您需要不断跟踪,请使用 Victors answer,否则我更喜欢这种解决方案。
你可以这样做:
fileprivate var lastContentOffset: CGPoint = .zero
func checkScrollDirection(_ scrollView: UIScrollView) -> UIScrollViewDirection {
return lastContentOffset.y > scrollView.contentOffset.y ? .up : .down
}
和 scrollViewDelegate:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
switch checkScrollDirection(scrollView) {
case .up:
// move up
case .down:
// move down
default:
break
}
lastContentOffset = scrollView.contentOffset
}
我发现这是最简单和最灵活的选项(它也适用于 UICollectionView 和 UITableView)。
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
switch velocity {
case _ where velocity.y < 0:
// swipes from top to bottom of screen -> down
trackingDirection = .down
case _ where velocity.y > 0:
// swipes from bottom to top of screen -> up
trackingDirection = .up
default: trackingDirection = .none
}
}
如果速度为 0,这不起作用,在这种情况下,您别无选择,只能使用已接受答案的存储 属性 解决方案。
我制定了重新使用滚动方向的协议。
声明这些 enum
和 protocol
。
enum ScrollDirection {
case up, left, down, right, none
}
protocol ScrollDirectionDetectable {
associatedtype ScrollViewType: UIScrollView
var scrollView: ScrollViewType { get }
var scrollDirection: ScrollDirection { get set }
var lastContentOffset: CGPoint { get set }
}
extension ScrollDirectionDetectable {
var scrollView: ScrollViewType {
return self.scrollView
}
}
用法来自 ViewController
// Set ScrollDirectionDetectable which has UIScrollViewDelegate
class YourViewController: UIViewController, ScrollDirectionDetectable {
// any types that inherit UIScrollView can be ScrollViewType
typealias ScrollViewType = UIScrollView
var lastContentOffset: CGPoint = .zero
var scrollDirection: ScrollDirection = .none
}
extension YourViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Update ScrollView direction
if self.lastContentOffset.x > scrollView.contentOffset.x {
scrollDirection = .left
} else if self.lastContentOffset.x > scrollView.contentOffset.x {
scrollDirection = .right
}
if self.lastContentOffset.y > scrollView.contentOffset.y {
scrollDirection = .up
} else if self.lastContentOffset.y < scrollView.contentOffset.y {
scrollDirection = .down
}
self.lastContentOffset.x = scrollView.contentOffset.x
self.lastContentOffset.y = scrollView.contentOffset.y
}
}
如果您想使用特定方向,只需更新您想要的特定 contentOffset
。
对于swift4
实施 scrollViewDidScroll
方法来检测平移手势何时超出 y 轴 0:
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
print("up")
}
else {
print("down")
}
}
只需将此方法添加到您的 view controller:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView.contentOffset.y < 0) {
// Move UP - Show Navigation Bar
self.navigationController?.setNavigationBarHidden(false, animated: true)
} else if (scrollView.contentOffset.y > 0) {
// Move DOWN - Hide Navigation Bar
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
}
我已经尝试了此线程中的每一个响应,但其中 none 可以为启用了弹跳的 tableView 提供适当的解决方案。所以我只是使用了部分解决方案以及一些经典的布尔标志解决方案。
1) 所以,首先你可以为 scrollDirection 使用枚举:
enum ScrollDirection {
case up, down
}
2) 设置 3 个新的私有变量来帮助我们存储 lastOffset、scrollDirection 和一个标记 enable/disable 滚动方向计算(帮助我们忽略 tableView 的弹跳效果),你将在后面使用:
private var shouldCalculateScrollDirection = false
private var lastContentOffset: CGFloat = 0
private var scrollDirection: ScrollDirection = .up
3) 在scrollViewDidScroll中添加以下内容:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// The current offset
let offset = scrollView.contentOffset.y
// Determine the scolling direction
if lastContentOffset > offset && shouldCalculateScrollDirection {
scrollDirection = .down
}
else if lastContentOffset < offset && shouldCalculateScrollDirection {
scrollDirection = .up
}
// This needs to be in the last line
lastContentOffset = offset
}
4) 如果你还没有实现 scrollViewDidEndDragging 实现它并在其中添加这些代码行:
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
guard !decelerate else { return }
shouldCalculateScrollDirection = false
}
5) 如果你还没有实现 scrollViewWillBeginDecelerating 实现它并在其中添加这行代码:
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
shouldCalculateScrollDirection = false
}
6) 最后,如果你还没有实现 scrollViewWillBeginDragging 实现它并在其中添加这行代码:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
shouldCalculateScrollDirection = true
}
如果您按照上述所有步骤操作,就可以开始了!
你可以去任何你想使用方向的地方,只需写:
switch scrollDirection {
case .up:
// Do something for scollDirection up
case .down:
// Do something for scollDirection down
}
extension UIScrollView {
enum ScrollDirection {
case up, down, unknown
}
var scrollDirection: ScrollDirection {
guard let superview = superview else { return .unknown }
return panGestureRecognizer.translation(in: superview).y > 0 ? .down : .up
}
}
我就是这样做的。它几乎适用于我尝试过的所有情况。
- 用户向上或向下滚动
- 用户在滚动时改变方向
- 用户不再拖动。滚动视图继续滚动。
var goingUp: Bool
let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView).y
/// `Velocity` is 0 when user is not dragging.
if (velocity == 0){
goingUp = scrollView.panGestureRecognizer.translation(in: scrollView).y < 0
} else {
goingUp = velocity < 0
}
如何在 VC 中获取 up/down 的 scroll/swipe 方向?
我想在我的 VC 中添加一个 UIScrollView 或其他东西,它可以查看用户 swipes/scrolls 是向上还是向下然后 hide/show 一个 UIView
取决于是否这是一个 up/down 手势。
Swift 2-4 与 UISwipeGestureRecognizer
另一个选项是使用 UISwipeGestureRecognizer
来识别滑动到请求的方向(这将适用于所有视图,而不仅仅是 UIScrollView
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let upGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
let downGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
upGs.direction = .up
downGs.direction = .down
self.view.addGestureRecognizer(upGs)
self.view.addGestureRecognizer(downGs)
}
@objc func handleSwipes(sender:UISwipeGestureRecognizer) {
if (sender.direction == .up) {
print("Up")
}
if (sender.direction == .down) {
print("Down")
}
}
}
如果您使用 UIScrollView
,那么您可以利用 scrollViewDidScroll:
功能。您需要保存它的最后一个位置(contentOffset
)并按以下方式更新它:
// variable to save the last position visited, default to zero
private var lastContentOffset: CGFloat = 0
func scrollViewDidScroll(scrollView: UIScrollView!) {
if (self.lastContentOffset > scrollView.contentOffset.y) {
// move up
}
else if (self.lastContentOffset < scrollView.contentOffset.y) {
// move down
}
// update the new position acquired
self.lastContentOffset = scrollView.contentOffset.y
}
当然还有其他方法可以做到这一点。
希望对你有所帮助。
我使用了 Victor 的回答,稍作改进。当滚动到滚动的末尾或开始时,然后获得反弹效果。我通过计算scrollView.contentSize.height - scrollView.frame.height
然后限制scrollView.contentOffset.y
范围大于0或小于scrollView.contentSize.height - scrollView.frame.height
来添加约束,反弹时不做任何更改。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if lastContentOffset > scrollView.contentOffset.y && lastContentOffset < scrollView.contentSize.height - scrollView.frame.height {
// move up
} else if lastContentOffset < scrollView.contentOffset.y && scrollView.contentOffset.y > 0 {
// move down
}
// update the new position acquired
lastContentOffset = scrollView.contentOffset.y
}
对于Swift我觉得最简单最强大的就是像下面这样。
它允许您跟踪方向何时发生变化,并在方向发生变化时仅做出一次反应。
另外,如果您需要在任何其他阶段在您的代码中查阅它,您可以随时访问 .lastDirection
滚动 属性。
enum WMScrollDirection {
case Up, Down, None
}
class WMScrollView: UIScrollView {
var lastDirection: WMScrollDirection = .None {
didSet {
if oldValue != lastDirection {
// direction has changed, call your func here
}
}
}
override var contentOffset: CGPoint {
willSet {
if contentOffset.y > newValue.y {
lastDirection = .Down
}
else {
lastDirection = .Up
}
}
}
}
以上假定您只跟踪 up/down 滚动。
它可以通过枚举进行定制。您可以 add/change 到 .left
和 .right
来跟踪任何方向。
我希望这对某人有所帮助。
干杯
Victor 的回答很好,但它非常昂贵,因为您总是在比较和存储值。如果您的目标是在不进行昂贵计算的情况下立即确定滚动方向,请尝试 using Swift:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
if translation.y > 0 {
// swipes from top to bottom of screen -> down
} else {
// swipes from bottom to top of screen -> up
}
}
好了。同样,如果您需要不断跟踪,请使用 Victors answer,否则我更喜欢这种解决方案。
你可以这样做:
fileprivate var lastContentOffset: CGPoint = .zero
func checkScrollDirection(_ scrollView: UIScrollView) -> UIScrollViewDirection {
return lastContentOffset.y > scrollView.contentOffset.y ? .up : .down
}
和 scrollViewDelegate:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
switch checkScrollDirection(scrollView) {
case .up:
// move up
case .down:
// move down
default:
break
}
lastContentOffset = scrollView.contentOffset
}
我发现这是最简单和最灵活的选项(它也适用于 UICollectionView 和 UITableView)。
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
switch velocity {
case _ where velocity.y < 0:
// swipes from top to bottom of screen -> down
trackingDirection = .down
case _ where velocity.y > 0:
// swipes from bottom to top of screen -> up
trackingDirection = .up
default: trackingDirection = .none
}
}
如果速度为 0,这不起作用,在这种情况下,您别无选择,只能使用已接受答案的存储 属性 解决方案。
我制定了重新使用滚动方向的协议。
声明这些 enum
和 protocol
。
enum ScrollDirection {
case up, left, down, right, none
}
protocol ScrollDirectionDetectable {
associatedtype ScrollViewType: UIScrollView
var scrollView: ScrollViewType { get }
var scrollDirection: ScrollDirection { get set }
var lastContentOffset: CGPoint { get set }
}
extension ScrollDirectionDetectable {
var scrollView: ScrollViewType {
return self.scrollView
}
}
用法来自 ViewController
// Set ScrollDirectionDetectable which has UIScrollViewDelegate
class YourViewController: UIViewController, ScrollDirectionDetectable {
// any types that inherit UIScrollView can be ScrollViewType
typealias ScrollViewType = UIScrollView
var lastContentOffset: CGPoint = .zero
var scrollDirection: ScrollDirection = .none
}
extension YourViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Update ScrollView direction
if self.lastContentOffset.x > scrollView.contentOffset.x {
scrollDirection = .left
} else if self.lastContentOffset.x > scrollView.contentOffset.x {
scrollDirection = .right
}
if self.lastContentOffset.y > scrollView.contentOffset.y {
scrollDirection = .up
} else if self.lastContentOffset.y < scrollView.contentOffset.y {
scrollDirection = .down
}
self.lastContentOffset.x = scrollView.contentOffset.x
self.lastContentOffset.y = scrollView.contentOffset.y
}
}
如果您想使用特定方向,只需更新您想要的特定 contentOffset
。
对于swift4
实施 scrollViewDidScroll
方法来检测平移手势何时超出 y 轴 0:
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
print("up")
}
else {
print("down")
}
}
只需将此方法添加到您的 view controller:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView.contentOffset.y < 0) {
// Move UP - Show Navigation Bar
self.navigationController?.setNavigationBarHidden(false, animated: true)
} else if (scrollView.contentOffset.y > 0) {
// Move DOWN - Hide Navigation Bar
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
}
我已经尝试了此线程中的每一个响应,但其中 none 可以为启用了弹跳的 tableView 提供适当的解决方案。所以我只是使用了部分解决方案以及一些经典的布尔标志解决方案。
1) 所以,首先你可以为 scrollDirection 使用枚举:
enum ScrollDirection {
case up, down
}
2) 设置 3 个新的私有变量来帮助我们存储 lastOffset、scrollDirection 和一个标记 enable/disable 滚动方向计算(帮助我们忽略 tableView 的弹跳效果),你将在后面使用:
private var shouldCalculateScrollDirection = false
private var lastContentOffset: CGFloat = 0
private var scrollDirection: ScrollDirection = .up
3) 在scrollViewDidScroll中添加以下内容:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// The current offset
let offset = scrollView.contentOffset.y
// Determine the scolling direction
if lastContentOffset > offset && shouldCalculateScrollDirection {
scrollDirection = .down
}
else if lastContentOffset < offset && shouldCalculateScrollDirection {
scrollDirection = .up
}
// This needs to be in the last line
lastContentOffset = offset
}
4) 如果你还没有实现 scrollViewDidEndDragging 实现它并在其中添加这些代码行:
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
guard !decelerate else { return }
shouldCalculateScrollDirection = false
}
5) 如果你还没有实现 scrollViewWillBeginDecelerating 实现它并在其中添加这行代码:
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
shouldCalculateScrollDirection = false
}
6) 最后,如果你还没有实现 scrollViewWillBeginDragging 实现它并在其中添加这行代码:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
shouldCalculateScrollDirection = true
}
如果您按照上述所有步骤操作,就可以开始了!
你可以去任何你想使用方向的地方,只需写:
switch scrollDirection {
case .up:
// Do something for scollDirection up
case .down:
// Do something for scollDirection down
}
extension UIScrollView {
enum ScrollDirection {
case up, down, unknown
}
var scrollDirection: ScrollDirection {
guard let superview = superview else { return .unknown }
return panGestureRecognizer.translation(in: superview).y > 0 ? .down : .up
}
}
我就是这样做的。它几乎适用于我尝试过的所有情况。
- 用户向上或向下滚动
- 用户在滚动时改变方向
- 用户不再拖动。滚动视图继续滚动。
var goingUp: Bool let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView).y /// `Velocity` is 0 when user is not dragging. if (velocity == 0){ goingUp = scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 } else { goingUp = velocity < 0 }