如何为输入附件视图的高度设置动画?
Posted
技术标签:
【中文标题】如何为输入附件视图的高度设置动画?【英文标题】:How do I animate the height of an input accessory view? 【发布时间】:2017-11-06 17:59:11 【问题描述】:在为输入附件视图的高度设置动画时,我遇到了奇怪的行为。我做错了什么?
我创建了一个带有单个子视图的 UIInputView
子类 (InputView
)。 InputView
及其intrinsicContentSize
的高度由子视图控制。当isVisible
为true
时,InputView
为 50 像素高,当isVisible
为 false 时为 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 来显示这种意外行为。
【问题讨论】:
【参考方案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,它默认有某种灰色背景,不能更改为清除。但是,您可以使用与 VC 相同的 backgroundColor。
但是,如果您改为使用常规 UIView 作为 InputAccessoryView,则默认为 UIColor.clear。不会注意到第一次“跳跃”。
【讨论】:
我不接受这是最好的答案还希望有人揭示更安全的解决方法,但这很好用。关于如何查找超级视图的提示也非常有用。 不幸的是,高度约束方法对我来说没有正确的动画。最好的解决方案是使用superview?.superview
,尽管在 ios 14 上,在键盘动画期间调用这可能会导致无限循环。在这种情况下,切换到 superview?.superview?.superview
(这是层次结构中的顶视图)可以正常工作。以上是关于如何为输入附件视图的高度设置动画?的主要内容,如果未能解决你的问题,请参考以下文章