it-swarm.com.ru

Изменение размера UITextField при наборе текста (с помощью Autolayout)

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

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

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

enter image description here

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

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

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

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

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

53
Matthias Bauch

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

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

Интересно, что кажется, что это ДОЛЖНО работать из коробки, учитывая этот текст из документов :

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

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

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

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

54
TomSwift

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

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

17
onmyway133

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

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

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

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

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

    }

}
2
sigmonky

У меня было похожее требование с 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];
}

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

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

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

Ура!

1
codeBearer

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

0
Learn OpenGL ES

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

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

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

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

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

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

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

0
bauerMusic

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

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