在iOS 8中更改inputAccessoryView的帧
长期潜伏者-第一次海报 我在用在iOS 8中更改inputAccessoryView的帧,ios,objective-c,ios8,inputaccessoryview,Ios,Objective C,Ios8,Inputaccessoryview,长期潜伏者-第一次海报 我在用UITextView重新创建一个条时遇到了问题,就像WhatsApp所做的那样 我正在使用一个自定义的UIView子类,并在以下位置惰性地实例化它: -(UIView*)inputAccessoryView 并在以下时间返回YES: -(BOOL)可以成为第一响应者 现在,我想在UITextView增大时更改inputAccessoryView的大小。在iOS 7上,我只需更改所述视图的框架大小(而不是其原点),然后调用reloadInputViews,它就会工作:
UITextView
重新创建一个条时遇到了问题,就像WhatsApp所做的那样
我正在使用一个自定义的UIView
子类,并在以下位置惰性地实例化它:
-(UIView*)inputAccessoryView
并在以下时间返回YES:
-(BOOL)可以成为第一响应者
现在,我想在UITextView
增大时更改inputAccessoryView
的大小。在iOS 7上,我只需更改所述视图的框架大小(而不是其原点),然后调用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
编辑:完整的源代码是可用的@我已经在这个问题上碰壁很久了,因为行为从iOS 7变为iOS 8。我尝试了一切,直到最明显的解决方案对我有效:
inputAccessoryView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
哼 不幸的是,iOS8向inputAccessoryView添加了一个私有高度约束,而该约束不是公共的 我建议在附件视图的框架应该更改时重新创建附件视图,并调用reloadInputViews以便安装新的附件视图
这就是我所做的,它按预期工作。为了解决这个问题,我使用了inputAccessoryView.autoresizingMask=UIViewAutoresizingFlexibleHeight 但这当然导致了我的textview崩溃。
因此,将约束添加到工具栏并在必要时更新它,或者将约束添加到文本视图本身并更新它对我来说很有效。是的,iOS8将私有高度约束添加到inputAccessoryView 考虑到重新创建整个inputAccessoryView并替换旧视图可能是非常昂贵的操作,您可以在重新加载输入视图之前删除约束
[inputAccessoryView removeConstraints:[inputAccessoryView constraints]];
[textView reloadInputViews];
另一个解决方法是枚举所有约束并设置新的高度值,这是一个100%有效且非常简单的解决方案。下面是一些C#代码(xamarin):
问题在于,在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;
}
}
}
首先,获取inputAccessoryView并设置为零
UIView *inputAccessoryView = yourTextView.inputAccessoryView;
yourTextView.inputAccessoryView = nil;
[yourTextView reloadInputViews];
然后设置框架和布局
inputAccessoryView.frame = XXX
[inputAccessoryView setNeedsLayout];
[inputAccessoryView layoutIfNeeded];
上次设置新inputAccessoryView并重新加载
yourTextView.inputAccessoryView = inputAccessoryView;
[yourTextView reloadInputViews];
这是一个完整的、自包含的解决方案(感谢@johnyc和@Joãonnes为我指明了正确的方向,@stigi为
内部内容的更改设置动画):
总结一下JohnnyC的做法:将您的InputAccessoryView的自动重新设置为。flexibleHeight
,计算其内部内容大小
,然后让框架完成其余工作
完整代码,为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()
}
}
好的,至少知道这一点很好!非常感谢你的帮助!您是否介意分享处理视图大小调整和移动的代码?正如我在评论中所说:当输入附件视图应该更改其框架时,请将其替换。我正在做与您完全相同的事情。我想我不会想到这个。太神了我将您的建议与-(CGSize)intrinsicContentSize结合使用,现在它可以工作了。只有那一行代码不起作用。然后在进行更改后,在附件视图上调用-invalidateIntriseContentSize。这是一个很好的解决方案!在我的回答中有一个完整的代码示例:“preciate!这是最好的索尔。我从来没有看到过你的答案(我得了237分,而且已经这样做了4年;显然我不太在乎“复制”你的答案)。此外,您的答案是C#(iOS客户端不常见),无法强制inputAccessoryView进行布局。在我找到您的答案之前,您已经为这个问题苦苦挣扎了好几天。我认为这也是更好的解决方案,但您是否已经解决了在动画块内调用可以避免触发键盘通知的问题?因此,无法调整滚动视图位置…@Omer不,我不记得有过这样的问题:(…可能它与iOS 9有关,很奇怪。谢谢,对我来说很有用!在Swift 3中有了一个小小的变化,intristicContentSize不再是一个函数,现在它是一个变量。@ChristianSchober感谢您注意到这一点,我更新了Swift 3的代码。
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()
}
}
}
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()
}
}