Configure UIScrollView content with autolayout, insets for keyboard, and rotation to landscape

paiv picture paiv · Oct 30, 2013 · Viewed 8.3k times · Source

I'm fighting with scrollviews in autolayout (iOS 6,7) for some time now, and it's getting depressing.

Consider a simple entry form

that I want be scrollable, and that should resize in landscape:

portrait

Views hierarchy is:

view hierarchy

I need to configure proper constraints so that

  • scrolling area gets updated properly when keyboard appears and disappears
  • content gets resized when device is rotated to landscape and back to portrait
  • scrolling area gets updated for landscape and portrait appropriately

Could this be done with no code?

What I get instead

Wrong scroll size when keyboard appears

wrong scroll size

Content not resized in landscape

no resize in landscape

Source code to play with:

source code

Answer

Jonathon Horsman picture Jonathon Horsman · Dec 18, 2013

Behold! After 2 days of searching I believe I may have an answer. Admittedly it cannot be done without code however.

First create your usual top, bottom, leading and trailing constraints from the contentView to the Scroll view. However with the leading and trailing, tick the "Placeholder - Remove at build time" option.

Then inside your viewDidLoad method add the following:

NSLayoutConstraint *leftConstraint =[NSLayoutConstraint
                                     constraintWithItem:self.contentView
                                     attribute:NSLayoutAttributeLeading
                                     relatedBy:0
                                     toItem:self.view
                                     attribute:NSLayoutAttributeLeft
                                     multiplier:1.0
                                     constant:0];
[self.view addConstraint:leftConstraint];

NSLayoutConstraint *rightConstraint =[NSLayoutConstraint
                                     constraintWithItem:self.contentView
                                     attribute:NSLayoutAttributeTrailing
                                     relatedBy:0
                                     toItem:self.view
                                     attribute:NSLayoutAttributeRight
                                     multiplier:1.0
                                     constant:0];
[self.view addConstraint:rightConstraint];

This dynamically adds the leading and trailing constraints from your contentView to the controller's main view (i.e. outside the scroll view).

Then when you rotate your device the input fields are stretched appropriately. This solves your rotation problem, regarding the keyboard appearing there's other answers to this on SO, but basically inside viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardDidHideNotification object:nil];

then add these 2 methods:

- (void) keyboardWasShown:(NSNotification *)notification
{
  NSDictionary *info = [notification userInfo];
  CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
  UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0);
  self.scrollView.contentInset = contentInsets;
  self.scrollView.scrollIndicatorInsets = contentInsets;
}

- (void) keyboardWillBeHidden:(NSNotification *)notification
{
  UIEdgeInsets contentInsets = UIEdgeInsetsZero;
  self.scrollView.contentInset = contentInsets;
  self.scrollView.scrollIndicatorInsets = contentInsets;
}