web-dev-qa-db-ja.com

入力中にUITextFieldのサイズを変更します(Autolayoutを使用)

2つのUITextFields(ボーダーなし)を持つUITableViewCellがあります。次の制約は、水平レイアウトを設定するために使用されます。

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

誰かが私が何を失っているのか知っていますか?この作品を作るにはどうすればいいですか?

54
Matthias Bauch

これは私のために働くものです:

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

おもしろいのは、これが与えられたときに箱から出して動作するはずであるかのように見えることです ドキュメントからのテキスト

さらに、ビューのプロパティが変更され、その変更が固有のコンテンツサイズに影響する場合、ビューはinvalidateIntrinsicContentSizeを呼び出して、レイアウトシステムが変更を認識して再レイアウトできるようにする必要があります。ビュークラスの実装では、組み込みサイズが依存するプロパティの値が変更された場合に、invalidateIntrinsicContentSizeを呼び出すことを確認する必要があります。 たとえば、文字列値が変更された場合、テキストフィールドはinvalidateIntrinsicContentSizeを呼び出します。

私の最良の推測は、テキストフィールドは、編集が終了してからではなく、編集が終了した後にのみinvalidateIntrinsicContentSizeを呼び出すことです。

編集:「これは私のために機能していません」の束。ここでの混乱は、おそらくtextFieldDidChange:ハンドラー。イベントはUIControlEventEditingChangedである必要があります。 IBを使用している場合は、適切なイベントを処理していることを再確認してください。

UITextFieldのサイズも制限できません。制約を使用して位置にロックできますが、幅の制約、または左+右の位置決め制約のセットにより、固有のコンテンツサイズにサイズ変更できなくなります。

58
TomSwift

UITextFieldが最初のレスポンダーステータスを辞任したときにintrinsicContentSizeのみを更新する理由がわかりません。これは、iOS 7とiOS 8の両方で発生します。

一時的な解決策として、intrinsicContentSizeをオーバーライドし、typingAttributesを使用してサイズを決定します。これはleftViewrightViewも考慮します

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

ここでは、キャレットを説明するために幅を広げます

19
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

最初のことから:in Swift 4、UITextFieldのintrinsicContentSizeはメソッドではなく計算変数です。

以下は、特殊なユースケースを持つSwift4ソリューションです。右揃えのテキストフィールドは、特定のピクセル境界まで左にサイズ変更および拡張できます。テキストフィールドはCurrencyTextFieldクラスに割り当てられ、これはITextFieldを継承します。 CurrencyTextFieldは、オブジェクトのintrinsicContentSize、計算された変数を返すためのカスタム動作を提供するために拡張されます。 。
CurrencyTextFieldインスタンスの「editing changed」イベントは、ホストUIViewControllerクラスのrepositionAndResize IBActionメソッドにマップされます。このメソッドは、CurrencyTextFieldインスタンスの現在の組み込みコンテンツサイズを単に無効にします。 CurrencyTextFieldのinvalidateIntrinsicContentSizeメソッド(UITextFieldから継承された)を呼び出すと、オブジェクトの固有のコンテンツサイズを取得しようとします。

CurrencyTextFieldのintrinsicContentSizeゲッターメソッドのカスタムロジックは、サイズを確認するためにCurrentTextFieldのtextプロパティをNSStringに変換する必要があります。これは、CurrencyTextFieldのx座標が規定のピクセル境界より大きい場合、intrinsiveContentSizeとして返します。

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

    }

}
3
sigmonky

何らかの理由で、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が単独で動作している の場合、これは変更を二重にカウントするため、最初に確認する必要があります。

1
Learn OpenGL ES

UITextViewで同様の要件がありました(高さなどを動的に増加させる必要がありました)。

私がやったことはこれに似たものでした:

self.contentViewsuperviewtextFieldです

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

また、要件を考慮して、updateConstraintsUITextViewsuperviewメソッドをオーバーライドする必要がありました。

そのソリューションを選択する場合(より細かく調整されたアプローチの場合があります)、次のことができます。

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

前述のように、より細かく調整されたアプローチが必要なため、オーバーライドオプションが使用されました。

さらに、いつものように、発生する可能性があると考えられるEdgeのケース(値のサイズ設定に関して)をチェックしていることを確認する必要があります。

お役に立てれば!

乾杯!

1
codeBearer

私にとっても、テキストフィールドにターゲットを追加しました(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