You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 15中viewIsAppearing()内UIKeyboardLayoutGuide不生效问题

iOS15中UIKeyboardLayoutGuide在viewIsAppearing()布局失效问题分析与解决

问题现象

  • 布局逻辑:在viewDidLoad()中添加子视图,viewIsAppearing()中设置约束,通过UIKeyboardLayoutGuide实现底部SearchBar随键盘位置移动(类似Safari搜索栏)
  • 版本差异:iOS16.4及以上、iOS17模拟器中显示正常,但iOS15.0/15.2模拟器中SearchBar完全不显示
  • 特殊情况:
    • 不使用UIKeyboardLayoutGuide,直接基于安全区域固定在底部时,控件显示正常
    • 将布局代码移到viewDidLoad()中,控件能正常显示,但切换导航页面返回后会消失;改用viewWillAppear()后该问题解决

原因分析

  1. iOS15中UIKeyboardLayoutGuide的初始化时机问题:
    在iOS15版本中,viewIsAppearing()调用时,keyboardLayoutGuide的布局属性尚未完成初始化或更新。此时设置的约束会基于guide的初始(异常)frame计算,导致控件被推到屏幕可视区域外,表现为“不显示”。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 07:34:51