Is UITraitsCollection the path to having diff iPad Landscape vs Portrait AutoLayout in IB/Storyboard

135 Views Asked by At

The Ask: I want to have different layouts between Portrait vs Landscape on the iPad. (I'm successful on the iPhone But iPad share similar size classes)

What I've done/Tried:

Reading and searching thru Google and SO, I haven't found the solution. What I did learn was about overriding UITraitsCollection and If possible, I would like to force the iPad Landscape configuration to follow the same one which I've already set up for the iPhone Landscape mode using hCompact

I tried this solution https://stackoverflow.com/a/60409218/14414215 but unsure where to put it and when just placed within the UIViewController, when running, the app doesn't even call it. (It's likely cos I don't have my views within a childViewController?)

This post here also references UITraitsCollection https://stackoverflow.com/a/41374705/14414215 however there is a lack of an example.

I'm fairly sure that this is something that many apps are doing but I'm not able to find the solution.

For reference: This is what I want to achieve on the iPad as well. (iPhone works great using this solution from @DonMag)

StackView layout side by side & on Top of each other (like zStack)

1

There are 1 best solutions below

2
On BEST ANSWER

With different device screen aspect ratios, and the multi-tasking capabilities on iPads, you're encouraged to think in terms of size not orientation.

If your app is being run with Slide Over / Split View, the size class can be wC hR even when the device is in "landscape orientation."

If you want to change layout based on aspect ratio:

  • if width > height use a "landscape" layout
  • if height > width use a "portrait" layout

You may want to create your own constraint collections and activate / deactivate them in viewWillTransition(to size:...)

If you want to stick with Trait variations, you'll run into problems trying to manipulate them for the "root" view of a controller.

You could try making your controller a child view controller, either by embedding it in a UIContainerView or adding it via code, and then implementing (in the "parent" view controller):

class ParentViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let vc = storyboard?.instantiateViewController(withIdentifier: "myVC") as? MyRealViewController {
            addChild(vc)
            view.addSubview(vc.view)
            vc.didMove(toParent: self)
        }
        
    }
    
    override func overrideTraitCollection(forChild childViewController: UIViewController) -> UITraitCollection? {
        if childViewController.view.bounds.width > childViewController.view.bounds.height {
            let collections = [UITraitCollection(horizontalSizeClass: .regular),
                               UITraitCollection(verticalSizeClass: .compact)]
            return UITraitCollection(traitsFrom: collections)
        }
        return super.overrideTraitCollection(forChild: childViewController)
    }
    
}