SwiftUI使用UIKit TextField Representable出现AttributeGraph循环报错问题
报错原因
该循环报错和searchVM的使用方式直接相关,核心触发链路如下:
- 你将
showSearchView设为true后,这个@Published属性会通知所有订阅它的视图重绘,FirstResponderTextfield因为持有@ObservedObject修饰的searchVM,会触发自身的updateUIView方法执行 updateUIView中调用uiView.becomeFirstResponder()唤起键盘,这个系统操作会触发键盘布局调整,反过来通知SwiftUI视图树更新布局,再次触发FirstResponderTextfield的重绘逻辑- 重绘后再次进入
updateUIView,判断到searchVM.showSearchView仍为true,会再次调用becomeFirstResponder(),由此形成状态更新闭环,就是AttributeGraph检测到的循环问题
修复方案
方案1:增加第一响应者状态判断
在updateUIView中新增状态判断,只有当文本框的响应状态和预期不一致时,才执行响应状态切换操作,避免重复调用触发循环:
func updateUIView(_ uiView: UIViewType, context: Context) { // 只有当前不是第一响应者,且需要显示搜索视图时,才唤起键盘 if searchVM.showSearchView, !uiView.isFirstResponder { uiView.becomeFirstResponder() } // 只有当前是第一响应者,且需要隐藏搜索视图时,才收起键盘 else if !searchVM.showSearchView, uiView.isFirstResponder { uiView.resignFirstResponder() } // 同步文本内容,避免状态不一致 uiView.text = text }
方案2:优化状态管理,减少多余更新源
当前SearchBar中存在两套独立的文本状态:本地@State text和SearchViewModel中的text,多余的状态同步逻辑也会增加视图重绘概率,建议统一为使用searchVM的文本状态:
- 修改
SearchViewModel,给文本属性加上@Published修饰保证同步:
class SearchViewModel: ObservableObject { @Published var text: String = "" @Published var showSearchView: Bool = false }
- 移除
SearchBar中的本地文本状态,直接绑定searchVM的text属性:
struct SearchBar: View { @ObservedObject var searchVM: SearchViewModel var body: some View { HStack { FirstResponderTextfield(searchVM: searchVM, text: $searchVM.text, placeholder: "Search") Text("Cancel") .onTapGesture { searchVM.showSearchView.toggle() } } } }
方案3:延迟键盘唤起操作(可选)
如果调整后仍有循环,可以把键盘唤起操作放到下一个runloop执行,避开当前的视图更新周期:
if searchVM.showSearchView, !uiView.isFirstResponder { DispatchQueue.main.async { uiView.becomeFirstResponder() } }
内容的提问来源于stack exchange,提问作者Jake Smith
相关产品推荐
相关产品推荐

