如何为自定义键盘扩展创建iPhone与iPad专属storyboard?
如何为iOS键盘扩展实现iPhone和iPad独立的Storyboard布局
我之前也碰到过这个需求,其实核心思路是绕开Info.plist的单一Storyboard限制,利用键盘扩展的入口控制器KeyboardViewController来做设备类型判断,手动加载对应的Storyboard文件。下面是具体的实现步骤:
1. 创建两个独立的Storyboard文件
首先为iPhone和iPad分别创建专属的Storyboard:
- 右键项目文件 → 选择
New File→ 选择Storyboard,命名为Keyboard-iPhone,在这个Storyboard里按照iPhone的尺寸设计键盘布局 - 重复上述步骤,创建
Keyboard-iPad,按照iPad的尺寸设计布局 - 确保每个Storyboard里都有一个初始视图控制器(勾选
Is Initial View Controller),这个控制器可以是UIInputViewController的子类,也可以直接用系统的类,根据你的业务需求来决定
2. 修改键盘扩展的Info.plist
默认情况下,键盘扩展是通过NSExtensionMainStoryboard指定启动的Storyboard,我们需要改成通过代码初始化控制器:
- 打开键盘扩展的
Info.plist文件 - 找到
NSExtension字典,删除里面的NSExtensionMainStoryboard键 - 添加一个新的键
NSExtensionPrincipalClass,类型设为String,值填$(PRODUCT_MODULE_NAME).KeyboardViewController(这个宏会自动替换成你的项目模块名,确保指向正确的入口控制器类)
3. 在入口控制器中实现动态加载逻辑
打开默认生成的KeyboardViewController,重写loadView方法,在这里根据设备类型加载对应的Storyboard:
import UIKit class KeyboardViewController: UIInputViewController { override func loadView() { // 判断当前设备是iPhone还是iPad let isiPad = UIDevice.current.userInterfaceIdiom == .pad let storyboardName = isiPad ? "Keyboard-iPad" : "Keyboard-iPhone" // 加载对应的Storyboard并获取初始控制器 guard let storyboard = UIStoryboard(name: storyboardName, bundle: nil), let rootVC = storyboard.instantiateInitialViewController() as? UIInputViewController else { // 如果加载失败, fallback 到默认的view加载逻辑 super.loadView() return } // 将Storyboard的根控制器视图设置为当前控制器的视图 self.view = rootVC.view // 添加子控制器,确保子控制器的生命周期方法正常触发 addChild(rootVC) rootVC.didMove(toParent: self) } override func viewDidLoad() { super.viewDidLoad() // 这里不需要额外的布局代码,逻辑都放在各自Storyboard对应的控制器里即可 } }
额外优化建议
- 如果iPhone和iPad的键盘逻辑差异较大,可以创建两个子类
iPhoneKeyboardViewController和iPadKeyboardViewController,都继承自UIInputViewController,然后分别设置到对应的Storyboard控制器上,这样各自的业务逻辑可以完全分开维护 - 测试时记得分别在iPhone和iPad模拟器/真机上验证布局和功能,避免出现适配问题
内容的提问来源于stack exchange,提问作者Dipen STL
相关产品推荐
相关产品推荐

