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

时间:2013-08-14 15:59:27

标签: iphone cocoa-touch uitextfield autolayout

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

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

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

enter image description here

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

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

在我看来,应该开箱即用。通过为IBAction事件实施UIControlEventEditingChanged,我可以确认输入实际上更改了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));
}

有人知道我错过了什么吗?我有什么办法让这项工作成功?

9 个答案:

答案 0 :(得分:58)

这对我有用:

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

有趣的是,考虑到text from the docs,它似乎应该开箱即用:

  

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

我最好的猜测是,文本字段仅在编辑完成后调用invalidateIntrinsicContentSize,而不是在编辑过程中。

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

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

答案 1 :(得分:19)

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

作为临时解决方案,我覆盖typingAttributes并使用leftView确定大小。这也考虑了rightView// 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 }

{{1}}

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

答案 2 :(得分:4)

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

InvalidOperationException

答案 3 :(得分:3)

首先要做的事情:在Swift 4中,UITextField的intrinsicContentSize是一个计算变量,而不是一个方法。

以下是具有专用用例的Swift4解决方案。右对齐文本字段可以调整大小并向左延伸到某个像素边界。 文本字段分配给 CurrencyTextField类,该类继承自 UITextField 。然后扩展CurrencyTextField以提供自定义行为,以返回对象的 intrinsicContentSize ,一个计算变量。 。
CurrencyTextField实例的“编辑已更改”事件将映射到其主机UIViewController类中的 repositionAndResize IBAction方法。此方法只是使CurrencyTextField实例的当前内在内容大小无效。调用CurrencyTextField的 invalidateIntrinsicContentSize 方法(继承形式为UITextField)会触发尝试获取对象的内在内容大小。

CurrencyTextField的intrinsicContentSize getter方法中的自定义逻辑必须将CurrentTextField的text属性转换为NSString,以确定其大小,如果CurrencyTextField的x坐标大于规定的像素边界,则返回为intrinsicContentSize。

class CurrencyTextField:UITextField {

}

extension CurrencyTextField {


     override open var intrinsicContentSize: CGSize {

        if isEditing {
            let string = (text ?? "") as NSString
            let stringSize:CGSize = string.size(withAttributes: 
             [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 
             19.0)])
             if self.frame.minX > 71.0 {
                return stringSize
             }
       }

       return super.intrinsicContentSize
    }


}

class TestController: UIViewController {

    @IBAction func repositionAndResize(_ sender: CurrencyTextField) {
        sender.invalidateIntrinsicContentSize()
    }


    override func viewDidLoad() {
        super.viewDidLoad()

    }

}

答案 4 :(得分:1)

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

我所做的与此类似:

考虑self.contentViewsuperview

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

另外,考虑到我的要求,我不得不覆盖updateConstraints UITextView的{​​{1}}方法。

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

superview

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

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

希望这有帮助!

干杯!

答案 5 :(得分:1)

出于某种原因,调用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 is working by itself,这将最终重复计算更改,以便首先检查。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

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

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

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

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

答案 8 :(得分:0)

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

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