Pages

Wednesday 24 December 2014

Using constraints in storyboard and also by code


Sample code is in Github.


Three years before, all the iPhones sizes (iPhone3, iPhone3GS, iPhone4, iPhone4S) are the same. So we didn't care about the compatibility issues like the android developers. When apple introduced iPhone5 and later iPhone5S, iPhone5C, it was not a big deal for us. All we did was, add a launch image for those new screen sizes and we had to adjust our views a little to use the newly added 0.5 inches screen. So the auto layout helped us to achieve this.

Now apple introduces even big screen sizes, and the old style auto layout cannot help us like in the past. Because old auto layout only works with the super view i.e, if the super view expands it expands, when the superview move the control also will move. But you cannot specify how much you want to move or how much you want to expand. There is nothing more than this.

With using the auto layout, you can place a control in a view in its top left corner, but it is merely impossible to place a control at 30% of left. Also you cannot make a text field expands its width, half the width of its superview. But the Constraints does this trick.

When trying to learn this technology, it'll like a hell at first. But once you get into it, you will find the constraints as one of the best tools apple introduced ever. 

If you use the constraints in your app, you don't have to worry about these screen sizes. Even you don't have to worry about the iPad screen sizes. You can have a universal storyboard for all your devices.

Here I am trying start this, taking the following sample screen. The final output should look like this in Portait and Landscape.




1) The screen has a header in the top of the main view with 60 pixels height.
2) The header has two buttons in each corner with 10 pixels of padding and a header label in the middle.
3) A textfield is added to the view, which is 75 pixels below from the headerview and center to the main view. The width of this text field is half the width of the main view.
4) There are two buttons 25 pixels below the textfield. One button is left aligned with the textfield and the other one is right aligned with the textfield.

This is easy if you identify which constraint is to be added for each control in storyboard. But, it is little difficult to do in code. To meet all the above requirements, we have to add the following constraints.

You can find a sample project in Github illustrating this example both in storyboard and code. The link is here.


Create all the controls like you do normally, add those to the viewcontroller's view. Don't care about their positions. The constraints will do that.

Before adding the constraints to a subview, make sure you give No to its "translatesAutoresizingMaskIntoConstraints" property. Otherwise you will get an exception.


Constraints for the subviews of the header view.


Constraints for the header view.


Constraints for the textfield and the two button below it. (Make sure you disable the translatesAutoresizingMaskIntoConstraints for these controls before adding constraints.)


The alignment of the code here is worse, but believe me, it works.

- (void)viewDidLoad {
BOOL addControlProgrammatically = YES;
if (addControlProgrammatically) {
[self.view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[headerView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]];
[self.view addSubview:headerView];
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[cancelBtn setTitle:@"Cancel" forState:UIControlStateNormal];
[headerView addSubview:cancelBtn];
UIButton *saveBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[saveBtn setTitle:@"Save" forState:UIControlStateNormal];
[headerView addSubview:saveBtn];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 31)];
[textField setBorderStyle:UITextBorderStyleRoundedRect];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem];
[button1 setTitle:@"Button1" forState:UIControlStateNormal];

UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem];
[button2 setTitle:@"Button2" forState:UIControlStateNormal];
cancelBtn.translatesAutoresizingMaskIntoConstraints = NO;
saveBtn.translatesAutoresizingMaskIntoConstraints = NO;
[headerView addConstraints:@[[NSLayoutConstraint constraintWithItem:cancelBtn
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeLeading
multiplier:1.0 constant:10],
[NSLayoutConstraint constraintWithItem:cancelBtn
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeCenterY
multiplier:1.0 constant:0]
]];
[headerView addConstraints:@[[NSLayoutConstraint constraintWithItem:saveBtn
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeTrailing
multiplier:1.0 constant:-10],
[NSLayoutConstraint constraintWithItem:saveBtn
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeCenterY
multiplier:1.0 constant:0]
]];

headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:headerView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0 constant:60],
[NSLayoutConstraint constraintWithItem:headerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0 constant:0],
[NSLayoutConstraint constraintWithItem:headerView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0 constant:0],
[NSLayoutConstraint constraintWithItem:headerView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0 constant:0]
]];
[self.view updateConstraintsIfNeeded];
textField.translatesAutoresizingMaskIntoConstraints = NO;
button1.translatesAutoresizingMaskIntoConstraints = NO;
button2.translatesAutoresizingMaskIntoConstraints = NO;
// [textField setText:@"Hello World"];
[textField setBackgroundColor:[UIColor redColor]];
[self.view addSubview:textField];
[self.view addSubview:button1];
[self.view addSubview:button2];
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:textField
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0 constant:0],
[NSLayoutConstraint constraintWithItem:textField
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:75],
[NSLayoutConstraint constraintWithItem:textField
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:0.5 constant:0]]];
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:button1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:textField
attribute:NSLayoutAttributeLeft
multiplier:1.0 constant:0],
[NSLayoutConstraint constraintWithItem:button1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:textField
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:25]]];
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:button2
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:textField
attribute:NSLayoutAttributeRight
multiplier:1.0 constant:0],
[NSLayoutConstraint constraintWithItem:button2
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:textField
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:25]]];

}
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}

No comments:

Post a Comment