Programmatically implementing two different layouts using size classes

天涯浪子 提交于 2019-12-02 16:34:38

In the meantime I have found a good solution. Since this question has so many upvotes, I thought I would quickly describe it. I was inspired to this solution by a WWDC session.

I have moved on to Swift so please excuse that the code will be in swift - the concept is however the same for Obj-C.

You start out by declaring three constraint arrays:

 // Constraints
 private var compactConstraints: [NSLayoutConstraint] = []
 private var regularConstraints: [NSLayoutConstraint] = []
 private var sharedConstraints: [NSLayoutConstraint] = []

And then you fill the constraints accordingly. You can i.e. do this in a separate function that you call from viewDidLoad or you do it in viewDidLoad directly.

sharedConstraints.append(contentsOf: [
     btnStackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
    ...
])

compactConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7),
    ...
])

regularConstraints.append(contentsOf: [
     btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4),
    ...
])

The important part is switching between the size classes and activating/deactivating the appropriate constraints.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

    super.traitCollectionDidChange(previousTraitCollection)

    if (!sharedConstraints[0].isActive) {
       // activating shared constraints
       NSLayoutConstraint.activate(sharedConstraints)
    }


    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        if regularConstraints.count > 0 && regularConstraints[0].isActive {
            NSLayoutConstraint.deactivate(regularConstraints)
        }
        // activating compact constraints
        NSLayoutConstraint.activate(compactConstraints)
    } else {
        if compactConstraints.count > 0 && compactConstraints[0].isActive {
            NSLayoutConstraint.deactivate(compactConstraints)
        }
        // activating regular constraints
        NSLayoutConstraint.activate(regularConstraints)
    }
}

I know that the constraints don't fit to the ones in the question. But the constraints themselves are irrelevant. The main thing is how one switches between two sets of constraints based on the size class.

Hope this helps.

You can examine the view's trait collection to determine its horizontal and vertical size class.

if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
}

Implement the traitCollectionDidChange: method to automatically be called when a trait changes due to autorotation.

For more information, see UITraitCollection Class Reference and UITraitEnvironment Protocol Reference.

Swift 4 code for the accepted answer:

if (self.view.traitCollection.horizontalSizeClass == .compact) {
...
}
Kirit Vaghela

traitCollection only works in iOS8. So your app will crash on iOS7. Use the code below to support both iOS7 and iOS8

if ([self.view respondsToSelector:@selector(traitCollection)]){

    if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
    ...
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!