Search code examples
iosswiftuitableviewuiscrollviewuikit

How to use UIScrollView to scroll to the top of the first view?


I currently have a UIScrollView with a UIImageView (top image) positioned below the UINavigationBar. However, I want to position the UIImageView at the very top of the screen (bottom image). Is there a way to implement this?

What I've tried so far: I added a UIScrollView extension (source) that is supposed to scroll down to the view parameter provided, but it hasn't worked for me.

enter image description here enter image description here

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

}

class MealDetailsVC: UIViewController {
    private var mealInfo: MealInfo
    
    init(mealInfo: MealInfo) {
        self.mealInfo = mealInfo
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        scrollView.scrollToView(view: iv, animated: false)  // used extension from above
    }
    
    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        
        return scrollView
    }()
    
    lazy var iv: UIImageView = {
        let iv = UIImageView()
        iv.image = Image.defaultMealImage!   
        iv.contentMode = .scaleAspectFill
        
        return iv
    }()
}

extension MealDetailsVC {
    func setupViews() {
        addBackButton()
        addSubviews()
        autoLayoutViews()
        constrainSubviews()
    }

    fileprivate func addBackButton() {
        ...
    }
    
    @objc func goBack(sender: UIBarButtonItem) {
        ...
    }

    fileprivate func addSubviews() {
        view.addSubview(scrollView)
        scrollView.addSubview(iv)
    }
    
    fileprivate func autoLayoutViews() {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        iv.translatesAutoresizingMaskIntoConstraints = false
    }

    fileprivate func constrainSubviews() {
        NSLayoutConstraint.activate([
            scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            scrollView.widthAnchor.constraint(equalTo: view.widthAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        NSLayoutConstraint.activate([
            iv.topAnchor.constraint(equalTo: scrollView.topAnchor),
            iv.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            iv.heightAnchor.constraint(equalTo: iv.widthAnchor, multiplier: 0.6)
        ])
    }
}

Solution

  • This may help.

    scrollView.contentInsetAdjustmentBehavior = .never


    For more information,

    This property specifies how the safe area insets are used to modify the content area of the scroll view. The default value of this property is UIScrollViewContentInsetAdjustmentAutomatic.