以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部

Posted

技术标签:

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

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

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

UIScrollView 移动,以便子视图位于 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

【讨论】:

以上是关于以编程方式将 UIScrollView 滚动到 Swift 中的子 UIView(子视图)的顶部的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将触摸传递给 UIScrollView 以进行滚动

以编程方式取消用户滚动 UIScrollView

如何以编程方式设置 UIScrollView 以使用 autoLayout 水平滚动

UIScrollView 不使用自动布局滚动(内容视图框架大小以编程方式更改)

以编程方式滚动 UIScrollView

使用布局约束以编程方式将视图添加到滚动视图