iosswiftpagecontrol

How to change starting page to another page in UIScrollView


I've created a scroll view where the user can cycle through the pages that I've assigned. The first button leads to page1 as the starting page. However I would like the current starting page to be page 2 when I press button 2 that leads me to the page views but I can't seem to find a way around it,

My code in the ViewController is as follows:

var pages : [View] {
    
    get {
        let page1: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page1.colorLabel.text = "Apartment A3"
        page1.priceLabel.text = "N$ 504 500"
        page1.imageView.image = UIImage(named: "apartment_a3_1")
        
        
        let page2: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page2.colorLabel.text = "Apartment A4"
        page2.priceLabel.text = "N$ 481 000"
        page2.imageView.image = UIImage(named: "apartment_a4_1")
        
        let page3: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page3.colorLabel.text = "Apartment A5"
        page3.priceLabel.text = "N$ 541 000"
        page3.imageView.image = UIImage(named: "apartment_a5_1")
        
        let page4: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page4.colorLabel.text = "Apartment A6"
        page4.priceLabel.text = "N$ 553 000"
        page4.imageView.image = UIImage(named: "apartment_a6_1")
        
        let page5: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page5.colorLabel.text = "Apartment A8"
        page5.priceLabel.text = "N$ 588 000"
        page5.imageView.image = UIImage(named: "apartment_a8_1")
        
        let page6: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page6.colorLabel.text = "Apartment A9"
        page6.priceLabel.text = "N$ 775 000"
        page6.imageView.image = UIImage(named: "apartment_a9a1")
        
        let page7: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
        page7.colorLabel.text = "Apartment A12"
        page7.priceLabel.text = "N$ 775 000"
        page7.imageView.image = UIImage(named: "apartment_a12_a")
        

        return [page1, page2, page3, page4, page5, page6, page7]
    }
}

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var pageControl: UIPageControl!

override func viewDidLoad() {
    super.viewDidLoad()
    
    //view.bringSubviewToFront(pageControl)
    
    setupScrollView(pages: pages)
    
    pageControl.numberOfPages = pages.count
    pageControl.currentPage = 0
}

func setupScrollView(pages: [View]) {
    //scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
    scrollView.contentSize = CGSize(width: view.frame.width * CGFloat(pages.count), height: view.frame.height)
    scrollView.isPagingEnabled = true
    
    
    for i in 0 ..< pages.count {
        pages[i].frame = CGRect(x: view.frame.width * CGFloat(i), y: 0, width: view.frame.width, height: view.frame.height)
        scrollView.addSubview(pages[i])
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let pageIndex = round(scrollView.contentOffset.x/view.frame.width)
        pageControl.currentPage = Int(pageIndex)
    }
}

I've tried to change PageControl.currentPage = 0 to 1 but no luck.


Solution

  • Couple notes...

    First, you should be using auto-layout -- makes it much easier to manage the frames and the scroll view content.

    Second, keep your size calculations relative to the scroll view's frame, not the view's frame... it will keep things more consistent, and avoid problems if you decide to make the scroll view not "full screen" (which you shouldn't do anyway, because you always want to respect the safe-area).

    Third, if you only have 7 "pages" this approach should be fine. If you may have more than that, you can run into memory issues. If that's the case, you should look into either a UICollectionView or UIPageViewController.

    So, assuming you can stick with the scroll view approach...

    To "start at the second page" you can set the scroll view's .contentOffset.x during viewDidLayoutSubviews.

    Start by adding a var property to track the width of the scroll view's frame:

    var scrollViewWidth: CGFloat = 0
    

    then implement viewDidLayoutSubviews():

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        // we only want to call this once
        if scrollViewWidth != scrollView.frame.width {
            scrollViewWidth = scrollView.frame.width
            scrollView.contentOffset.x = scrollView.frame.width
        }
    }
    

    Here's a quick look at some changes you might want to make to take advantage of the benefits of auto-layout:

    class ViewController: UIViewController {
    
        var pages : [View] {
            
            get {
                let page1: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page1.colorLabel.text = "Apartment A3"
                page1.priceLabel.text = "N$ 504 500"
                page1.imageView.image = UIImage(named: "apartment_a3_1")
                
                
                let page2: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page2.colorLabel.text = "Apartment A4"
                page2.priceLabel.text = "N$ 481 000"
                page2.imageView.image = UIImage(named: "apartment_a4_1")
                
                let page3: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page3.colorLabel.text = "Apartment A5"
                page3.priceLabel.text = "N$ 541 000"
                page3.imageView.image = UIImage(named: "apartment_a5_1")
                
                let page4: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page4.colorLabel.text = "Apartment A6"
                page4.priceLabel.text = "N$ 553 000"
                page4.imageView.image = UIImage(named: "apartment_a6_1")
                
                let page5: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page5.colorLabel.text = "Apartment A8"
                page5.priceLabel.text = "N$ 588 000"
                page5.imageView.image = UIImage(named: "apartment_a8_1")
                
                let page6: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page6.colorLabel.text = "Apartment A9"
                page6.priceLabel.text = "N$ 775 000"
                page6.imageView.image = UIImage(named: "apartment_a9a1")
                
                let page7: View = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! View
                page7.colorLabel.text = "Apartment A12"
                page7.priceLabel.text = "N$ 775 000"
                page7.imageView.image = UIImage(named: "apartment_a12_a")
                
                
                return [page1, page2, page3, page4, page5, page6, page7]
            }
        }
    
        @IBOutlet weak var scrollView: UIScrollView!
        @IBOutlet weak var pageControl: UIPageControl!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            setupScrollView(pages: pages)
            
            pageControl.numberOfPages = pages.count
            pageControl.currentPage = 0
        }
        
        var scrollViewWidth: CGFloat = 0
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            
            // we only want to call this once
            if scrollViewWidth != scrollView.frame.width {
                scrollViewWidth = scrollView.frame.width
                scrollView.contentOffset.x = scrollView.frame.width
            }
        }
    
        func setupScrollView(pages: [View]) {
            
            let stack = UIStackView()
            stack.translatesAutoresizingMaskIntoConstraints = false
            
            let clg = scrollView.contentLayoutGuide
            let flg = scrollView.frameLayoutGuide
            
            scrollView.addSubview(stack)
            NSLayoutConstraint.activate([
                // constrain all 4 sides of stack view to scroll view's Content Layout Guide
                stack.topAnchor.constraint(equalTo: clg.topAnchor),
                stack.leadingAnchor.constraint(equalTo: clg.leadingAnchor),
                stack.trailingAnchor.constraint(equalTo: clg.trailingAnchor),
                stack.bottomAnchor.constraint(equalTo: clg.bottomAnchor),
                
                // constrain height of stack view to scroll view's Frame Layout Guide
                stack.heightAnchor.constraint(equalTo: flg.heightAnchor),
            ])
            
            for i in 0 ..< pages.count {
                // add each page to the stack view
                stack.addArrangedSubview(pages[i])
                // make each page the same width as the scroll view's Frame Layout Guide width
                pages[i].widthAnchor.constraint(equalTo: flg.widthAnchor).isActive = true
            }
            
            scrollView.isPagingEnabled = true
            scrollView.delegate = self
    
        }
        
        @IBAction func pageControlChanged(_ sender: UIPageControl) {
            UIView.animate(withDuration: 0.3, animations: {
                self.scrollView.contentOffset.x = CGFloat(sender.currentPage) * self.scrollView.frame.width
            })
        }
    }
    extension ViewController: UIScrollViewDelegate {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            let pageIndex = round(scrollView.contentOffset.x / scrollView.frame.width)
            pageControl.currentPage = Int(pageIndex)
        }
    }
    

    Edit - response to comment...

    If you have buttons to "go to a page" you can implement a function like this:

    func showPage(_ pg: Int) {
        UIView.animate(withDuration: 0.3, animations: {
            self.scrollView.contentOffset.x = CGFloat(pg - 1) * self.scrollView.frame.width
        })
    }
    

    and call it with, for example:

    showPage(5)