override traitCollection in iOS 13
Asked Answered
W

2

7

In my initial view controller, I have have a UITabbarController as a child view controller.

I want to have UITabbarController to display its UITabbar with traitCollection having horizontalSizeClass of Compact so that in the tabbar, image and title appears vertically aligned and not side by side.

Overriding the traitCollection getter of UITabbarController is now not supported in iOS13, Xcode gives below warning.

        override var traitCollection: UITraitCollection{
            let current = super.traitCollection
            let compact = UITraitCollection(horizontalSizeClass: .compact)
            return UITraitCollection(traitsFrom: [current, compact])
        }

Class MyTabbarController overrides the -traitCollection getter, which is not supported. If you're trying to override traits, you must use the appropriate API.

After researching for appropriate API, I found

open func setOverrideTraitCollection(_ collection: UITraitCollection?, forChild childViewController: UIViewController)

After implementing this I am able to override trait collection of myTabbarController but only after the view has changed orientation. This API is only working if I override viewWillTransition to method.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        let currentTC = traitCollection
        let compactTC = UITraitCollection(horizontalSizeClass: .compact)
        let custom = UITraitCollection(traitsFrom: [currentTC, compactTC])
        print("ovverride trait collections before transition")
        setOverrideTraitCollection(custom, forChild: tabController)
    }

I am only able to override the traits when the device is rotated. This API is not working if I try to override the trait collection in any other view controller lifecycle method. How do I override the traitCollection when the view is initially loaded? I tried using the same code in the viewDidLoad() method of my initial view controller but it has no effect.

Workingman answered 28/12, 2019 at 10:11 Comment(0)
A
1

When using iOS 17 or newer, it is possible to directly override trait collection properties by setting the traitOverrides property of the view controller. Example:

override func viewDidLoad() {
    super.viewDidLoad()
    
    traitOverrides.horizontalSizeClass = .compact
}
Abettor answered 11/6 at 13:23 Comment(0)
A
0

I'm not sure if the OP ever got this working, but I ran into the same issue recently. In my case, I need to treat the device orientation the same for iPhone and iPad, and in particular set the horizontalSizeClass to .compact in portrait orientation.

Because setOverrideTraitCollection() only works on a child view controller, I had to embed my "master" view controller inside another view controller (which I call my "root" view controller), and handle the trait overrides in the root view controller. As the OP alluded to, this needs to happen at both app startup and whenever the orientation changes. In my case, I could do the startup code in prepareForSegue. Not sure why putting the code in viewDidLoad() didn't work for the OP -- perhaps because he wasn't calling setNeedsLayout() for the child view controller's view.

Here's my root view controller code:

class RootViewController: UIViewController {

    var masterViewController: MasterViewController?

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "MasterViewSegue" {
            masterViewController = segue.destination as? MasterViewController
            updateMasterViewTraits(for: CGSize(width: view.bounds.width, height: view.bounds.height))
        }
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        updateMasterViewTraits(for: size)
    }

    func updateMasterViewTraits(for size: CGSize) {
        var orientationTraits: UITraitCollection
        if size.width < size.height {
            orientationTraits = UITraitCollection(traitsFrom:[UITraitCollection(horizontalSizeClass: .compact), UITraitCollection(verticalSizeClass: .regular)])
        } else {
            orientationTraits = UITraitCollection(traitsFrom:[UITraitCollection(horizontalSizeClass: .regular), UITraitCollection(verticalSizeClass: .compact)])
        }
        let traits = UITraitCollection(traitsFrom: [traitCollection, orientationTraits])
        setOverrideTraitCollection(traits, forChild: masterViewController!)
        masterViewController!.view.setNeedsLayout()
    }

}
Attain answered 25/5, 2020 at 12:58 Comment(1)
Why do we need to do it this way? Is there any obvious reason why Apple doesn't allow you to override trait for the presenting VC?Wavawave

© 2022 - 2024 — McMap. All rights reserved.