为 keyboardWillShowNotification 设置 UIScrollView 的 contentInset 无法正常工作
Setting UIScrollView's contentInset for keyboardWillShowNotification not working properly
我有一个ViewController结构如下((x)
表示级别):
UIViewController (1)
- NavigationBar (2)
- UIScrollView (2)
- UIView (3)
- UITextField (4)
- UITextField (4)
- UITextField (4)
- UITextField (4)
- UIButton (4)
- 级别
4
的所有元素在垂直方向上相互约束,间距为 16。
- 级别
4
的第一个和最后一个元素被限制在 UIView (3
) 的顶部和底部。
- UIView (
3
) 的顶部和底部受限于 UIScrollView (2
)。
- UIScrollView (
2
) 被限制在 NavigationBar 的底部 (2
) 和 superview 的底部 (1
)
- (当然还有必要的水平约束!)
UIView (3
) 有以下约束:
- 所有子视图的前导约束为 0。
- 所有子视图的尾随约束 0。
- Bottom space of 24 to UIButton (should add some extra spacing)
- Top space of 24 to the topmost UITextField (top spacing)
- 第 space 个到 superView (UIScrollView)
- Bottom space of 0 to superView (UIScrollView)
- 'Equal width - minus 32' 到 NavigationBar(所以 — 固定宽度)
在viewController的viewDidLoad
中,我称之为:
registerForKeyboardWillShowNotification(self.scrollView)
registerForKeyboardWillHideNotification(self.scrollView)
其中 registerForKeyboard...ShowNotification
是 UIViewController
的扩展:
extension UIViewController
{
/// Act when keyboard is shown, by adding contentInsets to the scrollView.
func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: keyboardSize.height,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}
/// Act when keyboard is hidden, by removing contentInsets from the scrollView.
func registerForKeyboardWillHideNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: 0,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}
}
但是,当键盘显示时,它并没有插入 scrollView(足够)。我调试了一下,是这样的:
- 普通键盘:
height = 216
- 带建议栏的普通键盘:
height = 260
- iPhone 带建议栏的 X 键盘:
height = 291
我一开始以为建议栏可能是问题所在,但事实并非如此。
在 registerForKeyboardWillShowNotification
中,我将 bottom: keyboardSize.height
更改为 bottom: keyboardSize.height + 30
,这给出了完全相同的结果(我看到按钮的相同部分部分隐藏在键盘后面)。一旦我添加 50 或更多,它最终似乎会产生很小的差异。
- 我尝试了
keyboardDidShowNotification
而不是 keyboardWillShowNotification
,这没有什么区别。
- 我尝试
keyboardFrameBeginUserInfoKey
而不是 keyboardFrameEndUserInfoKey
,这没有什么区别。
我在这里错过了什么?
不幸的是我没能解决这个问题,我不确定为什么它没有按预期工作。
但是,我通过在 UIScrollView 中使用 UIStackView 获得了预期的行为。我使用 this article 作为参考。
UI布局
- UIViewController (1)
- 导航栏(2)
- UI滚动视图 (2)
- UI堆栈视图 (3)
- UI文本字段 (4)
- UI文本字段 (4)
- UI文本字段 (4)
- UI文本字段 (4)
- UI按钮 (4)
UIScrollView
- ScrollView 的
leading
和trailing
被16 约束到superView。
- ScrollView 的
top
被 0 限制在导航栏的底部。
- ScrollView 的
bottom
被 0 限制在 superView 的底部。
UIStackView
- StackView 的
leading
和 trailing
被 0 限制到 scrollView。
- StackView 与 scrollView 的宽度相等。
- StackView 的
top
和 bottom
被 24 限制到滚动视图,以获得所需的导航栏间距,以及按钮和键盘.
- StackView 设置为
axis=vertical
、alignment=fill
、distribution=fill
、spacing=24
。
NavigationBar
NavigationBar 是自定义的 class,它的高度从其内容中得出。这没有设置高度限制,但占位符高度为 100。NavigationBar 将填满整个屏幕。这通过删除占位符高度,并添加具有低优先级的 any 高度约束来解决(在本例中优先级为 1
)。
应用键盘插入的初始代码现在可以工作了。
/// Act when keyboard is shown, by adding contentInsets to the scrollView.
func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: keyboardSize.height,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}
我有一个ViewController结构如下((x)
表示级别):
UIViewController (1)
- NavigationBar (2)
- UIScrollView (2)
- UIView (3)
- UITextField (4)
- UITextField (4)
- UITextField (4)
- UITextField (4)
- UIButton (4)
- 级别
4
的所有元素在垂直方向上相互约束,间距为 16。 - 级别
4
的第一个和最后一个元素被限制在 UIView (3
) 的顶部和底部。 - UIView (
3
) 的顶部和底部受限于 UIScrollView (2
)。 - UIScrollView (
2
) 被限制在 NavigationBar 的底部 (2
) 和 superview 的底部 (1
) - (当然还有必要的水平约束!)
UIView (3
) 有以下约束:
- 所有子视图的前导约束为 0。
- 所有子视图的尾随约束 0。
- Bottom space of 24 to UIButton (should add some extra spacing)
- Top space of 24 to the topmost UITextField (top spacing)
- 第 space 个到 superView (UIScrollView)
- Bottom space of 0 to superView (UIScrollView)
- 'Equal width - minus 32' 到 NavigationBar(所以 — 固定宽度)
在viewController的viewDidLoad
中,我称之为:
registerForKeyboardWillShowNotification(self.scrollView)
registerForKeyboardWillHideNotification(self.scrollView)
其中 registerForKeyboard...ShowNotification
是 UIViewController
的扩展:
extension UIViewController
{
/// Act when keyboard is shown, by adding contentInsets to the scrollView.
func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: keyboardSize.height,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}
/// Act when keyboard is hidden, by removing contentInsets from the scrollView.
func registerForKeyboardWillHideNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: 0,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}
}
但是,当键盘显示时,它并没有插入 scrollView(足够)。我调试了一下,是这样的:
- 普通键盘:
height = 216
- 带建议栏的普通键盘:
height = 260
- iPhone 带建议栏的 X 键盘:
height = 291
我一开始以为建议栏可能是问题所在,但事实并非如此。
在 registerForKeyboardWillShowNotification
中,我将 bottom: keyboardSize.height
更改为 bottom: keyboardSize.height + 30
,这给出了完全相同的结果(我看到按钮的相同部分部分隐藏在键盘后面)。一旦我添加 50 或更多,它最终似乎会产生很小的差异。
- 我尝试了
keyboardDidShowNotification
而不是keyboardWillShowNotification
,这没有什么区别。 - 我尝试
keyboardFrameBeginUserInfoKey
而不是keyboardFrameEndUserInfoKey
,这没有什么区别。
我在这里错过了什么?
不幸的是我没能解决这个问题,我不确定为什么它没有按预期工作。
但是,我通过在 UIScrollView 中使用 UIStackView 获得了预期的行为。我使用 this article 作为参考。
UI布局
- UIViewController (1)
- 导航栏(2)
- UI滚动视图 (2)
- UI堆栈视图 (3)
- UI文本字段 (4)
- UI文本字段 (4)
- UI文本字段 (4)
- UI文本字段 (4)
- UI按钮 (4)
UIScrollView
- ScrollView 的
leading
和trailing
被16 约束到superView。 - ScrollView 的
top
被 0 限制在导航栏的底部。 - ScrollView 的
bottom
被 0 限制在 superView 的底部。
UIStackView
- StackView 的
leading
和trailing
被 0 限制到 scrollView。 - StackView 与 scrollView 的宽度相等。
- StackView 的
top
和bottom
被 24 限制到滚动视图,以获得所需的导航栏间距,以及按钮和键盘. - StackView 设置为
axis=vertical
、alignment=fill
、distribution=fill
、spacing=24
。
NavigationBar
NavigationBar 是自定义的 class,它的高度从其内容中得出。这没有设置高度限制,但占位符高度为 100。NavigationBar 将填满整个屏幕。这通过删除占位符高度,并添加具有低优先级的 any 高度约束来解决(在本例中优先级为 1
)。
应用键盘插入的初始代码现在可以工作了。
/// Act when keyboard is shown, by adding contentInsets to the scrollView.
func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil)
{
_ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification,
object: nil, queue: nil)
{ notification in
let userInfo = notification.userInfo!
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top,
left: scrollView.contentInset.left,
bottom: keyboardSize.height,
right: scrollView.contentInset.right)
scrollView.contentInset = contentInsets
block?(keyboardSize)
}
}