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

}