4

I have a method:

- (void)underlineTextField:(UITextField *)tf {
    CGFloat x = tf.frame.origin.x-8;
    CGFloat y = tf.origin.y+tf.frame.size.height+1;
    CGFloat width = self.inputView.frame.size.width-16;
    UIView *line = [[UIView alloc] initWithFrame:(CGRect){x,y,width,1}];
    line.backgroundColor = [UIColor whiteColor];
    [self.inputView addSubview:line];
}

That underlines an input UITextField; The textfield has a width that changes depending on the screen width (nib autolayout).

I have tried using

[self.view setNeedsLayout];
[self.view layoutIfNeeded];

and

[self.inputView setNeedsLayout];
[self.inputView layoutIfNeeded];

before I call this method with no change in result. the resulting line is much wider than the UITextField (it matches the original size in the Nib).

I just want the resulting frame of the UITextField in question after being processed by the autolayout

SOLUTION: (using 'Masonry' Autolayout)

- (UIView *)underlineTextField:(UITextField *)tf {
    UIView *line = [[UIView alloc] initWithFrame:CGRectZero];
    line.backgroundColor = [UIColor whiteColor];
    [self.inputView addSubview:line];
    [line mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(tf.mas_centerX);
        make.width.equalTo(tf.mas_width).with.offset(16);
        make.height.equalTo(@1);
        make.top.equalTo(tf.mas_bottom);
    }];
    return line;
}
Halpo
  • 2,982
  • 3
  • 25
  • 54

2 Answers2

4

Your underline view has a static frame, it is not connected to the textField through constraints. Instead of setting the frame, add constraints to self.inputView

- (void)underlineTextField:(UITextField *)tf {
    UIView *line = [[UIView alloc] init];
    line.backgroundColor = [UIColor whiteColor];
    [line setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.inputView addSubview:line];

    // Vertical constraints
    [self.inputView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[line(==1)]-(-1)-|" options:0 metrics:nil views:@{ @"line": line}]];

    // Horizontal constraints
    [self.inputView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(-8)-[line]-8-|" options:0 metrics:nil views:@{ @"line": line}]];

    [self.inputView layoutIfNeeded];
}

After the layoutIfNeeded call, the frame for your view should be right. I hoped I got the constants right. Because the line appears one unit under the textView make sure to unset Clip Subviews in the storyboard for the textField

I hope this works for you. Let me know if you have questions!

Catalina T.
  • 3,456
  • 19
  • 29
  • cheers, I wan't really confident with the whole programatic autolayout thing, but I found 'Masonry' a big help: – Halpo Apr 01 '15 at 22:14
0

You could adjust the frame of the underline in viewDidLayoutSubviews which is called after auto layout has set all the frames.

Note that you're creating a new view and calling addSubview: each time. You should do that once when you create the text view, outside of this method. Otherwise you'll create a new UIView every time your user rotates the device.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • `viewDidLayoutSubviews` is called about 20 times on load, I just want this to execute once. is there a reason why -setNeedsLayout isn't working? – Halpo Apr 01 '15 at 16:39
  • `setNeedsLayout` doesn't do that. You don't want it to execute once. You want to update the frame every time `inputView`'s frame changes. – Aaron Brager Apr 01 '15 at 18:47
  • You could also use an autoresizing mask or auto layout instead of setting the frame manually. – Aaron Brager Apr 01 '15 at 18:48