描述
我有一些 UITextField,当其中之一在编辑过程中被键盘覆盖时,我想向上滚动。这里有大量关于 SO 的答案,有多种风格:移动 View (通过更改其框架)、修改约束、使用 UIScrollView 和 UITableView,或使用 UIScrollView 并修改 contentInset。
我决定使用最后一个。这个也是described by Apple ,并且有 Swift version on SO以及described on this blog包括sample project on the GitHub .
部分代码
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
self.activeField = nil
}
func textFieldDidBeginEditing(textField: UITextField) {
self.activeField = textField
}
func keyboardWillShow(notification: NSNotification) {
if let activeField = self.activeField, keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect = self.view.frame
aRect.size.height -= keyboardSize.size.height
if (!CGRectContainsPoint(aRect, activeField.frame.origin)) {
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification) {
let contentInsets = UIEdgeInsetsZero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
问题
我只想要一个简单的修改 - 键盘和编辑字段之间多一点空间。因为开箱即用,它看起来像这样:
我在 scrollRectToVisible
调用中修改了 CGRect
,但它没有改变任何内容。更重要的是,即使使用 scrollRectToVisible
注释掉该行也没有任何效果 - 一切都与以前完全一样(包括向上滚动内容)。在 iOS 9.2 上检查
如果需要,复制是微不足道的 - 只需使用上面的 GitHub 链接下载工作代码并注释掉scrollRectToVisible 行即可。
经过测试的解决方法
我尝试过的解决方法,但不喜欢最终的效果:
- 增加
contentInset
- 用户可以向上滚动超过 contentSize 的内容 - 将
UIKeyboardDidShowNotification
替换为UIKeyboardWillShowNotification
,添加另一个UIKeyboardDidShowNotification
观察者,其中仅包含scrollRectToVisible
- 它可以工作,但随后有两个向上滚动的动画,看起来不太好。
问题
- 为什么更改
contentInset
(没有scrollRectToVisible
调用)会滚动scrollView中的内容?我在任何文档中都没有看到有关此类行为的信息 - 更重要的是 - 该怎么做,将其向上滚动一点,在编辑的文本字段和键盘之间留出一些空间?
我错过了什么?有一些简单的方法可以修复它吗?或者也许使用 scrollView.contentSize
更好,而不是 contentInset
?
最佳答案
我还没有找到为什么 scrollRectToVisible
在上述场景中不起作用,但是我找到了其他有效的解决方案。在提到的苹果文章中Managing the Keyboard最底部有提示
There are other ways you can scroll the edited area in a scroll view above an obscuring keyboard. Instead of altering the bottom content inset of the scroll view, you can extend the height of the content view by the height of the keyboard and then scroll the edited text object into view.
下面的解决方案完全基于扩展内容 View 的高度(scrollView.contentSize
)。它考虑了隐藏键盘时的方向变化和向后滚动。按要求工作 - 事件字段和键盘之间有一些空间,请参见下图
工作代码
我已将完整的工作代码放在 GitHub 上:ScrollViewOnKeyboardShow
var animateContenetView = true
var originalContentOffset: CGPoint?
var isKeyboardVisible = false
let offset : CGFloat = 18
override func viewDidLoad() {
super.viewDidLoad()
for case let textField as UITextField in contentView.subviews {
textField.delegate = self
}
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: #selector(ViewController.keyboardWillBeShown(_:)), name: UIKeyboardWillShowNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(ViewController.keyboardWillBeHidden(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
self.activeField = nil
}
func textFieldDidBeginEditing(textField: UITextField) {
self.activeField = textField
}
func keyboardWillBeShown(notification: NSNotification) {
originalContentOffset = scrollView.contentOffset
if let activeField = self.activeField, keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
var visibleRect = self.scrollView.bounds
visibleRect.size.height -= keyboardSize.size.height
//that's to avoid enlarging contentSize multiple times in case of many UITextFields,
//when user changes an edited text field
if isKeyboardVisible == false {
scrollView.contentSize.height += keyboardSize.height
}
//scroll only if the keyboard would cover a bottom edge of an
//active field (including the given offset)
let activeFieldBottomY = activeField.frame.origin.y + activeField.frame.size.height + offset
let activeFieldBottomPoint = CGPoint(x: activeField.frame.origin.x, y: activeFieldBottomY)
if (!CGRectContainsPoint(visibleRect, activeFieldBottomPoint)) {
var scrollToPointY = activeFieldBottomY - (self.scrollView.bounds.height - keyboardSize.size.height)
scrollToPointY = min(scrollToPointY, scrollView.contentSize.height - scrollView.frame.size.height)
scrollView.setContentOffset(CGPoint(x: 0, y: scrollToPointY), animated: animateContenetView)
}
}
isKeyboardVisible = true
}
func keyboardWillBeHidden(notification: NSNotification) {
scrollView.contentSize.height = contentView.frame.size.height
if var contentOffset = originalContentOffset {
contentOffset.y = min(contentOffset.y, scrollView.contentSize.height - scrollView.frame.size.height)
scrollView.setContentOffset(contentOffset, animated: animateContenetView)
}
isKeyboardVisible = false
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animateAlongsideTransition(nil) { (_) -> Void in
self.scrollView.contentSize.height = self.contentView.frame.height
}
}
deinit {
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
notificationCenter.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
关于ios - 当键盘覆盖输入字段时向上移动 View ,但在它们之间留有一些空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36236352/