iphone 键入时调整UITextField的大小(使用Autolayout)




cocoa-touch (8)

我不知道为什么UITextField在它的第一个响应者状态重新启动时才更新其intrinsicContentSize 。 这适用于iOS 7和iOS 8。

作为临时解决方案,我覆盖intrinsicContentSize并使用typingAttributes确定大小。 这也考虑了leftViewrightView

// 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
  }

在这里,我更广泛地考虑到插入符号

我有一个UITableViewCell,它有两个UITextFields(没有边框)。 以下约束用于设置水平布局。

@"|-10-[leftTextField(>=80)]-(>=10)-[rightTextField(>=40)]-10-|"

没有什么花哨的,并且按预期工作。

如您所见,上面的textField具有正确的大小。 较低的textField以空文本开头,由于空文本,它是80点宽。 当我在编辑textField中键入文本时,文本向左滚动,它不会改变其宽度。

我不喜欢这样,当用户键入textField时,textField的宽度应该调整。

在我看来,应该开箱即用。 通过为UIControlEventEditingChanged事件实现IBAction ,我可以确认键入实际上更改了UITextField的intrinsicContentSize

但是,在textField不再是第一个响应者之前,宽度不会改变。 如果我将光标放入另一个textField,则设置已编辑的textField的宽度。 对于我想要的东西,这有点晚了。

这些注释掉的行是我尝试过的,没有任何成功:

- (IBAction)textFieldDidChange:(UITextField *)textField {
    [UIView animateWithDuration:0.1 animations:^{
//        [self.contentView removeConstraints:horizontalConstraints];
//        [self.contentView addConstraints:horizontalConstraints];
//        [self.contentView layoutIfNeeded];

//        [self.contentView setNeedsLayout];

//        [self.contentView setNeedsUpdateConstraints];
    }];
    NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize));
}

有人知道我错过了什么吗? 我可以尝试做些什么呢?


Answer #1

这对我有用:

- (IBAction) textFieldDidChange: (UITextField*) textField
{
    [UIView animateWithDuration:0.1 animations:^{
        [textField invalidateIntrinsicContentSize];
    }];
}

有趣的是,考虑到文档中的文本,似乎它应该开箱即用:

此外,如果视图的属性发生更改并且该更改会影响内部内容大小,则视图必须调用invalidateIntrinsicContentSize,以便布局系统注意到更改并可以重新布局。 在视图类的实现中,必须确保如果内部大小所依赖的任何属性的值发生更改,则调用invalidateIntrinsicContentSize。 例如,如果字符串值更改,则文本字段将调用invalidateIntrinsicContentSize。

我最好的猜测是,一旦编辑完成,文本字段只调用invalidateIntrinsicContentSize,而不是在编辑期间。

编辑:一堆“这对我不起作用”。 我认为这里的混淆可能是与textFieldDidChange: handler绑定的触发事件。 该事件需要是UIControlEventEditingChanged。 如果您正在使用IB,请仔细检查您是否正在处理正确的事件。

UITextField大小也不受约束。 您可以使用约束将其锁定到位,但任何宽度约束或左右定位约束集都将阻止其调整其内在内容大小。


Answer #2

出于某种原因,调用invalidateIntrinsicContentSize对我来说也不起作用,但由于边距和编辑控件,我也遇到了其他解决方案的问题。

并不总是需要这种解决方案; 如果invalidateIntrinsicContentSize有效,那么所有需要做的就是在文本更改时添加一个调用,就像在其他答案中一样。 如果这不起作用,那么这是一个解决方案(从here改编)我发现它也可以使用一个明确的控件:

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)
    }
}

如果invalidateIntrinsicContentSize单独工作 ,则最终会对更改进行重复计算,以便首先进行检查。


Answer #3

对我来说,我还在文本字段中添加了一个目标(UIControlEventEditingChanged)

- (void)textFieldChanged:(UITextField *)textField {
    [textField sizeToFit];
}

如果你有背景颜色,那么只需在委托方法中更新它:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    textField.backgroundColor = [UIColor grayColor];
    return YES;
}

Answer #4

这是Swift的答案。 作为奖励,如果有占位符,则此代码段不会折叠文本字段。

// UITextField subclass

override func awakeFromNib() {
    super.awakeFromNib()
    self.addTarget(self, action: #selector(textFieldTextDidChange), forControlEvents: .EditingChanged)
}

func textFieldTextDidChange(textField: UITextField) {
    textField.invalidateIntrinsicContentSize()
}

override func intrinsicContentSize() -> CGSize {
    if self.editing {
        let textSize: CGSize = NSString(string: ((text ?? "" == "") ? self.placeholder : self.text) ?? "").sizeWithAttributes(self.typingAttributes)
        return CGSize(width: textSize.width + (self.leftView?.bounds.size.width ?? 0) + (self.rightView?.bounds.size.width ?? 0) + 2, height: textSize.height)
    } else {
        return super.intrinsicContentSize()
    }
}

Answer #5

您可以在viewDidLoad中添加此代码来解决问题。

if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
{
    self.edgesForExtendedLayout = UIRectEdgeNone;
}

Answer #6

我对UITextView有类似的要求(必须动态增加它的高度和其他一些东西)。

我做的是类似的事情:

考虑self.contentViewtextFieldsuperview

- (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));
}

另外,考虑到我的要求,我不得不重写我的UITextView superviewupdateConstraints方法。

如果您选择选择该解决方案(可能需要更精细的方法),您可以:

- (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];
}

如上所述,我使用了覆盖选项,因为我需要一个更精细的调整方法。

此外,与往常一样,您需要确保检查您认为可能遇到的任何边缘情况(就调整值而言)。

希望这可以帮助!

干杯!


Answer #7

另一个解决方案是将前导/尾随约束设置为textField,并使用isActive打开/关闭它们。

编辑开始时,打开它们,如果文本居中,效果将是相同的(文本似乎会自动调整大小作为输入)。 当编辑停止时,关闭约束,这将围绕文本包装框架。





autolayout