Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically implementing two different layouts using size classes

I have a four buttons layout. In portrait they should be shown one above the other. In landscape they should be in two columns each with two buttons.

I implement the buttons in code - really simple stuff:

UIButton *btn1 = [[UIButton alloc] init]; [self.view addSubview: btn1];   UIButton *btn2 = [[UIButton alloc] init]; [self.view addSubview: btn2];   UIButton *btn3 = [[UIButton alloc] init]; [self.view addSubview: btn3];   UIButton *btn4 = [[UIButton alloc] init]; [self.view addSubview: btn4];   NSDictionary *views = NSDictionaryOfVariableBindings(btn1, btn2, btn3, btn4);  [btn1 setTranslatesAutoresizingMaskIntoConstraints:NO]; [btn2 setTranslatesAutoresizingMaskIntoConstraints:NO]; [btn3 setTranslatesAutoresizingMaskIntoConstraints:NO]; [btn4 setTranslatesAutoresizingMaskIntoConstraints:NO];  // portrait constraints [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn1]-(50)-|"                                                                  options:0 metrics:nil views:views]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn2]-(50)-|"                                                                  options:0 metrics:nil views:views]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn3]-(50)-|"                                                                  options:0 metrics:nil views:views]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[btn4]-(50)-|"                                                                  options:0 metrics:nil views:views]];  [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2]-[btn3]-[btn4]-(50)-|"                                                                  options:0 metrics:nil views:views]]; 

This is obviously the setup for portrait layout. I would used to have determined the device and its orientation to make specific case for iPad and iPhone in there respective orientations. But now we are supposed to use size classes. How can I determine if the size class is "compact"... and thus set the appropriate constraints?

like image 872
Joseph Avatar asked Sep 05 '14 12:09

Joseph


People also ask

How many types are there to adjust layout for size classes?

Four of these are the Final size classes: Compact-Compact, Compact-Regular, Regular-Compact, and Regular-Regular.

What is Autolayout What is size classes and how can we use it?

In easy way we can say Autolayout is used for displaying same layout comatible on different different iPhone/iPad screen sizes (ex. Keep button in center for all screen sizes) while through Autoresizing classes we can set a different layout for a particular screen size.

What are size classes in Swift?

Size classes help you change the layout of views to adapt to various screen sizes, ranging from a tiny iPhone SE to a massive 12.9” iPad Pro. There are 3 different cases for a size class: regular , compact , and unspecified .


1 Answers

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.

like image 139
Joseph Avatar answered Sep 22 '22 23:09

Joseph