57

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

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

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

在此处输入图像描述

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

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

在我看来,这应该是开箱即用的。通过实现一个事件IBActionUIControlEventEditingChanged我可以确认输入实际上改变intrinsicContentSize了 UITextField 的。

但是,在 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));
}

有人知道我错过了什么吗?我可以尝试什么来完成这项工作?

4

9 回答 9

60

这对我有用:

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

有趣的是,鉴于文档中的以下文本,它似乎应该开箱即用:

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

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

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

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

于 2013-08-16T19:59:19.900 回答
20

我不知道为什么只在它辞去第一响应者状态UITextField时才更新它。intrinsicContentSize这发生在 iOS 7 和 iOS 8 上。

作为临时解决方案,我intrinsicContentSize使用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
  }

在这里,我将插入符号的范围扩大

于 2015-01-18T03:17:48.530 回答
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()
    }
}
于 2016-07-22T19:16:33.737 回答
3

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

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

CurrencyTextField 的intrinsicContentSize getter 方法中的自定义逻辑必须将CurrentTextField 的文本属性转换为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()

    }

}
于 2018-03-21T14:54:08.543 回答
2

我对 a 有类似的要求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));
}

另外,考虑到我的要求,我不得不重写updateConstraintsUITextView的 's方法superview

如果您选择选择该解决方案(也许选择更精细的方法),您可以执行以下操作:

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

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

此外,与往常一样,您需要确保检查您认为可能遇到的任何边缘情况(在大小值方面)。

希望这可以帮助!

干杯!

于 2013-08-16T19:10:25.880 回答
2

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

并不总是需要这个解决方案。如果invalidateIntrinsicContentSize可行,那么需要做的就是在文本更改时添加一个调用,就像在其他答案中一样。如果这不起作用,那么这是我发现的一个解决方案(改编自此处),它也适用于明确的控制:

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它自己工作,这将最终重复计算更改,因此需要先检查。

于 2017-04-25T23:08:42.390 回答
0

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

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

于 2016-10-19T08:07:52.967 回答
0

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

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

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

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

    textField.backgroundColor = [UIColor grayColor];
    return YES;
}
于 2016-11-16T09:58:31.640 回答
0

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

if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
{
    self.edgesForExtendedLayout = UIRectEdgeNone;
}
于 2017-03-07T06:41:06.647 回答