I have a UITableViewCell which has two UITextFields (without borders). The following constraints are used to set up the horizontal layout.
@\"|-10-[leftTextFi
I don't know why UITextField
only updates its intrinsicContentSize
when it resigns its first responder status. This happens for both iOS 7 and iOS 8.
As a temporary solution, I override intrinsicContentSize
and determine the size using typingAttributes
. This accounts for leftView
and rightView
as well
// This method is target-action for UIControlEventEditingChanged
func textFieldEditingChanged(textField: UITextField) {
textField.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
if isEditing {
let string = (text ?? "") as NSString
let size = string.size(attributes: typingAttributes)
return CGSize(width: size.width + (rightView?.bounds.size.width ?? 0) + (leftView?.bounds.size.width ?? 0) + 2,
height: size.height)
}
return super.intrinsicContentSize
}
Here I make the it wider to account for the caret
I had a similar requirement with a UITextView
(had to dynamically increase it's height and some other things).
What I did was something similar to this:
Considering self.contentView
is the superview
of textField
- (IBAction)textFieldDidChange:(UITextField *)textField {
//You can also use "textField.superview" instead of "self.contentView"
[self.contentView setNeedsUpdateConstraints];
//Since you wish to animate that expansion right away...
[UIView animateWithDuration:0.1 animations:^{
[self.contentView updateConstraintsIfNeeded];
}];
NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize));
}
Also, considering the requirements I had, I had to override the updateConstraints
method of my UITextView
's superview
.
Should you choose to opt for that solution (maybe for a more fine tuned approach), you can do:
- (void)updateConstraints {
[super updateConstraints];
//Firstly, remove the width constraint from the textField.
[self.myTextField removeConstraint:self.textFieldWidthConstraint];
self.textFieldWidthConstraint = nil;
CGSize contentSize = self.myTextField.intrinsicContentSize;
self.textFieldWidthConstraint = [NSLayoutConstraint constraintWithItem:self.myTextField attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:contentSize.width];
[self.myTextField addConstraint:self.textFieldWidthConstraint];
}
As mentioned, the override option was used by me because I required a more fine tuned approach.
Additionally, as always you'll need to make sure that you're checking for any edge cases (in terms of sizing values) that you think you might encounter.
Hope this helps!
Cheers!
For some reason, calling invalidateIntrinsicContentSize
wasn't working for me either, but I also ran into issues with the other solutions due to the margins and editing controls.
This solution isn't always needed; if invalidateIntrinsicContentSize
works, then all that needs to be done is to add a call to that when the text is changed, as in the other answers. If that isn't working, then here's a solution (adapted from here) I found which also works with a clear control:
class AutoResizingUITextField: UITextField {
var lastTextBeforeEditing: String?
override init(frame: CGRect) {
super.init(frame: frame)
setupTextChangeNotification()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupTextChangeNotification()
}
func setupTextChangeNotification() {
NotificationCenter.default.addObserver(
forName: Notification.Name.UITextFieldTextDidChange,
object: self,
queue: OperationQueue.main) { (notification) in
self.invalidateIntrinsicContentSize()
}
NotificationCenter.default.addObserver(
forName: Notification.Name.UITextFieldTextDidBeginEditing,
object: self,
queue: OperationQueue.main) { (notification) in
self.lastTextBeforeEditing = self.text
}
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
if isEditing, let text = text, let lastTextBeforeEditing = lastTextBeforeEditing {
let string = text as NSString
let stringSize = string.size(attributes: typingAttributes)
let origSize = (lastTextBeforeEditing as NSString).size(attributes: typingAttributes)
size.width = size.width + (stringSize.width - origSize.width)
}
return size
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
If invalidateIntrinsicContentSize is working by itself, this will end up double-counting the change so that needs to be checked first.
For me i also added a target to the textfield (UIControlEventEditingChanged)
- (void)textFieldChanged:(UITextField *)textField {
[textField sizeToFit];
}
if you have a background color, then just update it in delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
textField.backgroundColor = [UIColor grayColor];
return YES;
}
Another solution to this is to set leading/trailing constraints to the textField and use isActive
to turn them on/off.
When editing start, turn them on, if the text is centered, the effect will be the same (text will seems to be auto resizing as typing). When editing stops, turn constraints off, which will wrap the frame around the text.
You can add this code in viewDidLoad to solve the problem.
if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
}