web-dev-qa-db-ja.com

iOS 8でのinputAccessoryViewのフレームの変更

久しぶり-初めてのポスター!

WhatsAppのようにUITextViewでバーを再作成しているときに問題が発生しました。

私はカスタムUIViewサブクラスを使用しており、それを遅延してインスタンス化しています:

- (UIView *)inputAccessoryView

そしてYESを返します:

- (BOOL)canBecomeFirstResponder

ここで、inputAccessoryViewのサイズが大きくなったときに、UITextViewのサイズを変更します。 iOS 7では、上記のビューのフレームのサイズを変更します-それはOriginではありません-次にreloadInputViewsを呼び出すと機能します。キーボード。

ただし、iOS 8では、これは機能しません。これを機能させる唯一の方法は、フレームの原点も負の値に変更することです。これは、いくつかの奇妙なバグを作成することを除いて、問題ありません。たとえば、テキストを入力すると、UIViewは「元の」フレームに戻ります。

何か足りないものはありますか? WhatsAppがinputAccessoryViewを使用するのはかなり確実です。これは、ドラッグ時にキーボードを閉じる方法が原因です。最新バージョンのアプリでのみです。

あなたが私を助けることができるかどうか私に知らせてください!または、テストが必要な場合は、実行してほしい!

ありがとうございました! :)

ところで、これがcomposeBarというカスタムUIViewの高さを更新するために使用しているコードです。

// ComposeBar frame size
CGRect frame = self.composeBar.frame;
frame.size.height += heightDifference;
frame.Origin.y -= heightDifference;
self.composeBar.frame = frame;
[self.composeBar.textView reloadInputViews]; // Tried with this
[self reloadInputViews];                     // and this

編集:完全なソースコードが利用可能@ https://github.com/manuelmenzella/SocketChat-iOS

27
Manuel Menzella

IOS 7からiOS 8に動作が変わったため、私はかなり長い間、この壁に頭をぶつけてきました。すべての最も明白な解決策が機能するまで、すべてを試しました。

inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

ああ!

33
JohnnyC

JohnnyCの要約 answer :inpitAccessoryViewのautoresizingMask.flexibleHeightに設定し、そのintrinsicContentSizeを計算して、残りをフレームワークに任せます。

完全なコード、Swift 3用に更新:

class InputAccessoryView: UIView, UITextViewDelegate {

    let textView = UITextView()

    override var intrinsicContentSize: CGSize {
        // Calculate intrinsicContentSize that will fit all the text
        let textSize = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: CGFloat.greatestFiniteMagnitude))
        return CGSize(width: bounds.width, height: textSize.height)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        // This is required to make the view grow vertically
        autoresizingMask = .flexibleHeight

        // Setup textView as needed
        addSubview(textView)
        textView.translatesAutoresizingMaskIntoConstraints = false
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[textView]|", options: [], metrics: nil, views: ["textView": textView]))
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[textView]|", options: [], metrics: nil, views: ["textView": textView]))

        textView.delegate = self

        // Disabling textView scrolling prevents some undesired effects,
        // like incorrect contentOffset when adding new line,
        // and makes the textView behave similar to Apple's Messages app
        textView.isScrollEnabled = false
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: UITextViewDelegate

    func textViewDidChange(_ textView: UITextView) {
        // Re-calculate intrinsicContentSize when text changes
        invalidateIntrinsicContentSize()
    }

}
17
maxkonovalov

問題は、iOS 8では、inputAccessoryViewの高さを初期フレームの高さに等しく設定するNSLayoutConstraintが自動的にインストールされることです。レイアウトの問題を修正するには、その制約を目的の高さに更新してから、inputAccessoryViewにレイアウトして配置する必要があります。

- (void)changeInputAccessoryView:(UIView *)inputAccessoryView toHeight:(CGFloat)height {
    for (NSLayoutConstraint *constraint in [inputAccessoryView constraints]) {
        if (constraint.firstAttribute == NSLayoutAttributeHeight) {
            constraint.constant = height;
            [inputAccessoryView layoutIfNeeded];
            break;
        }
    }
}
5
Rahul Jaswa

これが完全な自己完結型の解決策です(@ JohnnyCに感謝し、@JoãoNunes正しい方向に向けてくれます@ stigifor explaining アニメーション化する方法intrinsicContent変更):

class InputAccessoryView: UIView {

    // InputAccessoryView is instantiated from nib, but it's not a requirement
    override func awakeFromNib() {
        super.awakeFromNib()        
        autoresizingMask = .FlexibleHeight
    }

    override func intrinsicContentSize() -> CGSize {
        let exactHeight = // calculate exact height of your view here
        return CGSize(width: UIViewNoIntrinsicMetric, height: exactHeight)
    }

    func somethingDidHappen() {
        // invalidate intrinsic content size, animate superview layout        
        UIView.animateWithDuration(0.2) {
            self.invalidateIntrinsicContentSize()
            self.superview?.setNeedsLayout()
            self.superview?.layoutIfNeeded()
        }
    }
}
5

100%機能する非常にシンプルなソリューションは、すべての制約を列挙して新しい高さの値を設定することです。これは、いくつかのC#コード(xamarin)です。

foreach (var constraint in inputAccessoryView.Constraints)
{
    if (constraint.FirstAttribute == NSLayoutAttribute.Height)
    {
        constraint.Constant = newHeight;
    }
}
3

はい、iOS8はinputAccessoryViewにプライベート高さ制約を追加します。

InputAccessoryView全体を再作成して古いものを置き換えることは非常にコストのかかる操作になる可能性があることを考慮に入れると、入力ビューをリロードする前に制約を削除するだけで済みます

[inputAccessoryView removeConstraints:[inputAccessoryView constraints]];
[textView reloadInputViews];

ちょうど別の回避策

1

残念ながら、iOS8ではinputAccessoryViewにプライベートの高さ制約が追加されており、この制約はnot publicです。

フレームを変更する必要があるときにアクセサリビューを再作成し、reloadInputViewsを呼び出して新しいビューがインストールされるようにすることをお勧めします。

これは私が行うことであり、期待どおりに機能します。

1
Gwendal Roué

これを修正するために、inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;を使用しました。

しかしもちろん、これにより私のテキストビューが崩壊しました。したがって、ツールバーに制約を追加して、必要に応じて更新するか、または制約をテキストビュー自体に追加して更新するとうまくいきました。

0
Felipe

フリスト、inputAccessoryViewを取得してnilを設定

UIView *inputAccessoryView = yourTextView.inputAccessoryView;
yourTextView.inputAccessoryView = nil;
[yourTextView reloadInputViews];

次に、フレームとレイアウトを設定します

inputAccessoryView.frame = XXX
[inputAccessoryView setNeedsLayout];
[inputAccessoryView layoutIfNeeded];

最後に新しいinputAccessoryViewを再度設定して再読み込み

yourTextView.inputAccessoryView = inputAccessoryView;
[yourTextView reloadInputViews];
0
maquannene