【问题标题】:How to get UIScrollView vertical direction in Swift?如何在 Swift 中获取 UIScrollView 的垂直方向?
【发布时间】:2015-10-29 16:40:06
【问题描述】:

如何在 VC 中获取向上/向下的滚动/滑动方向?

我想在我的 VC 中添加一个 UIScrollView 或其他东西,它可以查看用户是向上还是向下滑动/滚动,然后隐藏/显示 UIView,具体取决于它是否是向上/向下手势。

【问题讨论】:

    标签: ios swift cocoa-touch uiviewcontroller uiscrollview


    【解决方案1】:

    如果您使用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
    }
    

    当然还有其他方法可以做到这一点。

    希望对你有所帮助。

    【讨论】:

    • 谢谢它有点工作。但是由于滚动视图具有反弹效果,如果我一直滚动到顶部或者如果我执行“拉动刷新”,它会来回触发两个函数“didScrollUp”和“didScrollDown”这导致我的视​​图切换在隐藏/显示几次之间。所以也许我要向滚动视图/表格视图添加滑动手势?
    • 小心UITableView 里面有一个UIScrollView!!如果您添加了另一个,则需要区分两者
    • 我没有添加另一个滚动视图。我正在使用“覆盖func scrollViewDidScroll(scrollView:UIScrollView)”但是由于滚动视图具有反弹效果,它会将我的视图切换为显示/隐藏几次。那么我最好在我的 tableview 上添加滑动手势吗?
    • 当我想在向下滚动时隐藏按钮但在向上滚动时显示按钮时,这对我来说非常有用。我解决了弹跳/滚动高于/低于滚动限制的问题,我在下面的新回复中发布了以下内容。
    • 您必须将bounces 设置为false self.scrollView.bounces = false 否则您将在scrollView 的顶部或底部位置获得随机事件(上下混合运动)。
    【解决方案2】:

    Victor 的回答很棒,但它非常昂贵,因为您总是在比较和存储值。如果您的目标是在不进行昂贵计算的情况下立即识别滚动方向,请尝试使用 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 答案,否则我更喜欢此解决方案。 ?

    【讨论】:

    • 我一开始使用了这个,但请注意,如果您以编程方式触发滚动,它就不起作用,例如通过UIPageControl
    【解决方案3】:

    我使用了 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
    }
    

    【讨论】:

    • 这应该被标记为答案。 Victor 对反弹效果不起作用。
    【解决方案4】:

    对于 swift4

    实现scrollViewDidScroll 方法,该方法检测何时平移手势超出 y 轴 0:

        public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
            print("up")
        }
        else {
            print("down")
        }
     }
    

    【讨论】:

      【解决方案5】:

      我已经尝试了此线程中的每一个响应,但没有一个可以为启用反弹的 tableView 提供适当的解决方案。所以我只使用了部分解决方案以及一些历史悠久的经典布尔标志解决方案。

      1) 所以,首先你可以为滚动方向使用枚举:

      enum ScrollDirection {
          case up, down
      }
      

      2) 设置 3 个新的私有变量来帮助我们存储 lastOffset、scrollDirection 和一个标志来启用/禁用滚动方向计算(帮助我们忽略 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
      }
      

      【讨论】:

        【解决方案6】:

        你可以这样做:

        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
        }
        

        【讨论】:

          【解决方案7】:

          我发现这是最简单、最灵活的选项(它也适用于 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 - 在这种情况下,您别无选择,只能使用接受的答案的存储属性解决方案。

          【讨论】:

            【解决方案8】:

            带有 UISwipeGestureRecognizer 的 Swift 2-4

            另一个选项是使用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")
                    }
                }
            }
            

            【讨论】:

            • 我的问题是我有一个带有嵌入式 /Seguetable 视图的容器视图。所以我的刷卡没有被拿起。知道我怎么还能得到它们吗?很快就会接受
            【解决方案9】:

            只需将此方法添加到您的视图 控制器

            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)
                }
            }
            

            【讨论】:

              【解决方案10】:

              对于 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
                          }
                      }
                  }
              }
              

              以上假设您只跟踪向上/向下滚动。 它可以通过枚举进行定制。您可以添加/更改.left.right 以跟踪任何方向。

              我希望这对某人有所帮助。

              干杯

              【讨论】:

                【解决方案11】:

                我制定了重复使用滚动方向的协议。

                声明这些enumprotocols。

                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

                【讨论】:

                • 应该是 self.lastContentOffset.x
                【解决方案12】:
                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
                    }
                }
                

                【讨论】:

                  【解决方案13】:

                  我就是这样做的。它适用于我尝试过的几乎所有情况。

                  1. 用户向上或向下滚动
                  2. 用户在滚动时改变方向
                  3. 用户不再拖动。滚动视图继续滚动。
                      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
                      }
                  

                  【讨论】:

                    猜你喜欢
                    • 2015-05-19
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-11-19
                    • 2022-11-25
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-03-20
                    • 2015-11-10
                    相关资源
                    最近更新 更多