Resize a UITextField while typing (by using Autolayout)

后端 未结 9 1750
梦如初夏
梦如初夏 2020-12-04 13:05

I have a UITableViewCell which has two UITextFields (without borders). The following constraints are used to set up the horizontal layout.

@\"|-10-[leftTextFi

相关标签:
9条回答
  • 2020-12-04 13:29

    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

    0 讨论(0)
  • 2020-12-04 13:29

    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!

    0 讨论(0)
  • 2020-12-04 13:29

    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.

    0 讨论(0)
  • 2020-12-04 13:29

    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;
    }
    
    0 讨论(0)
  • 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.

    0 讨论(0)
  • 2020-12-04 13:36

    You can add this code in viewDidLoad to solve the problem.

    if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
    {
        self.edgesForExtendedLayout = UIRectEdgeNone;
    }
    
    0 讨论(0)
提交回复
热议问题