0

I am following a tutorial to create a swift app that adds a number of views to a scroll view and then allows me to scroll between them. I have the app working and understand it for the most part. When I change the orientation of the device the views width don't get updated so I have parts of more than one view controller on the screen? Does anybody know how to fix this? From my understanding I need to call something in the viewsDidTransition method to redraw the views. Any help would be greatly appreciated. Thanks!

Here is what I have so far:

import UIKit

class ViewController: UIViewController {

    private let scrollView = UIScrollView()
    
    private let pageControl: UIPageControl = {
       let pageControl = UIPageControl()
        pageControl.numberOfPages = 5
        pageControl.backgroundColor = .systemBlue
        return pageControl
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
        pageControl.addTarget(self,
                              action: #selector(pageControlDidChange(_:)),
                              for: .valueChanged)
        scrollView.backgroundColor = .red
        view.addSubview(scrollView)
        view.addSubview(pageControl)
    }
    
    @objc private func pageControlDidChange(_ sender: UIPageControl){
        let current = sender.currentPage
        scrollView.setContentOffset(CGPoint(x: CGFloat(current) * view.frame.size.width,
                                             y: 0), animated: true)
    }
    
    
    

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        pageControl.frame = CGRect(x: 10, y: view.frame.size.height - 100, width: view.frame.size.width - 20, height: 70)

        scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height - 100)
        
        
        if scrollView.subviews.count == 2 {
            configureScrollView()
        }
        
    }
    
    private func configureScrollView(){
        scrollView.contentSize = CGSize(width: view.frame.size.width*5, height: scrollView.frame.size.height)
        scrollView.isPagingEnabled = true
        let colors: [UIColor] = [.systemRed, .systemGray, .systemGreen, .systemOrange, .systemPurple]
        
        
        
        for x in 0..<5{
            let page = UIView(frame: CGRect(x: CGFloat(x) * view.frame.size.width, y: 0, width: view.frame.size.width, height: scrollView.frame.size.height))
            page.backgroundColor = colors[x]
            scrollView.addSubview(page)
        }
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        print("hello world")
        
    }
    
    
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        pageControl.currentPage = Int(floorf(Float(scrollView.contentOffset.x) / Float(scrollView.frame.size.width)))
    }
    }
    
Overture
  • 83
  • 1
  • 10
  • I use a scroll view and it handles changes in orientation well. But I don't have the time (right now) to totally answer your question. It - to me - seems like it's about view controller overrides or life cycles. (Views have constraints where controllers are more attuned to the hardware and OS.) `viewWillTransition(to size:)` seems wrong to me. First - size classes are *not* meant to say much more than there's more than a change between Regular/Compact - and trust me, you'll learn how things operate on an iPad *very* different than an iPhone (and that even depends on the iPhone screen size). –  Mar 23 '21 at 15:06
  • /2.... Look into 'viewDidAppear` for setting the size of you content view within a scroll view. That's what I use and seems to work well. –  Mar 23 '21 at 15:09
  • @dfd thanks for your answer, how does one call viewDidUpdate when the view is rotated? does it happen automatically? I have tried a different approach and have run into something similar: https://stackoverflow.com/questions/66788328/rotation-problem-with-scroll-view-in-swift – Overture Mar 24 '21 at 19:55
  • FIrst, let's talk a moment about view controller life cycles, an oldie but goodie - https://stackoverflow.com/questions/5562938/looking-to-understand-the-ios-uiviewcontroller-lifecycle Scroll down to the third answer for a nice graphic. All these are overrides. Just like the override you are using - `viewWillAppear` (and *not* viewWillUpdate, I think nothing like that exists) is just part of this life cycle. I also see my second comment contained a hash(?) typo that may have led you wrong. –  Mar 25 '21 at 02:12
  • What I've found is - on an orientation change - it's not reliable to assume that `viewWillTransition(to size:)` will get hit. Usually, but not always. But a view controller will always (maybe more than once) resize it's subviews. –  Mar 25 '21 at 02:14
  • @dfd, thanks I understand that much better now, thanks. I can see that viewDidLayoutSubviews() is called everytime I rotate and seems more logical. What logic would you put in here to solve the rotation issues? So far I have: **bold** `let screenRect = UIScreen.main.bounds let screenWidth = screenRect.size.width scrollView.contentOffset.x = CGFloat(Int(CGFloat(pageControl.currentPage) * screenWidth))`. This works for all the pages except the last one, rotate the first time works fine but rotating it back brings me back a page. Thanks! – Overture Mar 25 '21 at 10:39

0 Answers0