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

后端 未结 11 1558
遇见更好的自我
遇见更好的自我 2020-12-29 03:35

I have a few screens worth of content within my UIScrollView which only scrolls vertically.

I want to programmatically scroll to a view contained somewhere in it\'s

相关标签:
11条回答
  • 2020-12-29 03:49

    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 Shots2

    0 讨论(0)
  • 2020-12-29 03:50

    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"

    For example:

    you import your UIScrollView to your code and name it bob.

    you have an extension script written like the one above by "dyson returns"

    you now write in your code:

    "bob.scrollToTop"

    This attached the extension function "scrollToTop" to you UIScrollView in the storyboard.

    Good luck and chin up!

    0 讨论(0)
  • 2020-12-29 03:57

    swift 5.0 code

    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)
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-29 04:00

    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:

    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.

    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)
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-29 04:00

    Updated dyson's answer to behave like UITableView's scrollToRowAtIndexPath:atScrollPosition:animated: since that was my use case:

    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)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题