iOS自定义键盘选中CollectionView项时调整SFSafariViewController高度问题
问题分析与解决方案
首先得明确核心问题:你的自定义键盘属于App Extension,它的视图层级和权限都和主App有很大区别,你之前的代码犯了两个关键错误——错误地操作键盘自身的高度约束来控制Safari窗口,以及用了不正确的方式呈现SFSafariViewController。
为什么你的代码没效果?
- 视图层级分离:
SFSafariViewController是模态弹出的,属于宿主App的窗口层级,和键盘的视图完全独立。你修改self.heightConstraint只是在调整键盘本身的高度,和Safari窗口没有任何关系,而且键盘的高度受系统约束管控,手动修改后大概率会被系统重置。 - 错误的present方式:键盘的
UIInputViewController不属于宿主App的视图控制器链,直接调用self.present无法让Safari控制器正确显示在宿主App的界面上。
修正后的实现方案
第一步:获取宿主App的顶层视图控制器
键盘扩展无法直接访问宿主App的视图层级,我们需要先找到宿主App的顶层VC,再通过它来present Safari控制器:
private func getTopHostViewController() -> UIViewController? { if #available(iOS 13.0, *) { // iOS13+ 用Scene获取窗口 guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = scene.windows.first else { return nil } var topVC = window.rootViewController while let presentedVC = topVC?.presentedViewController { topVC = presentedVC } return topVC } else { // iOS13以下直接取keyWindow guard let window = UIApplication.shared.keyWindow else { return nil } var topVC = window.rootViewController while let presentedVC = topVC?.presentedViewController { topVC = presentedVC } return topVC } }
第二步:修改CollectionView选中事件的代码
替换你原来的didSelectItemAt方法,改用正确的方式呈现并控制Safari窗口:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if collectionView == self.key.colImages { guard let articleURLString = self.articles[indexPath.item].url, let targetURL = URL(string: articleURLString) else { return } // 复制URL到剪贴板(保留你的原有逻辑) UIPasteboard.general.string = articleURLString // 创建并配置Safari控制器 let safariVC = SFSafariViewController(url: targetURL, entersReaderIfAvailable: true) safariVC.delegate = self // 配置Safari的外观(保留你的原有逻辑) if #available(iOS 10.0, *) { safariVC.preferredBarTintColor = .white safariVC.preferredControlTintColor = UIColor(red:0.19, green:0.58, blue:0.91, alpha:1.0) } else { safariVC.view.tintColor = .white } // 设置Safari的模态样式(iOS13+支持非全屏弹窗) if #available(iOS 13.0, *) { safariVC.modalPresentationStyle = .pageSheet // 自定义弹窗高度(这里设置为500,你可以根据需求调整) safariVC.preferredContentSize = CGSize(width: UIScreen.main.bounds.width, height: 500) } // 通过宿主App的顶层VC来present if let topHostVC = getTopHostViewController() { topHostVC.present(safariVC, animated: true, completion: nil) } } else { self.fetchArticles(fromSource: "\(categoriesArray[indexPath.row])") } }
额外注意事项
- 开启键盘的开放权限:在你的键盘Extension的
Info.plist中,确保RequestsOpenAccess设置为YES,否则无法访问UIApplication.shared以及执行跨App的视图呈现操作。 - 系统约束兼容:键盘自身的高度尽量交给系统管理,不要手动修改它的约束,否则可能导致键盘布局异常。
内容的提问来源于stack exchange,提问作者Ben Shaw
相关产品推荐
相关产品推荐

