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 &lt;#&gt; === 的崩溃消息,但结果证明崩溃是由其他原因引起的。

崩溃实际上是由于缺少未链接到应用程序本身的依赖项造成的。

我还检查了仪器,也没有显示内存泄漏或保留周期。

缺少的依赖是另一个框架,它提供了我们 SwiftUI 代码中使用的一些 SwiftUI 视图修饰符。但是有人不小心从应用程序的“Link Binary With Libraries”和“Embed Frameworks”构建阶段中删除了该框架。一旦我将依赖项添加到正确的构建阶段并添加到主应用程序方案中,那么相关的日志消息就会停止出现(并且崩溃也停止了)。

换句话说,=== AttributeGraph: cycle detected through attribute &lt;#&gt; === 似乎不会直接导致崩溃或内存泄漏,但可能与导致(如缺少依赖项)SwiftUI 无法解析视图层次结构的其他问题有关。

YMMV.

【讨论】:

【参考方案4】:

我在父视图中删除这段代码,警告消失,

.focusedValue(\.path, $binding)

【讨论】:

我在 OPs 问题中看不到此代码。它到底有什么关系? 我在项目中遇到了同样的问题,这里只是一个track

以上是关于SwiftUI:如何在 UIViewRepresentable UITextField 上调用 becomeFirstResponder 时修复“通过属性检测到的 AttributeGraph 循环”的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 如何在 SwiftUI 中弹出到特定视图?

如何在 SwiftUI 中使用 Realm

如何在 UIkit 中添加 SwiftUI 对象

如何使用 SwiftUI 在 UICollectionViewCell 中填充内容

如何在 SwiftUI 中创建指数

如何在 SwiftUI 的 ScrollView 中制作动画? SwiftUI 中的手风琴风格动画