在 iOS 8 中动态调整 inputAccessoryView 的大小

Resize inputAccessoryView dynamically in iOS 8

基本上我试图在 inputAccessoryView 属性.

中创建一个调整大小的 UITextView

我有一个 viewController,方法 canBecomeFirstResponder 返回 true 和一个视图,我通过自定义 class(从 XIB 获取它)实例化。在此视图中有一个 UITextView。

我尝试从 class 内部调整完整的 inputAccessoryView 的大小。我尝试了几种方法:设置框架方向,尝试使用高度限制。它似乎只调整了一半:

这基本上就是我想要的(有或没有自动布局,但在 iOS 7/8+ 中工作):

class MessageInputAccessory: UIView, UITextViewDelegate
{
    @IBOutlet weak var textView: UITextView!

    func textViewDidChange(textView: UITextView)
    {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))

        self.frame.size.height = contentSize.height + 20

        self.textView.reloadInputViews()
    }
}

我找到了这个 post:Changing the frame of an inputAccessoryView in iOS 8。说明正在创建一个自动创建的约束。问题是,它不会为我创造约束。无论如何,禁用或启用自动布局(也通过 setTranslatesAutoresizingMaskIntoConstraints())。

它适用于我 iOS7/iOS8:

class MessageInputAccessory: UIView, UITextViewDelegate {
    private var textView: UITextView!
    private var heightConstraint: NSLayoutConstraint?

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

        commonInit()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
    }

    func commonInit() {
        self.userInteractionEnabled = true

        textView = UITextView()
        textView.delegate = self
        textView.bounces = false
        textView.scrollEnabled = false
        textView.layer.cornerRadius = 5
        textView.layer.borderWidth = 1
        textView.layer.borderColor = UIColor.blueColor().CGColor

        self.addSubview(textView)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        textView.frame = self.bounds
    }

    override func addConstraint(constraint: NSLayoutConstraint) {
        self.heightConstraint = constraint

        super.addConstraint(constraint)
    }

    func textViewDidChange(textView: UITextView) {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
        self.frame.size.height = contentSize.height

        if let heightConstraint = self.heightConstraint {
            heightConstraint.constant = self.frame.size.height
        }

        self.textView.reloadInputViews()
    }
}

编辑: 这也适用于 xib (iOS7/iOS8):

class MessageInputAccessory: UIView, UITextViewDelegate {
    @IBOutlet var textView: UITextView!
    @IBOutlet var textViewHeightConstraint: NSLayoutConstraint!
    private var heightConstraint: NSLayoutConstraint?

    override func awakeFromNib() {
        textView.layer.cornerRadius = 5
        textView.layer.borderWidth = 1
        textView.layer.borderColor = UIColor.blueColor().CGColor
    }

    override func layoutSubviews() {
        textViewHeightConstraint.constant = self.bounds.size.height

        super.layoutSubviews()
    }

    override func addConstraint(constraint: NSLayoutConstraint) {
        if constraint.firstItem === self {
            self.heightConstraint = constraint
        }

        super.addConstraint(constraint)
    }

    override func addConstraints(constraints: [AnyObject]) {
        super.addConstraints(constraints)
    }

    func textViewDidChange(textView: UITextView) {
        var contentSize = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
        self.frame.size.height = contentSize.height

        if let heightConstraint = self.heightConstraint {
            heightConstraint.constant = self.frame.size.height
        }

        self.textView.reloadInputViews()
    }

    override func intrinsicContentSize() -> CGSize {
        return self.bounds.size;
    }
}

这是我的xib:

这不是一个很好的解决方案,但它确实有效。如果您知道更好的方法,请告诉我。

一个相当简单的解决方法是根本不调整实际的 inputAccessoryView 大小 - 如果您知道它需要的最大高度,您可以在该最大高度创建它,使其透明,然后添加可以根据需要调整大小的子视图。

使它工作所需的细节是 inputAccessoryView 需要是包含以下内容的 UIView 子类:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    point = [self.innerView convertPoint:point fromView:self];
    return [self.innerView pointInside:point withEvent:event];
}

(其中 self.innerView 是您动态调整大小的子视图)。

这使得它声明子视图内的点击,但传递任何落在它之外的点击。

要注意的主要缺点是键盘通知会给您包含整个最大高度 inputAccessoryView 的框架。因此,如果您使用它们来(例如)调整插图,则需要在那里做一些额外的数学运算。

SWIFT 5

如果有人像我一样一直试图在 Whosebug 中寻找输入附件视图的解决方案,我已经制作了一个简单的项目演示,用于使用 UITextView 动态调整输入附件的大小。它还在 UITextView 顶部带有一个可点击按钮,以演示在 Input Accessory View 顶部添加的视图。所有这些都加载在 XIB 中以便于定制。写在Swift 5,还负责X设备的安全区。

class InputAccessoryView: UIView, UITextViewDelegate {

@IBOutlet weak var textView: UITextView!

@IBOutlet weak var accessoryButtonViewHeightConstraint: NSLayoutConstraint!

@IBOutlet weak var accessoryButtonView: UIView!
// MARK: - Init

required init?(coder aDecoder: NSCoder) {

    super.init(coder: aDecoder)

    self.setup()
}

override init(frame: CGRect) {

    super.init(frame: frame)

    self.setup()
}

private func setup() {

    let view = self.viewFromNibForClass()

    view?.frame = self.bounds

    view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]

    self.accessoryButtonViewHeightConstraint.constant = 0

    if let view = view {

        self.addSubview(view)

        self.textView.translatesAutoresizingMaskIntoConstraints = false
    }
}

override func didMoveToWindow() {

    super.didMoveToWindow()

    if let window = self.window {

        self.bottomAnchor.constraint(lessThanOrEqualTo: window.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }
}

private func viewFromNibForClass() -> UIView? {

    let bundle = Bundle(for: type(of: self))

    let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)

    let view = nib.instantiate(withOwner: self, options: nil).first as? UIView

    return view
}

func textViewDidChange(_ textView: UITextView) {

    self.accessoryButtonViewHeightConstraint.constant = 40

    self.accessoryButtonView.alpha = 1

    self.updateHeight(textView.contentSize.height + 20 + self.accessoryButtonViewHeightConstraint.constant)
}

private func updateHeight(_ height: CGFloat) {

    for constraint in self.constraints where constraint.firstAttribute == .height {

        constraint.constant = height
    }
}

@IBAction func accessoryButtonTouchUpInside(_ sender: Any) {

    let height = self.frame.size.height - 40

    self.updateHeight(height)

    self.accessoryButtonViewHeightConstraint.constant = 0
}
}

这是一个用法示例: https://github.com/jaysalvador/InputAccessoryView