I believe there are many people are facing this problem. Last time my approach was to change the the scrollView‘s contentSize upon keyboard appear, and restore to normal when keyboard dismiss.

Now the problem in auto layout of scrollView was the contentSize is based on it’s subviews. I have figured out a way to do that

In ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
@interface ViewController ()

@property (nonatomic) UIScrollView *scrollView;
@property (nonatomic) UITextField *textField;

@end

@implementation TSPromotionVoucherFormViewController {
// 1.
UIView *_dummyView;
NSLayoutConstraint *_dummyViewHeightConstraint;
}

- (void)viewDidLoad {
[super viewDidLoad];

// 2.
_scrollView = [[UIScrollView alloc] init];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_scrollView];

_dummyView = [[UIView alloc] init];
_dummyView.translatesAutoresizingMaskIntoConstraints = NO;
_dummyView.backgroundColor = [UIColor clearColor];
[_scrollView addSubview:_dummyView];

_textField = [[UITextField alloc] init];
_textField.translatesAutoresizingMaskIntoConstraints = NO;
_textField.placeholder = @"Dummy text";
[_scrollView addSubview:_textField];

// 3.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollView
, _dummyView
, _textField);

NSDictionary *matrics = @{@"inputMargin": @(20), @"textHeight": @(44)};

NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:@"V:|[_scrollView]|"
options:0
metrics:nil
views:viewsDictionary];
[self.view addConstraints:constraints];

constraints = [NSLayoutConstraint
constraintsWithVisualFormat:@"|[_scrollView]|"
options:0
metrics:nil
views:viewsDictionary];
[self.view addConstraints:constraints];

constraints = [NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-40-[_textField(textHeight)]-5-[_dummyView]-5-|"
options:0
metrics:matrics // remember to pass in the matrics if you want to use the "variables"
views:viewsDictionary];
[_scrollView addConstraints:constraints];
constraints = [NSLayoutConstraint
constraintsWithVisualFormat:@"|-inputMargin-[textField]-inputMargin-|"
options:0
metrics:matrics
views:viewsDictionary];
[_scrollView addConstraints:constraints];
constraints = [NSLayoutConstraint
constraintsWithVisualFormat:@"|-inputMargin-[_dummyView]-inputMargin-|"
options:0
metrics:matrics
views:viewsDictionary];
[_scrollView addConstraints:constraints];

// 4.
_dummyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:_dummyView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:0];
[_dummyView addConstraint:_dummyViewHeightConstraint];

// 5.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

#pragma mark - keyboard notification
- (void)keyboardWillShow:(NSNotification *)notification
{
// 6.
NSDictionary *userInfo = [notification userInfo];
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

[_dummyView removeConstraint:_dummyViewHeightConstraint];

_dummyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:_dummyView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:keyboardSize.height];
[_dummyView addConstraint:_dummyViewHeightConstraint];
}

- (void)keyboardWillHide:(NSNotification *)notification
{
// 7.
[_dummyView removeConstraint:_dummyViewHeightConstraint];

_dummyViewHeightConstraint = [NSLayoutConstraint constraintWithItem:_dummyView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:0];
[_dummyView addConstraint:_dummyViewHeightConstraint];
}

In the case above, you have a UIScrollView in your ViewController, and a UITextField inside the UIScrollView.

I remention the problem here, when the textField on focus, the keyboard will brought up, and the scrollView‘s contentSize still the same,
the keyboard may blocked the textField and become invisible, yet it cannot scroll up.

Let’s begin to solve this

  1. Declare 2 ivars, a dummy view & it’s height constraint.
  2. Initialize all views, and make sure translatesAutoresizingMaskIntoConstraints is set to NO for all views that wanted to use auto layout.
  3. Add constraints to all these view. The matrics is actually variables that wanted to use inside the constraints’ visual format.
  4. Initialize the dummy view’s height constraint separately to that ivar. Since it is dummy, so set the height (refer the constant argument) to 0.
  5. Remember to add keyboard’s notification (listen to keyboard’s event)
  6. When the keyboard is shown, remove the dummy view’s constraint first, then set the dummy view’s height to keyboard’s height, then only add the height constraint back to the dummy view.
  7. When keyboard is hide, do the same thing with 6, but the constant now is 0.

Run it, and it will works as expected. Enjoy :)