iOS 输入框键盘遮挡的 8 类问题逐条排查(附源码行号)
【免费下载链接】IQKeyboardManagerCodeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more.项目地址: https://gitcode.com/gh_mirrors/iq/IQKeyboardManager
一条用户反馈:收件人表单页,点地址框整页被键盘顶起、导航栏被盖住;另一台设备上点输入框毫无反应,placeholder 完全藏在键盘后面。打开调试日志,控制台只输出一行Need to move: 0.0。两种现象根因相同:IQKeyboardManager 的键盘遮挡避让没有按预期工作。本文按 5 种界面容器把 8 类常见偏移问题逐条过一遍,配置项都标注到源码文件和行号。
项目一句话定位
IQKeyboardManager 是一个免写代码的 iOS 键盘遮挡避让库:一行代码启用后,它替你移动页面、滚动列表、调整 contentInset,保证 UITextField 和 UITextView 不被键盘盖住,不需要手算 frame、不需要写子类。Swift 版本要求 iOS 13+,GitHub star 数长期在 4 万+ 量级;8.x 仍在迭代(当前仓库 Package.swift 依赖 IQKeyboardToolbarManager >= 1.1.4、IQTextView >= 1.0.5),ObjC 版本已拆到独立仓库。
本文覆盖:
- 验证接入是否真正生效,给出一行日志确认法
- 按列表 / 滚动 / 弹窗 / 全屏 / 嵌套 5 种容器定位偏移根因
- 配置距离与排除项(keyboardDistance、disabledDistanceHandlingClasses),附取值区间
- 读懂 7.x → 8.x 迁移表,修掉 3 个高频编译报错
- 输出一份可直接贴进发版前自查的 8 项清单
5 行代码接入并确认它真的在跑
// AppDelegate:主开关 + 全局距离 + 调试日志,三行足够
IQKeyboardManager.shared.isEnabled = true IQKeyboardManager.shared.keyboardDistance = 15.0 IQKeyboardManager.shared.isDebuggingEnabled = true定义位于 IQKeyboardManager.swift,isEnabled 默认值 false,需显式开启;keyboardDistance 位于 第 112 行,默认值 10.0,最小值 0(负值只输出警告);isDebuggingEnabled 位于 IQKeyboardManager+Debug.swift,默认值 false,官方示例同样这样打开(AppDelegate.swift 第 39 行)。
推荐起步参数:
keyboardDistance建议 10–20pt:过小输入框贴键盘,过大页面被推得过高。单个输入框要例外时,用textField.iq.distanceFromKeyboard = 50.0(定义在第 82 行,默认值UIView.defaultKeyboardDistance,含义是跟随全局设置)。layoutIfNeededOnUpdate默认 false(第 127 行);页面跟着键盘动画抖动时,先试切换这一项。
生效确认:键盘弹出后控制台会输出Enabled和Need to move: <数值>,后者就是本轮计算出的偏移量,正数表示输入框被挡、需要上移(IQKeyboardManager+Position.swift 第 176 行)。看到数值非零、输入框确实上移,才说明接入真正在工作。
按五种容器逐一定位偏移原因
列表容器:UITableViewController 子类页面不响应键盘避让的修法
典型表现:页面继承 UITableViewController,键盘弹起后毫无反应,输入框留在键盘后面。原因很直接——类级排除生效了。
// 1. 确认控制器类是否在排除列表里 print(IQKeyboardManager.shared.disabledDistanceHandlingClasses) // 2. 确需恢复时(少见),把 UITableViewController 从排除列表移除 IQKeyboardManager.shared.disabledDistanceHandlingClasses.removeAll { $0 == UITableViewController.self }定义位于 IQKeyboardManager.swift,disabledDistanceHandlingClasses 默认值 3 项:
[UITableViewController.self, UIInputViewController.self, UIAlertController.self];同一类同时出现在 enabledDistanceHandlingClasses 时,排除优先。
常规路径(UIViewController + UITableView)下,库不会推整页,而是滚动列表并调整底部 contentInset。对 cell 复用场景有专门补偿:当前行显示不全时,先算上一行 cell 应滚到多少位置,再取较小值滚动。
出处:IQKeyboardManager+Position.swift
handleTableViewCase,UICollectionView 对应 第 512 行。
滚动容器:UIScrollView 的偏移被"顺手改掉"又还原不回去
典型表现:键盘弹出后 scrollView 的 contentOffset / contentInset 被修改,收起键盘后滚动位置停在原地,用户得手动滚回去。
// 跳过这个 scrollView 的滚动调整 scrollView.iq.ignoreScrollingAdjustment = true // 或只停止修改它的 contentInset scrollView.iq.ignoreContentInsetAdjustment = true // 键盘收起后,把这个 scrollView 的 contentOffset 还原回键盘弹起前的值 scrollView.iq.restoreContentOffset = true定义位于 UIScrollView+IQKeyboardManagerExtension.swift,三个属性默认值均为 false;restoreContentOffset 位于 第 80 行,默认值 false。
库是怎么找到目标 scrollView 的:从输入框的 superview 沿链条向上找,取第一个isScrollEnabled == true且ignoreScrollingAdjustment == false的 UIScrollView;中间某层容器关闭了滚动,就继续往上找。你看到的"偏移"若是这条遍历找错了容器,给那层容器设ignoreScrollingAdjustment = true即可解决。
出处:IQKeyboardManager+Position.swift。
模态 / 弹层内的输入框:默认被排除,要自己接管
典型表现:UIAlertController 或自定义弹层里的输入框,键盘弹出后要么把整个根视图顶起来(背景页面跟着动),要么完全不动。
// 弹层内输入框强制关闭全局管理,改由弹层自己的 scrollView 处理 alertTextField.iq.enableMode = .disabled // 打开点按外部收起键盘(默认关闭) IQKeyboardManager.shared.resignOnTouchOutside = true // 把自定义视图加入手势忽略列表,避免弹层内按钮被收起手势抢走 IQKeyboardManager.shared.touchResignedGestureIgnoreClasses.append(MyCardView.self)定义位于 UIView+IQKeyboardManagerExtension.swift,enableMode 默认值
.default(跟随全局开关);resignOnTouchOutside 位于 IQKeyboardManager+Resign.swift,默认值 false。
⚠️ UIAlertController 同时出现在距离排除列表和点按收起的排除列表里(后者默认[UIAlertController.self, UIInputViewController.self],IQKeyboardResignHandler.swift 第 55 行)。弹层内输入框建议 enableMode + 弹层内滚动自己接管,别指望全局逻辑。
全屏 / 边缘延伸布局:键盘盖住导航栏怎么办
典型表现:edgesForExtendedLayout设成.all或.bottom,页面延伸到底部安全区,键盘弹出后偏移量不对,甚至盖住导航栏。
// 官方示例演示延伸到边缘的页面(全屏模式) override func viewDidLoad() { super.viewDidLoad() self.edgesForExtendedLayout = .all }定义位于 EdgesForExtendedLayoutViewController.swift,示例显式设为
.all;edgesForExtendedLayout 的默认值由 UIKit 控制。
库的位置计算已经把底部 safe area 从键盘高度里减掉(IQKeyboardManager+Position.swift 第 283 行),并以导航栏高度(无导航栏时为状态栏高度)作为上边界(第 299 行)。所以这个场景的偏移,多半来自你手动的 safe area inset,或对根视图 origin 的自定义动画在跟库抢位置。
嵌套容器:自定义视图层级下调整不生效
典型表现:输入框嵌在 stack view、自定义容器或 collection view 的 supplementary view 里,程序化改完布局后位置不更新,或偏移量不对。
// 程序化修改 frame / 约束后,手动触发一次位置计算 IQKeyboardManager.shared.reloadLayoutIfNeeded() // 检查输入框链条上是否存在可滚动的祖先 print(String(describing: textField.iq.superviewOf(type: UIScrollView.self)))定义位于 IQKeyboardManager.swift,reloadLayoutIfNeeded 仅在 isEnabled、键盘可见且根视图配置就绪时生效;superviewOf 为 UIView+Parent.swift 的同类型祖先查找。
还有一种定位办法:给实际移动量挂回调,和期望值对一下:
// 每次偏移变化打印实际上移量,便于核对 IQKeyboardManager.shared.movedDistanceChanged = { moved in print("moved: \(moved)") }定义位于 IQKeyboardManager+Position.swift,movedDistance 默认值 0。
7.x 升 8.x 版本差异速查
| 旧 API(7.x) | 新 API(8.x) | 影响点 | 是否破坏性变更 |
|---|---|---|---|
enableAutoToolbar(默认 true) | IQKeyboardToolbarManager.shared.isEnabled | 键盘上方工具栏 | 是,默认改为 false,旧属性标废弃 |
IQPreviousNextView | IQDeepResponderContainerView | 前后切换按钮容器 | 是,类不可用 |
IQKeyboardReturnKeyHandler | IQKeyboardReturnManager(独立库) | 返回键处理 | 是,类不可用 |
keyboardDistanceFromTextField | keyboardDistance | 全局键盘距离 | 是,重命名 |
resignOnTouchOutside | API 名不变,拆入 Resign subspec | 点按外部收起 | 否,仅拆分 |
完整迁移清单(含工具栏属性、类重命名明细):Documentation/MIGRATION GUIDE 7.0 TO 8.0.md。
三个最高频编译报错:
"keyboardDistanceFromTextField" is unavailable, renamed to "keyboardDistance"→ 改用IQKeyboardManager.shared.keyboardDistance(废弃声明见 IQKeyboardManager+Deprecated.swift 第 33 行)。IQPreviousNextView is unavailable: renamed to "IQDeepResponderContainerView"→ Storyboard 里自定义视图的类名和模块都改成 IQKeyboardToolbarManager(声明 第 88 行)。registerKeyboardSizeChange is unavailable→ 改用 IQKeyboardNotification 独立库(声明 第 63 行)。
自查清单
IQKeyboardManager.shared.isEnabled == true(默认 false,不设等于没开,第 72 行)isDebuggingEnabled = true,键盘弹出时控制台出现EnabledNeed to move:数值非零,且量级等于期望偏移- 当前控制器类不在
disabledDistanceHandlingClasses里(默认 3 项,含 UITableViewController) - 输入框的
iq.enableMode没被别的代码设成.disabled - scrollView 的
iq.ignoreScrollingAdjustment/iq.ignoreContentInsetAdjustment没被误设 true - 没有自管的 safe area inset / 根视图 origin 动画在和库抢位置
- 程序化改过布局后调用过
reloadLayoutIfNeeded()
生产环境补充:按需启停与 3 个边缘 case
按需启停。README 明确警告:与第三方键盘 UI 冲突时,由开发者负责切换(README.md 第 91 行):
// 展示第三方输入组件期间关闭,dismiss 后恢复 IQKeyboardManager.shared【免费下载链接】IQKeyboardManagerCodeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more.项目地址: https://gitcode.com/gh_mirrors/iq/IQKeyboardManager
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考