久しぶり-初めてのポスター!
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
IOS 7からiOS 8に動作が変わったため、私はかなり長い間、この壁に頭をぶつけてきました。すべての最も明白な解決策が機能するまで、すべてを試しました。
inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
ああ!
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()
}
}
問題は、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;
}
}
}
これが完全な自己完結型の解決策です(@ 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()
}
}
}
100%機能する非常にシンプルなソリューションは、すべての制約を列挙して新しい高さの値を設定することです。これは、いくつかのC#コード(xamarin)です。
foreach (var constraint in inputAccessoryView.Constraints)
{
if (constraint.FirstAttribute == NSLayoutAttribute.Height)
{
constraint.Constant = newHeight;
}
}
はい、iOS8はinputAccessoryViewにプライベート高さ制約を追加します。
InputAccessoryView全体を再作成して古いものを置き換えることは非常にコストのかかる操作になる可能性があることを考慮に入れると、入力ビューをリロードする前に制約を削除するだけで済みます
[inputAccessoryView removeConstraints:[inputAccessoryView constraints]];
[textView reloadInputViews];
ちょうど別の回避策
残念ながら、iOS8ではinputAccessoryViewにプライベートの高さ制約が追加されており、この制約はnot publicです。
フレームを変更する必要があるときにアクセサリビューを再作成し、reloadInputViewsを呼び出して新しいビューがインストールされるようにすることをお勧めします。
これは私が行うことであり、期待どおりに機能します。
これを修正するために、inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;を使用しました。
しかしもちろん、これにより私のテキストビューが崩壊しました。したがって、ツールバーに制約を追加して、必要に応じて更新するか、または制約をテキストビュー自体に追加して更新するとうまくいきました。
フリスト、inputAccessoryViewを取得してnilを設定
UIView *inputAccessoryView = yourTextView.inputAccessoryView;
yourTextView.inputAccessoryView = nil;
[yourTextView reloadInputViews];
次に、フレームとレイアウトを設定します
inputAccessoryView.frame = XXX
[inputAccessoryView setNeedsLayout];
[inputAccessoryView layoutIfNeeded];
最後に新しいinputAccessoryViewを再度設定して再読み込み
yourTextView.inputAccessoryView = inputAccessoryView;
[yourTextView reloadInputViews];