Swift 约束在调整约束高度后无法移回屏幕底部
Swift constraints unable to move back to bottom of screen after adjusting constraint height
我有一个使用自动布局的按钮位于屏幕底部。我正在尝试使按钮调整位置始终位于键盘上方。到目前为止,我已经能够让按钮向上移动,但我无法让按钮的位置重置。
我完全以编程方式做所有事情,我根本不使用故事板。没有出口的解决方案将不胜感激。
我的viewDidLoad
函数(只展示与按钮移动相关的逻辑)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardMovedUp(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardMovedDown(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
keyboardMovedUp
函数
@objc
func keyboardMovedUp(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let viewEndFrame = view.convert(endFrame!, from: view.window)
let endFrameY = endFrame?.origin.y ?? 0
let animationRate = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let animationCurveRawNSM = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSM?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve: UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
self.moveUp = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -viewEndFrame.origin.y)
self.moveUp?.isActive = true
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
和我的 keyboardMovedDown
函数
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
self.moveUp?.isActive = false
self.moveDown = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20)
self.moveDown?.isActive = true
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
使用此代码,当键盘显示时,按钮会向上移动,但一旦键盘被重置,按钮不会移动位置。
我可以通过修改按钮的 origin.y
参数使代码正常工作,但我听说这会导致自动版式出现兼容性问题。
编辑:@IchBinStudenten 这是我当前代码经过您的更改后的样子
func viewDidLoad() {
self.moveUp = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20)
self.moveUp?.isActive = true
}
@objc
func keyboardMoved(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let viewEndFrame = view.convert(endFrame!, from: view.window)
let endFrameY = endFrame?.origin.y ?? 0
let animationRate = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let animationCurveRawNSM = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSM?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve: UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
self.moveUp?.constant = -viewEndFrame.height - 20
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
self.moveUp?.constant = 0
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
使用1个约束就可以达到你想要的效果。无论如何,您正在使用一个约束。设置 self.moveUp
在键盘向上移动时激活,并更改其值
self.moveUp?.constant = -20.0
当键盘向下移动时。为了使代码更简洁,只需在 class 中创建一些变量来保留对 self.continueButton.bottomAnchor.constraint(equalTo:self.view.bottomAnchor)
的引用,这样您就可以修改它的 constant 属性 只要。例如:
let keyboardConstraint: NSLayoutConstraint
override func viewDidLoad() {
self.keyboardConstraint = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
self.keyboardConstraint.isActive = true
}
@objc
func keyboardMovedUp(notification: Notification) {
/// ...your code
self.keyboardConstraint.constant = -viewEndFrame.origin.y
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
self.keyboardConstraint.constant = -20.0
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
另一种选择是通过 CocoaPods 安装 IQKeyboardManager,它会为您管理这一切。
如果这能解决您的问题,请告诉我。
为方便起见,将约束设为一个实例属性:
private lazy var continueButtonBottomAnchor = continueButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -bottomSafeArea)
将其添加为子视图时在 loadView
中激活它:
continueButtonBottomAnchor.isActive = true
bottomSafeArea
是计算得到的 属性 因为我假设您想将按钮放置在底部安全区域。计算属性不存储值(仅计算),因此您可以在这些通知中使用正确的值延迟访问它们。
@objc private func keyboardMovedUp(_ notification: Notification) {
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
continueButtonBottomAnchor.constant = -keyboardFrame.cgRectValue.height
UIView.animate(withDuration: 0, animations: {
self.view.layoutIfNeeded()
})
}
@objc private func keyboardMovedDown() {
continueBUttonBottomAnchor.constant = -bottomSafeArea // using our computed property
UIView.animate(withDuration: 0, animations: {
self.view.layoutIfNeeded()
})
}
与接受的答案不同,不要添加您自己的或尝试获取动画参数(持续时间和时间);通过在通知内设置动画,这些参数将自动应用,因此您的约束将与键盘完美同步移动。
我有一个使用自动布局的按钮位于屏幕底部。我正在尝试使按钮调整位置始终位于键盘上方。到目前为止,我已经能够让按钮向上移动,但我无法让按钮的位置重置。
我完全以编程方式做所有事情,我根本不使用故事板。没有出口的解决方案将不胜感激。
我的viewDidLoad
函数(只展示与按钮移动相关的逻辑)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardMovedUp(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardMovedDown(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
keyboardMovedUp
函数
@objc
func keyboardMovedUp(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let viewEndFrame = view.convert(endFrame!, from: view.window)
let endFrameY = endFrame?.origin.y ?? 0
let animationRate = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let animationCurveRawNSM = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSM?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve: UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
self.moveUp = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -viewEndFrame.origin.y)
self.moveUp?.isActive = true
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
和我的 keyboardMovedDown
函数
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
self.moveUp?.isActive = false
self.moveDown = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20)
self.moveDown?.isActive = true
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
使用此代码,当键盘显示时,按钮会向上移动,但一旦键盘被重置,按钮不会移动位置。
我可以通过修改按钮的 origin.y
参数使代码正常工作,但我听说这会导致自动版式出现兼容性问题。
编辑:@IchBinStudenten 这是我当前代码经过您的更改后的样子
func viewDidLoad() {
self.moveUp = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20)
self.moveUp?.isActive = true
}
@objc
func keyboardMoved(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let viewEndFrame = view.convert(endFrame!, from: view.window)
let endFrameY = endFrame?.origin.y ?? 0
let animationRate = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue
let animationCurveRawNSM = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSM?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve: UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
self.moveUp?.constant = -viewEndFrame.height - 20
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
self.moveUp?.constant = 0
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
使用1个约束就可以达到你想要的效果。无论如何,您正在使用一个约束。设置 self.moveUp
在键盘向上移动时激活,并更改其值
self.moveUp?.constant = -20.0
当键盘向下移动时。为了使代码更简洁,只需在 class 中创建一些变量来保留对 self.continueButton.bottomAnchor.constraint(equalTo:self.view.bottomAnchor)
的引用,这样您就可以修改它的 constant 属性 只要。例如:
let keyboardConstraint: NSLayoutConstraint
override func viewDidLoad() {
self.keyboardConstraint = self.continueButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
self.keyboardConstraint.isActive = true
}
@objc
func keyboardMovedUp(notification: Notification) {
/// ...your code
self.keyboardConstraint.constant = -viewEndFrame.origin.y
UIView.animate(withDuration: animationRate!, delay: TimeInterval(0), options: animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
@objc
func keyboardMovedDown(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
self.keyboardConstraint.constant = -20.0
UIView.animate(withDuration: 0.25) {
self.view.layoutIfNeeded()
}
}
}
另一种选择是通过 CocoaPods 安装 IQKeyboardManager,它会为您管理这一切。
如果这能解决您的问题,请告诉我。
为方便起见,将约束设为一个实例属性:
private lazy var continueButtonBottomAnchor = continueButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -bottomSafeArea)
将其添加为子视图时在 loadView
中激活它:
continueButtonBottomAnchor.isActive = true
bottomSafeArea
是计算得到的 属性 因为我假设您想将按钮放置在底部安全区域。计算属性不存储值(仅计算),因此您可以在这些通知中使用正确的值延迟访问它们。
@objc private func keyboardMovedUp(_ notification: Notification) {
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
continueButtonBottomAnchor.constant = -keyboardFrame.cgRectValue.height
UIView.animate(withDuration: 0, animations: {
self.view.layoutIfNeeded()
})
}
@objc private func keyboardMovedDown() {
continueBUttonBottomAnchor.constant = -bottomSafeArea // using our computed property
UIView.animate(withDuration: 0, animations: {
self.view.layoutIfNeeded()
})
}
与接受的答案不同,不要添加您自己的或尝试获取动画参数(持续时间和时间);通过在通知内设置动画,这些参数将自动应用,因此您的约束将与键盘完美同步移动。