【问题标题】:Programmatically scroll a UIScrollView to the top of a child UIView (subview) in Swift以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部
【发布时间】:2016-08-18 12:16:55
【问题描述】:

我的 UIScrollView 中有一些屏幕内容,它们只能垂直滚动。

我想以编程方式滚动到包含在其层次结构中某处的视图。

UIScrollView 移动,以便子视图位于 UIScrollView 的顶部(动画与否)

【问题讨论】:

    标签: ios swift uiscrollview


    【解决方案1】:

    这是我最后写的一个扩展。

    用法:

    从我的 viewController 调用,self.scrollView 是 UIScrollView 的出口,self.cmetsHeader 是其中的一个视图,靠近底部:

    self.scrollView.scrollToView(self.commentsHeader, animated: true)
    

    代码:

    您只需要 scrollToView 方法,但也保留 scrollToBottom / scrollToTop 方法,因为您可能也需要这些方法,但感觉可以随意删除。

    extension UIScrollView {
    
        // Scroll to a specific view so that it's top is at the top our scrollview
        func scrollToView(view:UIView, animated: Bool) {
            if let origin = view.superview {
                // Get the Y position of your child view
                let childStartPoint = origin.convertPoint(view.frame.origin, toView: self)
                // Scroll to a rectangle starting at the Y of your subview, with a height of the scrollview
                self.scrollRectToVisible(CGRect(x:0, y:childStartPoint.y,width: 1,height: self.frame.height), animated: animated)
            }
        }
    
        // Bonus: Scroll to top
        func scrollToTop(animated: Bool) {
            let topOffset = CGPoint(x: 0, y: -contentInset.top)
            setContentOffset(topOffset, animated: animated)
        }
    
        // Bonus: Scroll to bottom
        func scrollToBottom() {
            let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
            if(bottomOffset.y > 0) {
                setContentOffset(bottomOffset, animated: true)
            }
        }
    
    }
    

    【讨论】:

    • 如果键盘在屏幕上,这对我来说效果不佳。而不是使用 scrollRectToVisible 我正在使用: setContentOffset(CGPoint(x: 0, y: childStartPoint.y), animated: animated)
    • self.scrollRectToVisible(CGRect(x: 0, y: childStartPoint.y, width: 1, height: view.frame.height), animated: animated) 为我工作。使用的视图高度
    • 在使用内容插入时考虑将height: self.frame.height 替换为height: frame.height - (contentInset.top + contentInset.bottom)
    • @Nati 当键盘出现在屏幕上时,您需要确保 UIScrollView 的 contentInset 属性设置正确以考虑键盘下方的区域。
    • let childStartPoint = origin.convertPoint(view.frame.origin, toView: self) 替换为let childStartPoint = origin.convert(view.frame.origin, to: self) 解决了我的问题。
    【解决方案2】:
     scrollView.scrollRectToVisible(CGRect(x: x, y: y, width: 1, height:
    1), animated: true)
    

    scrollView.setContentOffset(CGPoint(x: x, y: y), animated: true)
    

    另一种方法是

    scrollView.contentOffset = CGPointMake(x,y);
    

    我用这样的动画来做

    [UIView animateWithDuration:2.0f delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    scrollView.contentOffset = CGPointMake(x, y); }
    completion:NULL];
    

    【讨论】:

      【解决方案3】:
      scrollView.setContentOffset(CGPoint, animated: Bool)
      

      该点的 y 坐标是您要显示的视图框架的 y 坐标,相对于滚动视图的内容视图

      【讨论】:

        【解决方案4】:

        这是我的答案,这很快。这将无限滚动滚动视图中的页面。

        private func startBannerSlideShow()
        {
        UIView.animate(withDuration: 6, delay: 0.1, options: .allowUserInteraction, animations: {
            scrollviewOutlt.contentOffset.x = (scrollviewOutlt.contentOffset.x == scrollviewOutlt.bounds.width*2) ? 0 : scrollviewOutlt.contentOffset.x+scrollviewOutlt.bounds.width
        }, completion: { (status) in
            self.startBannerSlideShow()
        })
        }
        

        【讨论】:

          【解决方案5】:

          更新了dyson's answer,使其行为类似于UITableViewscrollToRowAtIndexPath:atScrollPosition:animated:,因为那是我的用例:

          extension UIScrollView {
              
              /// Scrolls to a subview of the current `UIScrollView `.
              /// - Parameters:
              ///   - view: The subview  to which it should scroll to.
              ///   - position: A constant that identifies a relative position in the `UIScrollView ` (top, middle, bottom) for the subview when scrolling concludes. See UITableViewScrollPosition for descriptions of valid constants.
              ///   - animated: `true` if you want to animate the change in position; `false` if it should be immediate.
              func scrollToView(view: UIView,
                                position: UITableView.ScrollPosition = .top,
                                animated: Bool) {
                  
                  // Position 'None' should not scroll view to top if visible like in UITableView
                  if position == .none &&
                      bounds.intersects(view.frame) {
                      return
                  }
                  
                  if let origin = view.superview {
                      // Get the subview's start point relative to the current UIScrollView
                      let childStartPoint = origin.convert(view.frame.origin,
                                                           to: self)
                      var scrollPointY: CGFloat
                      switch position {
                      case .bottom:
                          let childEndY = childStartPoint.y + view.frame.height
                          scrollPointY = CGFloat.maximum(childEndY - frame.size.height, 0)
                      case .middle:
                          let childCenterY = childStartPoint.y + view.frame.height / 2.0
                          let scrollViewCenterY = frame.size.height / 2.0
                          scrollPointY = CGFloat.maximum(childCenterY - scrollViewCenterY, 0)
                      default:
                          // Scroll to top
                          scrollPointY = childStartPoint.y
                      }
          
                      // Scroll to the calculated Y point
                      scrollRectToVisible(CGRect(x: 0,
                                                 y: scrollPointY,
                                                 width: 1,
                                                 height: frame.height),
                                          animated: animated)
                  }
              }
          
              
              /// Scrolls to the top of the current `UIScrollView`.
              /// - Parameter animated: `true` if you want to animate the change in position; `false` if it should be immediate.
              func scrollToTop(animated: Bool) {
                  let topOffset = CGPoint(x: 0, y: -contentInset.top)
                  setContentOffset(topOffset, animated: animated)
              }
          
              /// Scrolls to the bottom of the current `UIScrollView`.
              /// - Parameter animated: `true` if you want to animate the change in position; `false` if it should be immediate.
              func scrollToBottom(animated: Bool) {
                  let bottomOffset = CGPoint(x: 0,
                                             y: contentSize.height - bounds.size.height + contentInset.bottom)
                  if (bottomOffset.y > 0) {
                      setContentOffset(bottomOffset, animated: animated)
                  }
              }
          }
          

          【讨论】:

            【解决方案6】:

            对我来说,导航栏与滚动视图内容的一小部分重叠。所以我做了两件事:

            • 尺寸检查器 - 滚动视图 - 内容插入 --> 从自动更改为从不。
            • Size Inspector - Constraints- "Align Top to" (Top Alignment Constraints)- 第二项 --> 从 Superview.Top 更改为 Safe Area.Topvalue(constant field ) 设置为 0

            【讨论】:

            • 这不能回答问题
            【解决方案7】:

            swift 5.0 代码

            extension UIScrollView {
            
                // Scroll to a specific view so that it's top is at the top our scrollview
                func scrollToView(view:UIView, animated: Bool) {
                    if let origin = view.superview {
                        // Get the Y position of your child view
                        let childStartPoint = origin.convert(view.frame.origin, to: self)
                        // Scroll to a rectangle starting at the Y of your subview, with a height of the scrollview
                        self.scrollRectToVisible(CGRect(x:0, y:childStartPoint.y,width: 1,height: self.frame.height), animated: animated)
                    }
                }
            
                // Bonus: Scroll to top
                func scrollToTop(animated: Bool) {
                    let topOffset = CGPoint(x: 0, y: -contentInset.top)
                    setContentOffset(topOffset, animated: animated)
                }
            
                // Bonus: Scroll to bottom
                func scrollToBottom() {
                    let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
                    if(bottomOffset.y > 0) {
                        setContentOffset(bottomOffset, animated: true)
                    }
                }
            
            }
            

            【讨论】:

              【解决方案8】:

              用于在动画完成时滚动到顶部或底部

              // MARK: - UIScrollView extensions
              
              extension UIScrollView {
                  /// Animate scroll to bottom with completion
                  ///
                  /// - Parameters:
                  ///   - duration:   TimeInterval
                  ///   - completion: Completion block
                  func animateScrollToBottom(withDuration duration:  TimeInterval,
                                          completion:             (()->())? = nil) {
              
                      UIView.animate(withDuration: duration, animations: { [weak self] in
                          self?.setContentOffset(CGPoint.zero, animated: false)
                          }, completion: { finish in
                              if finish { completion?() }
                      })
                  }
              
                  /// Animate scroll to top with completion
                  ///
                  /// - Parameters:
                  ///   - duration:   TimeInterval
                  ///   - completion: Completion block
                  func animateScrollToBottomTop(withDuration duration:  TimeInterval,
                                                completion:             (()->())? = nil) {
                      UIView.animate(withDuration: duration, animations: { [weak self] in
                          guard let `self` = self else {
                              return
                          }
                          let desiredOffset = CGPoint(x: 0, y: -self.contentInset.top)
                          self.setContentOffset(desiredOffset, animated: false)
              
                          }, completion: { finish in
                              if finish { completion?() }
                      })
                  }
              }
              

              【讨论】:

                【解决方案9】:

                对我来说scrollRectToVisible() 不起作用(请参阅here),所以我使用setContentOffset() 并根据 AMAN77 的回答自己计算:

                extension UIScrollView {
                
                    func scrollToView(view:UIView, animated: Bool) {
                        if let superview = view.superview {
                            let child = superview.convert(view.frame, to: self)
                            let visible = CGRect(origin: contentOffset, size: visibleSize)
                            let newOffsetY = child.minY < visible.minY ? child.minY : child.maxY > visible.maxY ? child.maxY - visible.height : nil
                            if let y = newOffsetY {
                                setContentOffset(CGPoint(x:0, y: y), animated: animated)
                            }
                        }
                    }
                
                }
                

                这是一个水平滚动视图,但同样的想法也可以垂直应用。

                【讨论】:

                  【解决方案10】:

                  重要的是要向你们中的任何初学者指出,您需要将故事板中的 UIScrollView 链接到您的代码中,然后使用扩展名“.nameoffunction”

                  例如:

                  您将 UIScrollView 导入您的代码并将其命名为 bob。

                  你有一个像上面那样由“dyson returns”编写的扩展脚本

                  你现在写你的代码:

                  “bob.scrollToTop”

                  这将扩展功能“scrollToTop”附加到情节提要中的 UIScrollView。

                  祝你好运,振作起来!

                  【讨论】:

                    【解决方案11】:

                    您可以使用以下方法,它对我很有效

                    func scrollViewToTop( _ someView:UIView)
                    {
                    
                        let targetViewTop = someView.frame.origin.y
                        //If you have a complicated hierarchy it is better to 
                        // use someView superview (someView.superview?.frame.origin.y) and figure out your view origin
                        let viewToTop = targetViewTop - scrollView.contentInset.top
                        scrollView.setContentOffset(CGPoint(x: 0, y: viewToTop), animated: true)
                    
                    }
                    

                    或者你可以有一个扩展

                     extension UIScrollView
                    {
                           func scrollViewToTop( _ someView:UIView){
                        let targetViewTop = someView.frame.origin.y
                        let viewToTop = targetViewTop - self.contentInset.top
                        self.setContentOffset(CGPoint(x: 0, y: viewToTop), animated: true)
                    
                       }
                    }
                    

                    这里是滚动视图的约束

                    一些屏幕截图

                    截图2

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-12-15
                      • 2010-10-31
                      • 1970-01-01
                      • 2019-08-05
                      • 1970-01-01
                      相关资源
                      最近更新 更多