尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

douyin-ios-swift 经典难题破解:悬浮键盘之上的 UITextView 与手势冲突处理

douyin-ios-swift 经典难题破解:悬浮键盘之上的 UITextView 与手势冲突处理 douyin-ios-swift 经典难题破解悬浮键盘之上的 UITextView 与手势冲突处理【免费下载链接】douyin-ios-swift抖音 iOS Swift版项目地址: https://gitcode.com/gh_mirrors/do/douyin-ios-swift在抖音风格的 iOS 应用中输入框悬浮在软键盘上方、与表情面板手势互不干扰是 IM 聊天和评论功能里最经典的两个难题。开源项目 douyin-ios-swift 用纯 Swift 实现了一套高仿抖音 App其评论输入栏与聊天输入框的完整解法正好把「悬浮键盘的 UITextView」和「手势冲突处理」这两块硬骨头拆解得清晰易懂非常适合新手学习 iOS 交互细节。为什么这两个问题让人头疼想象一下使用抖音的体验 点评论输入框系统软键盘弹出输入栏紧贴键盘顶部而不是把整个页面顶起来 点表情按钮键盘收起、表情面板滑出输入框依然悬停不动 手指点面板外的空白处面板优雅地收起但不会误触发列表的点击事件。要同时做到这三点需要解决两个核心矛盾输入框要悬浮却不遮挡页面、点击空白要收起却不误伤子控件。方案一让输入框悬浮在键盘之上第一步把输入框挂到 Window 上而非页面悬浮的关键在于层级。项目把整个输入容器ChatTextView直接添加到 App 的主窗口上而不是聊天页面的 view 上见Douyin/Controller/GroupChat/ChatTextView.swiftfunc show() { let window UIApplication.shared.delegate?.window as? UIWindow window?.addSubview(self) }挂到 Window 之后输入框天然浮在键盘之上也不受页面转场影响。对应地ChatListController在viewWillAppear时调用show()退出时调用dismiss()生命周期管理非常干净。第二步监听键盘通知实时计算高度输入框的纵向位置取决于键盘高度。项目监听keyboardWillShowNotification并通过一个通用工具方法从通知里取出键盘 frame见Douyin/Extension/Notification.swiftcontainerBoardHeight notification.keyBoardHeight()keyBoardHeight()内部读取keyboardFrameEndUserInfoKey并做了横竖屏适配返回值直接就是键盘安全区的总高度。拿到高度后updateContainerFrame()用一段 0.25 秒的缓动动画把容器 frame 平滑移动到screenHeight - 键盘高 - 输入栏高的位置——这就是悬浮效果的来源不是输入框动是它永远重新计算一次贴键盘的位置。第三步禁用滚动让高度随文字增长UITextView默认是可滚动的悬浮栏必须关掉textView.isScrollEnabled false关掉滚动后文字变多时输入框高度会撑破容器所以每次textViewDidChange时用NSAttributedString的扩展方法multiLineSize(width:)见Douyin/Extension/NSAttributedString.swift手动算出多行文本高度再驱动容器重排。这一招同样用在了评论页的HoverTextViewDouyin/Controller/AwemeList/HoverTextView.swift里两处方案完全一致。顺手解决聊天列表同步避让输入框变高时聊天列表也要往上推否则最新消息会被挡住。ChatTextView通过 delegate 回调onEditBoardHeightChange(height:)ChatListController收到后直接改写tableView.contentInset并滚动到底部——一行代码完成列表与悬浮栏的联动比手动改 frame 稳妥得多。方案二手势冲突的三件套悬浮层带来的最大副作用是误触一个铺满屏幕的透明容器会把子控件和背后页面的点击全部截胡。项目用三个经典手段逐个化解。手段一重写 hitTest透明区域让路ChatTextView和HoverTextView都重写了hitTest当命中点落在自身且当前处于无编辑状态时直接返回nil让触摸事件穿透到下层视图override func hitTest(_ point: CGPoint, with event: UIEvent?) - UIView? { let hitView super.hitTest(point, with: event) if hitView self { if editMessageType .EditNoneMessage { return nil // 透明区域不拦截触摸 } } return hitView }这是 iOS 触摸传递的第一课想让某个区域不存在就在 hitTest 里返回 nil。手段二手势代理按视图过滤避免误关面板容器上加了一个全局单击手势用来实现点空白处收起面板。但表情格、照片格本身也需要被点击选中两个手势天然打架。项目在gestureRecognizer(_:shouldReceive:)中按视图类名过滤func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) - Bool { // 点在表情格 / 照片格上时放行给 cell 自己的选中逻辑 if NSStringFromClass((touch.view?.superview?.classForCoder)!) .contains(EmotionCell) || ... .contains(PhotoCell) { return false } return true }同样的模式在评论弹层CommentsPopViewDouyin/Controller/AwemeList/CommentsPopView.swift里也复用了一次点评论列表行时返回false防止弹层被误关闭。shouldReceive 按 touch 来源视图过滤是处理容器级手势冲突最实用的套路。手段三横滑翻页期间临时禁用 pan动画结束再恢复表情面板是一个横向UICollectionView左右滑动手册式翻页。如果翻页吸附动画执行时 pan 手势仍然生效用户轻轻一拖就会把刚吸附好的页面又拖乱。EmotionSelectorDouyin/Controller/GroupChat/EmotionSelector.swift的做法很直接拖拽结束scrollViewDidEndDragging时先panGestureRecognizer.isEnabled false执行 0.2 秒的吸附动画并更新指示点动画 completion 里再isEnabled true恢复。一个先禁用、后恢复的开关就消除了翻页动画与手势的抢占逻辑短小但非常典型。这套方案值得抄什么把上面内容归纳成一张iOS 悬浮输入栏 手势冲突速查清单场景项目中的解法参考文件输入框悬浮键盘挂 Window 键盘通知 动画重定位Douyin/Controller/GroupChat/ChatTextView.swift输入框高度自适应isScrollEnabled false 文本高度计算Douyin/Extension/NSAttributedString.swift列表避让悬浮栏delegate 回调改写contentInsetDouyin/Controller/GroupChat/ChatListController.swift透明区域触摸穿透重写hitTest返回nilDouyin/Controller/AwemeList/HoverTextView.swift容器手势误伤子控件shouldReceive:按视图过滤Douyin/Controller/AwemeList/CommentsPopView.swift翻页动画抢手势动画期间禁用/恢复 pan 手势Douyin/Controller/GroupChat/EmotionSelector.swift总结douyin-ios-swift 并没有引入任何复杂的第三方交互框架仅靠Window 层级 键盘通知 hitTest 穿透 手势代理过滤这三板斧就把悬浮键盘的 UITextView与多层手势冲突这两个经典难题处理得干净利落。如果你想在自己的聊天、评论、弹幕功能里落地同样的体验照着上表的路径逐一走一遍基本一天就能搭出一套属于自己的悬浮输入组件 。【免费下载链接】douyin-ios-swift抖音 iOS Swift版项目地址: https://gitcode.com/gh_mirrors/do/douyin-ios-swift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表