如何设置输入附件视图的高度动画?

时间:2017-11-06 17:59:11

标签: ios uiviewanimation intrinsic-content-size

在动画输入附件视图的高度时,我遇到了奇怪的行为。我做错了什么?

我使用单个子视图创建UIInputView子类(InputView)。 InputView及其intrinsicContentSize的高度由子视图控制。 InputViewisVisibletrue为50像素,isVisible为假时为{0}高。

import UIKit

class InputView: UIInputView {
    private let someHeight: CGFloat = 50.0, zeroHeight: CGFloat = 0.0
    private let subView = UIView()
    private var hide: NSLayoutConstraint?, show: NSLayoutConstraint?

    var isVisible: Bool {
        get {
            return show!.isActive
        }
        set {
            // Always deactivate constraints before activating conflicting ones
            if newValue == true {
                hide?.isActive = false
                show?.isActive = true
            } else {
                show?.isActive = false
                hide?.isActive = true
            }
        }
    }

    // MARK: Sizing

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return CGSize(width: size.width, height: someHeight)
    }

    override var intrinsicContentSize: CGSize {
        return CGSize.init(width: bounds.size.width, height: subView.bounds.size.height)
    }

    // MARK: Initializers

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

    override init(frame: CGRect, inputViewStyle: UIInputViewStyle) {
        super.init(frame: frame, inputViewStyle: inputViewStyle)

        addSubview(subView)
        subView.backgroundColor = UIColor.purple

        translatesAutoresizingMaskIntoConstraints = false
        subView.translatesAutoresizingMaskIntoConstraints = false

        subView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
        subView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        subView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        subView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true

        show = subView.heightAnchor.constraint(equalToConstant: someHeight)
        hide = subView.heightAnchor.constraint(equalToConstant: zeroHeight)
        hide?.isActive = true
    }
}

当按下按钮时,主机视图控制器在一秒动画块中切换isVisible

import UIKit

class MainViewController: UIViewController {
    let testInputView = InputView.init(frame: .zero, inputViewStyle: .default)

    @IBAction func button(_ sender: AnyObject) {
        UIView.animate(withDuration: 1.0) {
            let isVisible = self.testInputView.isVisible
            self.testInputView.isVisible = !isVisible
            self.testInputView.layoutIfNeeded()
        }
    }

    override var canBecomeFirstResponder: Bool {
        return true
    }

    override var inputAccessoryView: UIView? {
        return testInputView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

isVisible设置为true时,我希望输入附件视图从屏幕底部平滑增长,并在设置isVisible时平滑缩小到屏幕按钮到false。相反,只要isVisibletrue并且输入附件视图从其框架的中心增长,键盘背景叠加就会显示为完整的50像素高度。

The purple box is not growing from the bottom of the screen and is surrounded by the keyboard overlay background

缩小时,输入附件视图会在平稳地继续动画之前立即失去一些高度。

我创建了一个显示此意外行为的input accessory view demonstration project

1 个答案:

答案 0 :(得分:1)

这将为您提供正确的动画:

    UIView.animate(withDuration: 1.0) {
        let isVisible = self.testInputView.isVisible
        self.testInputView.isVisible = !isVisible
        self.testInputView.superview?.superview?.layoutIfNeeded()
    }

然而,如果Apple更改设计,调用superview绝不是一个好习惯。所以可能会有更好的答案。

这是超视图所代表的内容:

print(testInputView.superview) // UIInputSetHostView

print(testInputView.superview?.superview) // UIInputSetContainerView

编辑:添加了更安全的解决方案

我对UIInputView不太熟悉。但是,在不调用超级视图的情况下解决它的一种方法是仅对子视图的高度变化进行动画处理:

第1步:
将isVisible移动到动画块之外。

@IBAction func button(_ sender: AnyObject) {
    let isVisible = self.testInputView.isVisible
    self.testInputView.isVisible = !isVisible
    UIView.animate(withDuration: 1.0) {
        self.testInputView.layoutIfNeeded()
    }
}

第2步: 在InputView中创建一个新方法,它改变InputView的高度约束而不是intrinsicContentSize。

private func updateHeightConstraint(height: CGFloat) {
    for constraint in constraints {
        if constraint.firstAttribute == .height {
            constraint.constant = height
        }
    }
    self.layoutIfNeeded()
}

第3步: 并在setter中调用该方法。

if newValue == true {
     updateHeightConstraint(height: someHeight)
     hide?.isActive = false
     show?.isActive = true
} else {
     updateHeightConstraint(height: zeroHeight)
     show?.isActive = false
     hide?.isActive = true
}

第4步: 最后在init中进行了一些更改。

override init(frame: CGRect, inputViewStyle: UIInputViewStyle) {
    super.init(frame: frame, inputViewStyle: inputViewStyle)

    addSubview(subView)

    backgroundColor = .clear
    subView.backgroundColor = UIColor.purple

    subView.translatesAutoresizingMaskIntoConstraints = false

    subView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
    subView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
    subView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true

    show = subView.heightAnchor.constraint(equalToConstant: someHeight)
    hide = subView.heightAnchor.constraint(equalToConstant: zeroHeight)
    hide?.isActive = true
}

<强>结论: 在动画紫色子视图的高度之前,InputView中的这个结果会改变它的高度。唯一的缺点是UIInputView,默认情况下有某种灰色背景,无法更改为Clear。但是,您可以使用与VC相同的backgroundColor。

但是如果您改为使用常规UIView作为InputAccessoryView,则默认为UIColor.clear。比第一次&#34;跳跃&#34;不会被注意到。