当@State变量以高频率刷新视图时,导航栏-/工具栏按钮不可靠

Posted

技术标签:

【中文标题】当@State变量以高频率刷新视图时,导航栏-/工具栏按钮不可靠【英文标题】:Navigationbar- / Toolbar Button not working reliable when @State variable refresh the view with a high frequency 【发布时间】:2020-08-22 20:03:12 【问题描述】:

我注意到,当至少有一个 @State 变量经常刷新视图时,导航栏/工具栏按钮无法正常工作。

我创建了一个简单的应用程序来测试它。

使用下面的代码示例,您有 3 个选项来触发模式表。主视图中的一个按钮,工具栏中的一个按钮和导航栏中的一个按钮。

当我的计时器没有更新“数字”时,所有 3 个按钮都可以正常工作。当我启动计时器时,它将每 0,1 秒刷新一次视图,每次只有主视图中的按钮才能工作。工具栏/导航栏中的按钮大部分时间都不起作用。 (我的计时器的 TimeInterval 越短,按钮工作的越少)

import SwiftUI

struct ContentView: View 

    @State private var number = 0
    @State private var showModal = false

    func startTimer() 
        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true)  (_) in
            number += 1
        
    

    var body: some View 

        NavigationView 
            VStack 
                Text("Count \(number)")
                Button(action: startTimer, label: 
                    Text("Start Timer")
                )

                Button(action:  showModal.toggle() , label: 
                    Text("Open Modal")
                )
            
            .navigationBarTitle("Home", displayMode: .inline)
            .navigationBarItems(leading:
                                    Button(action: 
                                        showModal.toggle()
                                    , label: 
                                        Text("Open Modal")
                                    ))

            .toolbar 
                ToolbarItem(placement: .bottomBar) 
                    Button(action: 
                        showModal.toggle()
                    , label: 
                        Text("Open Modal")
                    )
                
            
        

        .sheet(isPresented: $showModal, content: 
            Text("Hello, World!")
        )

    

有没有人知道是否有办法让这 2 个按钮正常工作?

Xcode 12 beta 5 和 Xcode Xcode 11.6 会出现此问题(没有工具栏,因为那里不可用)

【问题讨论】:

【参考方案1】:

提供的变体中的number 状态会刷新整个视图,这是问题的结果,对于 UI 来说不是很理想。

导航栏的解决方案,一般来说风格不错,就是将刷新部分分离到独立视图中,所以SwiftUI渲染引擎只重建它。

使用 Xcode 12b3 / ios 14 测试

struct TestOftenUpdate: View 

    @State private var showModal = false

    var body: some View 

        NavigationView 
            VStack 
                QuickTimerView()
                Button(action:  showModal.toggle() , label: 
                    Text("Open Modal")
                )
            
            .navigationBarTitle("Home", displayMode: .inline)
            .navigationBarItems(leading:
                                    Button(action: 
                                        showModal.toggle()
                                    , label: 
                                        Text("Open Modal")
                                    ))

            .toolbar 
                ToolbarItem(placement: .bottomBar) 
                    Button(action: 
                        showModal.toggle()
                    , label: 
                        Text("Open Modal")
                    )
                
            
        

        .sheet(isPresented: $showModal, content: 
            Text("Hello, World!")
        )

    


struct QuickTimerView: View 
    @State private var number = 0

    var body: some View 
        VStack 
            Text("Count \(number)")
            Button(action: startTimer, label: 
                Text("Start Timer")
            )
        
    

    func startTimer() 
        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true)  (_) in
            number += 1
        
    


toolbar 是不同的问题 - 即使没有计时器视图,在打开第一张工作表后它也不起作用,这就是原因

我们看到按钮布局已经损坏并且超出了工具栏区域,这就是命中测试失败的原因 - 这是应该报告的 Apple 缺陷。

解决方法 1:

将工具栏放置在导航视图之外(视觉上工具栏较小,但有时可能合适,因为按钮可以工作)

    NavigationView 
       // ... other code
    
    .toolbar 
        ToolbarItem(placement: .bottomBar) 
            Button(action: 
                showModal.toggle()
            , label: 
                Text("Open Modal")
            )
        
    

【讨论】:

非常感谢您的帮助。我总是只将更大的视图放入自己的独立视图中,而不仅仅是文本标签。我想我一般需要使用更多单独的视图。工具栏的问题似乎已经在 Xcode 12b5 / iOS 14 中得到修复。有没有办法在 Xcode/simulator 中可视化当 UI 中的某些内容发生变化时重新渲染 UI 的哪些部分?【参考方案2】:

Xcode 13.1,iPhone 13 模拟器目标(和画布预览)。

其他解决方案都不适合我,但我确实发现这只会影响顶部导航按钮,并且如果导航栏标题很大。

所以起作用的事情似乎是:

    设置内嵌标题
    .navigationBarTitleDisplayMode(.inline)
    
    无标题 没有顶部边缘工具栏(仅底部栏)

如果问题确实出现了,我可以下拉视图中包含的列表(无需启用可刷新),这也将恢复按钮功能。

【讨论】:

这对我来说效果很好,毕竟其他选项都没有。谢谢! 我确实发现了同样的问题并将栏标题设置为内联而不是大标题对我有用。 谢谢,那个内联修饰符成功了。我已经在嵌套子视图并且不知道这是修复方法 为我工作,Xcode 13.2

以上是关于当@State变量以高频率刷新视图时,导航栏-/工具栏按钮不可靠的主要内容,如果未能解决你的问题,请参考以下文章

导航栏中的 SwiftUI 元素不响应状态

当导航栏是超级视图视图时,如何在导航栏后面放置视图?

在导航栏中按下后退按钮时刷新 RootViewController 的内容

标签栏/导航刷新

刷新导航栏背景颜色

按下后退按钮时,自动折叠移动设备上的下拉导航栏菜单