简体   繁体   English

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

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

I have a few screens worth of content within my UIScrollView which only scrolls vertically.我的 UIScrollView 中有几个屏幕的内容,它们只能垂直滚动。

I want to programmatically scroll to a view contained somewhere in it's hierarchy.我想以编程方式滚动到包含在其层次结构中某处的视图。

The UIScrollView move so that the child view is at the top of the UIScrollView (either animated or not) UIScrollView 移动以便子视图位于 UIScrollView 的顶部(无论是否动画)

Here's an extension I ended up writing.这是我最终写的一个扩展。

Usage:用法:

Called from my viewController, self.scrollView is an outlet to the UIScrollView and self.commentsHeader is a view within it, near the bottom:从我的 viewController 调用,self.scrollView 是 UIScrollView 的出口,self.commentsHeader 是其中的一个视图,靠近底部:

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

Code:代码:

You only need the scrollToView method, but leaving in scrollToBottom / scrollToTop methods too as you'll probably need those too, but feel free to delete them.您只需要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)

or

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

Another way is另一种方式是

scrollView.contentOffset = CGPointMake(x,y);

and i do it with animated like this我用这样的动画来做

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

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

Updated dyson's answer to behave like UITableView 's scrollToRowAtIndexPath:atScrollPosition:animated: since that was my use case:更新了戴森的答案,使其表现得像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)
        }
    }
}

Here is my answer, this is in swift.这是我的答案,这是快速的。 This will scroll the pages in scrollview infinitely.这将无限滚动滚动视图中的页面。

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

For me, the thing was the navigation bar which overlapped the small portion of the scrollView content.对我来说,问题是导航栏与滚动视图内容的一小部分重叠。 So I've made 2 things:所以我做了两件事:

  • Size Inspector - Scroll View - Content Insets --> Change from Automatic to Never.尺寸检查器 - 滚动视图 -内容插入 --> 从自动更改为从不。
  • Size Inspector - Constraints- "Align Top to" (Top Alignment Constraints)- Second item --> Change from Superview.Top to Safe Area.Top and the value(constant field) set to 0 Size Inspector - Constraints- "Align Top to" (Top Alignment Constraints)- 第二项 -->从 Superview.Top 更改为 Safe Area.Top并将值(常量字段)设置为 0

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

For scroll to top or bottom with completion of the animation用于在动画完成时滚动到顶部或底部

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

For me scrollRectToVisible() didn't work (see here ), so I used setContentOffset() and calculated it myself, based on AMAN77's answer:对我来说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)
            }
        }
    }

}

It is for a horizontal scroll view, but the same idea can be applied vertically too.它用于水平滚动视图,但同样的想法也可以垂直应用。

It is important to point out for any of you beginners out there that you will need to link the UIScrollView from your story board into you code then use the extension ".nameoffunction"重要的是要指出,对于任何初学者来说,您需要将故事板中的 UIScrollView 链接到您的代码中,然后使用扩展名“.nameoffunction”

For example:例如:

you import your UIScrollView to your code and name it bob.您将 UIScrollView 导入您的代码并将其命名为 bob。

you have an extension script written like the one above by "dyson returns"你有一个类似上面由“dyson Returns”编写的扩展脚本

you now write in your code:你现在写在你的代码中:

"bob.scrollToTop" “bob.scrollToTop”

This attached the extension function "scrollToTop" to you UIScrollView in the storyboard.这将扩展功能“scrollToTop”附加到故事板中的 UIScrollView 。

Good luck and chin up!祝你好运,振作起来!

You can use the following method , it works well for me您可以使用以下方法,它对我很有效

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)

}

Or you can have as an extension或者您可以将其作为扩展名

 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)

   }
}

here are constraints for the scroll view这是滚动视图的约束滚动视图的约束

some screen shots一些屏幕截图

Screen Shots 2屏幕截图2

swift 5.0 code快速 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.

相关问题 UIView中的UIScrollView:以编程方式检测滚动 - UIScrollView in UIView: detecting scroll programmatically Swift-在UIImageView / UIScrollView之上的UIView - Swift - UIView on top of UIImageView/UIScrollView UIView(XIB)和scrollview之上的UIScrollView将不会滚动 - UIScrollView on top of UIView (XIB) and scrollview will not scroll 以编程方式创建UIScrollView作为另一个UIView的子视图的必要步骤是什么? - What are the necessary steps to create a UIScrollView programmatically as a subview of another UIView? 无法设置Swift中UIScrollView的子视图UIView的backgroundColor - Cannot set backgroundColor of UIView which is a subview of UIScrollView in Swift view.Layer.CornerRadius 不适用于 UIView Swift 3 iOS 的 UIScrollView 子视图 - view.Layer.CornerRadius not working on UIScrollView subView of UIView Swift 3 iOS 如何以编程方式滚动UIScrollView,它是自定义UITableViewCell的子视图? - How to scroll UIScrollView programmatically which is a subview of the custom UITableViewCell? 以编程方式访问UIScrollView的子视图 - Programmatically access subview of UIScrollView 在 UIScrollView 中禁用子视图的滚动 - Disable scroll of a subview in a UIScrollView 以编程方式将UiView放置为子视图 - Placing UiView as a subview programmatically
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM