如何为输入附件视图的高度设置动画?
How do I animate the height of an input accessory view?
我在为输入附件视图的高度设置动画时遇到奇怪的行为。我做错了什么?
我创建了一个带有单个子视图的 UIInputView
子类 (InputView
)。 InputView
及其 intrinsicContentSize
的高度由子视图控制。当 isVisible
为 true
时 InputView
为 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
。相反,只要 isVisible
为 true
并且输入附件视图从其框架的 中心 增长,键盘背景覆盖就会以完整的 50 像素高度出现。
缩小时,输入附件视图会立即失去一些高度,然后才能流畅地继续动画。
我创建了一个 input accessory view demonstration project 来显示这种意外行为。
这将为您提供正确的动画:
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.
相同的背景颜色
但是,如果您改为使用常规 UIView 作为 InputAccessoryView,则默认为 UIColor.clear。比第一个"jump"不会被注意到。
我在为输入附件视图的高度设置动画时遇到奇怪的行为。我做错了什么?
我创建了一个带有单个子视图的 UIInputView
子类 (InputView
)。 InputView
及其 intrinsicContentSize
的高度由子视图控制。当 isVisible
为 true
时 InputView
为 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
。相反,只要 isVisible
为 true
并且输入附件视图从其框架的 中心 增长,键盘背景覆盖就会以完整的 50 像素高度出现。
缩小时,输入附件视图会立即失去一些高度,然后才能流畅地继续动画。
我创建了一个 input accessory view demonstration project 来显示这种意外行为。
这将为您提供正确的动画:
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.
相同的背景颜色但是,如果您改为使用常规 UIView 作为 InputAccessoryView,则默认为 UIColor.clear。比第一个"jump"不会被注意到。