SwiftUI:如何在 UIViewRepresentable UITextField 上调用 becomeFirstResponder 时修复“通过属性检测到的 AttributeGraph 循环”
Posted
技术标签:
【中文标题】SwiftUI:如何在 UIViewRepresentable UITextField 上调用 becomeFirstResponder 时修复“通过属性检测到的 AttributeGraph 循环”【英文标题】:SwiftUI: How to fix 'AttributeGraph cycle detected through attribute' when calling becomeFirstResponder on UIViewRepresentable UITextField 【发布时间】:2020-01-12 20:23:05 【问题描述】:我正在尝试使用其标记属性和@Binding 将文本字段设置为第一响应者。由于我无法从 SwiftUI TextField 访问底层 UITextField 并直接调用 .becomeFirstResponder()
,因此我不得不使用 UIViewRepresentable
包装 UITextField。下面的代码有效,但会导致以下控制台消息=== AttributeGraph: cycle detected through attribute <#> ===
。
听起来我有内存泄漏和/或保留周期,我已将问题隔离到 textField.becomeFirstResponder()
行,但检查了 Xcode 的内存图层次结构后我看不出有什么问题?
非常感谢您提供的任何帮助。
struct CustomTextField: UIViewRepresentable
var tag: Int
@Binding var selectedTag: Int
@Binding var text: String
func makeCoordinator() -> Coordinator
Coordinator(self)
class Coordinator: NSObject, UITextFieldDelegate
var parent: ResponderTextField
init(_ textField: ResponderTextField)
self.parent = textField
func textFieldDidChangeSelection(_ textField: UITextField)
parent.text = textField.text ?? ""
func makeUIView(context: Context) -> UITextField
let textField = UITextField(frame: .zero)
textField.tag = tag
textField.delegate = context.coordinator
return textField
func updateUIView(_ textField: UITextField, context: Context)
if textField.tag == selectedTag, textField.window != nil, textField.isFirstResponder == false
textField.becomeFirstResponder()
【问题讨论】:
您找到解决方案了吗?我遇到了同样的问题? 我无法提供解决方案,但我遇到了同样的问题。 “AttributeGraph:检测到循环...”消息在控制台上出现多次,在某些情况下,应用程序崩溃指向 textField.becomeFirstResponder()。如果我在调用 becomeFirstResponder() 之前检查 textField.canBecomeFirstResponder 甚至会发生崩溃... 尝试以下操作:将您的结构 CustomTextField 更改为最终类(最终属性很重要)。那么你需要添加一个显式的初始化程序(init(....))。就我而言,这似乎解决了 AttributeGraph 问题。顺便说一句,这也是当观察对象的已发布属性(传递到此自定义文本字段)更改时视图未更新的解决方案... 适用于 Xcode 12 / ios 14 我认为可能是由于在协调器中捕获自我引起的。我在这里找到了一个类似的循环:***.com/questions/66540207 【参考方案1】:凭直觉,我将 becomeFirstResponder() 放入异步调度中。这修复了警告。 我猜在创建视图时会发生某种创建循环,并且您调用 becomeFirstResponder(),以便调用视图,这会触发 SwiftUI 找到它尚未正确创建的视图(或其他东西像这样)
无论如何 - 这对我有用:
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>)
if uiView.text != self.text
uiView.text = self.text
if uiView.window != nil, !uiView.isFirstResponder
//This triggers attribute cycle if not dispatched
DispatchQueue.main.async
uiView.becomeFirstResponder()
【讨论】:
非常感谢。我的自定义 UIViewRepresentable 在 iOS 15 上坏了,但这修复了它! 是的,这真的有效! 这会导致另一个警告:Modifying state during view update, this will cause undefined behavior.
【参考方案2】:
困惑的 Vorlon 的回答对我有用,但它仍然有点不稳定,因为在导航到另一个视图期间,uiView.isFirstResponder
似乎没有返回我所期望的内容。这导致uiView.becomeFirstResponder()
的调用时间超出预期。
解决此问题的另一种方法是在您的协调器中添加一个布尔值,以跟踪您已经将该字段设为第一响应者。
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<FocusableTextField>)
uiView.text = text
if isFirstResponder && !context.coordinator.didBecomeFirstResponder
uiView.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
class Coordinator: NSObject
@Binding var text: String
var didBecomeFirstResponder: Bool = false
init(text: Binding<String>)
_text = text
@objc public func textViewDidChange(_ textField: UITextField)
self.text = textField.text ?? ""
【讨论】:
【参考方案3】:我也收到了=== AttributeGraph: cycle detected through attribute <#> ===
的崩溃消息,但结果证明崩溃是由其他原因引起的。
崩溃实际上是由于缺少未链接到应用程序本身的依赖项造成的。
我还检查了仪器,也没有显示内存泄漏或保留周期。
缺少的依赖是另一个框架,它提供了我们 SwiftUI 代码中使用的一些 SwiftUI 视图修饰符。但是有人不小心从应用程序的“Link Binary With Libraries”和“Embed Frameworks”构建阶段中删除了该框架。一旦我将依赖项添加到正确的构建阶段并添加到主应用程序方案中,那么相关的日志消息就会停止出现(并且崩溃也停止了)。
换句话说,=== AttributeGraph: cycle detected through attribute <#> ===
似乎不会直接导致崩溃或内存泄漏,但可能与导致(如缺少依赖项)SwiftUI 无法解析视图层次结构的其他问题有关。
YMMV.
【讨论】:
【参考方案4】:我在父视图中删除这段代码,警告消失,
.focusedValue(\.path, $binding)
【讨论】:
我在 OPs 问题中看不到此代码。它到底有什么关系? 我在项目中遇到了同样的问题,这里只是一个track以上是关于SwiftUI:如何在 UIViewRepresentable UITextField 上调用 becomeFirstResponder 时修复“通过属性检测到的 AttributeGraph 循环”的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI - 如何在 SwiftUI 中弹出到特定视图?