Изменение размера поля UITextField при вводе текста (с помощью автозапуска)


у меня есть UITableViewCell, который имеет два UITextFields (без границ). Для настройки горизонтальной компоновки используются следующие ограничения.

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

ничего особенного, и работает, как ожидалось.

как вы можете видеть в верхнем поле имеет правильный размер. Нижнее текстовое поле начиналось с пустого текста, и из-за пустого текста оно имеет ширину 80 пунктов. Когда я набираю текст в текстовое поле редактирования, текст прокручивается до слева он не меняет своей ширины.

мне это не нравится, ширина текстового поля должна регулироваться, пока пользователь вводит в это текстовое поле.

на мой взгляд, это должно работать из коробки. Путем реализации IBAction на UIControlEventEditingChanged событие я могу подтвердить, что ввод текста на самом деле изменяет intrinsicContentSize из UITextField.

но хорошо, ширина не меняется, пока текстовое поле больше не является первым ответчиком. Если я помещу курсор в другое текстовое поле задается ширина редактируемого текстового поля. Это немного поздно для того, что я хочу.

эти закомментированные строки-это то, что я пытался без успеха:

- (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 53

9 ответов:

Это то, что работает для меня:

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

что интересно, кажется, что это должно работать из коробки, учитывая это текст из документов:

кроме того, если свойство представления изменяется и это изменение влияет внутренний размер содержимого, представление должно вызывать invalidateIntrinsicContentSize, чтобы система макета заметила изменения и повторной компоновки. В реализации вашего класса представления, вы должны убедиться, что если стоимость любого имущества, на котором внутренний размер зависит от изменений, которые вы вызываете invalidateIntrinsicContentSize. например, текстовое поле вызывает invalidateIntrinsicContentSize при изменении строкового значения.

Я предполагаю, что текстовое поле вызывает invalidateIntrinsicContentSize только после завершения редактирования, а не во время.

Edit: куча "это не работает для меня". Я думаю, что путаница здесь возможно запускающее событие, которое привязано к textFieldDidChange: обработчик. Событие должно быть UIControlEventEditingChanged. Если вы используете IB, дважды проверьте, что вы обрабатываете правильное событие.

The UITextField также не может быть ограничено по размеру. Вы можете заблокировать его в положение с ограничениями, но любое ограничение ширины или набор ограничений левого+правого позиционирования не позволит ему изменить размер до его внутреннего размера содержимого.

Я не знаю, почему 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
  }

здесь я делаю его шире, чтобы учесть каре

вот ответ в 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()
    }
}

у меня было аналогичное требование с UITextView (пришлось динамически увеличивать его высоту и некоторые другие вещи).

то, что я сделал, было чем-то похожим на это:

учитывая self.contentView - это superview на 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 ' 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 была использована мной, потому что мне требовался более тонко настроенный подход.

кроме того, как всегда, вам нужно будет убедиться, что вы проверяете любые крайние случаи (с точки зрения значений размера), которые, по вашему мнению, могут возникнуть.

надеюсь, что это помогает!

Ура!

первым делом: в Swift 4 intrinsicContentSize UITextField является вычисляемой переменной, а не методом.

Ниже представлено решение Swift4 со специализированным вариантом использования. Текстовое поле с выравниванием по правому краю может изменяться и расширяться влево до определенной границы пикселя. Текстовое поле присваивается CurrencyTextField class, который наследует от UITextField. CurrencyTextField затем расширяется, чтобы обеспечить пользовательское поведение для возврата объект intrinsicContentSize, вычисленная переменная. .
В "редактирование изменен " событие экземпляра CurrencyTextField сопоставляется с repositionAndResize IBAction метод В для UIViewController класса. Этот метод просто делает недействительным текущий внутренний размер содержимого экземпляра CurrencyTextField. Вызов CurrencyTextField в invalidateIntrinsicContentSize метод (унаследованная форма UITextField ) запускает попытайтесь получить внутренний размер содержимого объекта.

пользовательская логика в методе получения intrinsicContentSize Currenttextfield должна преобразовать свойство text CurrentTextField в NSString, чтобы определить его размер, который он возвращает как intrinsicContentSize, если координата x Currentextfield больше заданной границы пикселя.

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

    }

}

другое решение для этого-установить ведущие / конечные ограничения на текстовое поле и использовать isActive чтобы включить/выключить их.

при запуске редактирования включите их, если текст центрирован, эффект будет таким же (текст будет автоматически изменяться по мере ввода). При остановке редактирования отключите ограничения, которые обернут рамку вокруг текста.

для меня я также добавил цель в текстовое поле (UIControlEventEditingChanged)

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

Если у вас есть цвет фона, то просто обновите его в методе делегата:

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

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

вы можете добавить этот код в viewDidLoad для решения проблемы.

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

Почему-то, называя 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 работает сам по себе, это будет в конечном итоге двойной подсчет изменения, так что необходимо проверить в первую очередь.