繁体   English   中英

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

[英]Programmatically scroll a UIScrollView to the top of a child UIView (subview) in Swift

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

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

UIScrollView 移动以便子视图位于 UIScrollView 的顶部(无论是否动画)

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

用法:

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

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

}
 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];
scrollView.setContentOffset(CGPoint, animated: Bool)

其中点的 y 坐标是您要显示的视图框架相对于 scrollView 的内容视图的 y 坐标。

更新了戴森的答案,使其表现得像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)
        }
    }
}

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

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

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

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

内容插入 - 从不 将ScrollView.Top 与Safe Area.Top 对齐

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

// 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?() }
        })
    }
}

对我来说scrollRectToVisible()不起作用(见这里),所以我使用了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)
            }
        }
    }

}

它用于水平滚动视图,但同样的想法也可以垂直应用。

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

例如:

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

你有一个类似上面由“dyson Returns”编写的扩展脚本

你现在写在你的代码中:

“bob.scrollToTop”

这将扩展功能“scrollToTop”附加到故事板中的 UIScrollView 。

祝你好运,振作起来!

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

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

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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM