iOS 15中viewIsAppearing()内UIKeyboardLayoutGuide不生效问题
iOS15中UIKeyboardLayoutGuide在viewIsAppearing()布局失效问题分析与解决
问题现象
- 布局逻辑:在
viewDidLoad()中添加子视图,viewIsAppearing()中设置约束,通过UIKeyboardLayoutGuide实现底部SearchBar随键盘位置移动(类似Safari搜索栏) - 版本差异:iOS16.4及以上、iOS17模拟器中显示正常,但iOS15.0/15.2模拟器中SearchBar完全不显示
- 特殊情况:
- 不使用
UIKeyboardLayoutGuide,直接基于安全区域固定在底部时,控件显示正常 - 将布局代码移到
viewDidLoad()中,控件能正常显示,但切换导航页面返回后会消失;改用viewWillAppear()后该问题解决
- 不使用
原因分析
- iOS15中UIKeyboardLayoutGuide的初始化时机问题:
在iOS15版本中,viewIsAppearing()调用时,keyboardLayoutGuide的布局属性尚未完成初始化或更新。此时设置的约束会基于guide的初始(异常)frame计算,导致控件被推到屏幕可视区域外,表现为“不显示”。 - viewDidLoad布局后的返回消失问题:
viewDidLoad()仅在视图控制器初始化时执行一次,当页面被导航栈弹出再返回时,视图会重新触发布局,但之前绑定的keyboardLayoutGuide约束可能因视图状态重置而失效,导致控件消失。而viewWillAppear()每次页面即将显示时都会执行,能确保约束被重新正确应用。
解决方案
方案1:在viewWillAppear()中执行一次性布局
通过添加标记位避免重复设置约束,确保布局仅执行一次,同时适配iOS15的guide初始化时机:
final class ViewController: UIViewController { private let searchBar = UISearchBar() private let registButton = UIButton(type: .system) private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) private var isLayoutConfigured = false // 标记布局是否已完成 override func viewDidLoad() { super.viewDidLoad() addSubviews() // 初始化控件样式 registButton.setTitle("注册", for: .normal) searchBar.searchBarStyle = .minimal } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard !isLayoutConfigured else { return } layout() isLayoutConfigured = true } private func addSubviews() { view.addSubview(collectionView) view.addSubview(searchBar) view.addSubview(registButton) } private func layout() { let searchBarHeight = 50 collectionView.snp.makeConstraints { $0.top.equalTo(view.safeAreaLayoutGuide) $0.horizontalEdges.equalTo(view.safeAreaLayoutGuide) $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-searchBarHeight) } searchBar.snp.makeConstraints { $0.height.equalTo(searchBarHeight) $0.left.equalTo(view.safeAreaLayoutGuide) $0.bottom.equalTo(view.keyboardLayoutGuide.snp.top) } registButton.snp.makeConstraints { $0.height.equalTo(searchBarHeight) $0.left.equalTo(searchBar.snp.right) $0.right.equalTo(view.safeAreaLayoutGuide) $0.bottom.equalTo(view.keyboardLayoutGuide.snp.top) $0.width.equalTo(100) } } }
方案2:修正附加测试代码(移除布局中的重复addSubview)
将子视图添加操作移到viewDidLoad(),避免重复添加,同时在viewWillAppear()中执行布局:
import UIKit import SnapKit final class ViewController: UIViewController { private let uiTextField: UITextField = { $0.placeholder = "search" $0.backgroundColor = .red return $0 }(UITextField()) private var isLayoutConfigured = false override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white view.addSubview(uiTextField) // 子视图添加提前到viewDidLoad } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard !isLayoutConfigured else { return } layout() isLayoutConfigured = true } private func layout() { uiTextField.snp.makeConstraints { $0.height.equalTo(50) $0.horizontalEdges.equalToSuperview() $0.bottom.equalTo(view.keyboardLayoutGuide.snp.top) } } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { view.endEditing(true) } }
内容的提问来源于stack exchange,提问作者Joon
相关产品推荐
相关产品推荐

