Подтвердить что ты не робот

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

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

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

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

enter image description here

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

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

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

Но хорошо, ширина не меняется до тех пор, пока 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));
}

Кто-нибудь знает, что мне не хватает? Что я могу попытаться сделать этой работой?

4b9b3361

Ответ 1

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

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

Интересно, что кажется, что это СЛЕДУЕТ работать из коробки, учитывая этот текст из документов:

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

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

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

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

Ответ 2

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

Здесь я делаю его более широким для учета каретки

Ответ 3

Вот ответ в Свифт. В качестве бонуса этот фрагмент не сбрасывает текстовое поле, если есть местозаполнитель.

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

Ответ 4

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

Я сделал что-то похожее на это:

Учитывая, что self.contentView является 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));
}

Кроме того, учитывая требования, которые у меня были, мне пришлось переопределить метод updateConstraints моего UITextView 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];
}

Как уже упоминалось, опция переопределения использовалась мной, потому что мне нужен более тонкий подход.

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

Надеюсь, это поможет!

Ура!

Ответ 5

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

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

Ответ 6

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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