1

Hi student learning swift here. On iPad, I have been building a custom segmented control app, where in portrait, the segmented control is at the top and in landscape, the control is on the left. The segmented control is implemented by embedding a collectionView inside a UIView. This just helps me separate the datasource/delegate methods from the viewController. The content is displayed by a standard nested collectionView. Like so:

portraitenter image description here

I have managed to handle rotation within this view controller without any warnings or errors. I initiate two arrays of constraints for portrait and landscape and I deactivate/activate them in viewWillTransitionToSize as needed. The problem occurs when: new view controller is pushed onto this one -> device is rotated -> pop back to this controller.

Rotating to portrait on a new view controller and popping back causes a "UICollectionViewFLowLayout is not defined" error. Rotating to landscape is less severe, it causes the content collection view to be the wrong size. Like this:

Portrait bugLandscape bug

Below is my implementation of viewWillTransitionToSize. Thanks! I'd appreciate any ideas.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    segmentedControl.collectionView.collectionViewLayout.invalidateLayout()
    segmentedControl.collectionView.reloadData()
    contentScroll.collectionViewLayout.invalidateLayout()
    contentScroll.reloadData()

    super.viewWillTransition(to: size, with: coordinator)

    NSLayoutConstraint.deactivate(p)    //deactivate constraints for portrait and landscape
    NSLayoutConstraint.deactivate(l)

    if isPortrait {
        if let layout = segmentedControl.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            layout.scrollDirection = .horizontal
        }

        NSLayoutConstraint.activate(p)

        DispatchQueue.main.async {    //scrolling content and segmentedControl to their correct places
            self.jumpContentViewToIndex(tabIndex: self.targetIndex, animated: false)    
            self.segmentedControl.scrollAndUpdateIndex(to: self.targetIndex)
        }
    } else {
        if let layout = segmentedControl.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            layout.scrollDirection = .vertical
        }

        NSLayoutConstraint.activate(l)

        DispatchQueue.main.async {    //scrolling content and segmentedControl to their correct places
            self.contentScroll.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
            self.segmentedControl.scrollAndUpdateIndex(to: self.targetIndex)
        }
    }
}
kelelenceu
  • 81
  • 10

1 Answers1

1

After jumping through a lot of stackoverflow links, I was able to find a helpful answer here and here. My problem was because a view controller that is in the middle of a navigation stack will not recalculate its layout until the next layout pass. You could either force a layout update by calling layoutIfNeeded() during rotation or what I did was move all the rotation logic to viewDidLayoutSubviews() and just set a flag in viewWillTransition:tosize to check whether you need to perform your rotation logic.

kelelenceu
  • 81
  • 10