在iOS 8中更改inputAccessoryView的框架

很久以前的潜伏者 – 第一次海报!

我有一个问题,而像WhatsApp那样用UITextView重新创build一个栏。

我正在使用一个自定义的UIView子类,并懒洋洋地实例化它:

- (UIView *)inputAccessoryView

并返回YES:

- (BOOL)canBecomeFirstResponder

现在,我想在UITextView的大小增长时更改inputAccessoryView的大小。 在iOS 7上,我只是简单地改变视图框架的大小,而不是原始视图的大小,然后调用reloadInputViews ,它将工作:视图将向上移动,以便在键盘上完全可见。

但是在iOS 8上,这不起作用。 使其工作的唯一方法是也将框架的起源改为负值。 这样会很好,除了它会产生一些奇怪的错误:例如, UIView在input任何文本时会返回到“原始”框架。

有什么我失踪? 我很确定WhatsApp使用inputAccessoryView因为他们closures拖动键盘的方式 – 只在最新版本的应用程序。

请让我知道,如果你能帮助我! 或者如果有任何testing,你想让我跑步!

谢谢! 🙂

顺便说一句,这里是我用来更新自定义UIView称为composeBar的高度的composeBar

 // 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.我尝试了一切,直到最明显的解决scheme都为我工作:

 inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 

咄!

总结JohnnyC的答案 :将你的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中,自动安装一个NSLayoutConstraint,它将inputAccessoryView的高度设置为初始帧高度。 为了解决布局问题,您需要将该约束更新到所需的高度,然后指示您的inputAccessoryView展开。

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

这是一个完整的,自包含的解决scheme(感谢@JohnnyC@JoãoNunes指引我朝着正确的方向, @stigi 解释如何为intrinsicContent变化制作animation):

 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%的工作和非常简单的解决scheme是枚举所有约束,并设置新的高度值。 这里是一些C#代码(xamarin):

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

不幸的是,iOS8向inputAccessoryView添加了一个私有高度约束,并且这个约束公开。

我build议在框架应该改变时重新创build附件视图,并调用reloadInputViews,以便安装新框架。

这就是我所做的,它按预期工作。

是的,iOS8添加一个私人的高度限制到inputAccessoryView。

考虑到重新创build整个inputAccessoryView并replace旧的是可以真正的昂贵的操作,你可以在重新加载input视图之前删除约束

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

只是另一个解决方法

为了解决这个问题,我使用了inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

但当然这导致我的文本视图崩溃。 所以,当我必须添加一个约束到工具栏并更新它,或者将约束添加到textview本身并更新它为我工作。

首先,获取inputAccessoryView并设置nil

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

然后设置框架和布局

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

最后再次设置新的inputAccessoryView并重新加载

 yourTextView.inputAccessoryView = inputAccessoryView; [yourTextView reloadInputViews];