【问题标题】:Why onAppear called again after onDisappear while switching tab in TabView in SwiftUI?为什么在 SwiftUI 的 TabView 中切换选项卡时 onDisappear 后再次调用 onAppear?
【发布时间】:2020-11-24 21:09:13
【问题描述】:

如果有任何更改,我会在标签项出现时调用 API。为什么在调用 onDisappear 之后调用 onAppear?

这是一个简单的例子:

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                Text("Home")
                    .navigationTitle("Home")
                    .onAppear {
                        print("Home appeared")
                    }
                    .onDisappear {
                        print("Home disappeared")
                    }
            }
            .tabItem {
                Image(systemName: "house")
                Text("Home")
            }.tag(0)
        
            NavigationView {
                Text("Account")
                    .navigationTitle("Account")
                    .onAppear {
                        print("Account appeared")
                    }
                    .onDisappear {
                        print("Account disappeared")
                    }
            }
            .tabItem {
                Image(systemName: "gear")
                Text("Account")
            }.tag(1)
        }
    }
}    

只要运行上面的代码,我们就会在 onDisappear 之后看到 onAppear。

Home appeared
---After switch tab to Account---
Home disappeared
Account appeared
Home appeared

有什么办法可以避免这种情况吗?

【问题讨论】:

  • 您无法控制 SwiftUI 如何刷新视图,最好不要依赖它。为什么要避免多个onAppear
  • 因为在切换选项卡后刷新整个视图时调用嵌套视图(tab>navigation>view1>view2)的初始化。
  • why 对我来说不是建设性问题...您是否考虑根据您尝试实现(或解决)什么来重新提出您的问题,然后...有人可能会发现如何做到这一点。
  • 我不知道为什么围绕这个问题的消极情绪。多年来,我们一直依赖 viewWillAppear / viewWillDisappear 以某种方式工作。在 iOS14 Beta 上运行 iOS13 编译的应用程序时,我也遇到了同样的问题。当推送一个详细信息屏幕时,再次调用 NavigationView 中的 .onAppear。
  • 您找到解决方案了吗?这似乎仍然是 iOS 14 稳定版中的行为。

标签: ios swiftui tabview


【解决方案1】:

这是一个非常烦人的错误,想象一下这种情况: Home view onAppear 方法包含一个重复获取数据的计时器。 通过切换到Account 视图可以无形地触发计时器。

解决方法:

  1. 为每个嵌入的 NavigationView 内容创建一个独立视图
  2. 将当前选择值作为@Binding 参数传递给独立视图

例如:

struct ContentView: View {
    @State var selected: MenuItem = .HOME

    var body: some View {
        return TabView(selection: $selected) {
            HomeView(selectedMenuItem: $selected)
                .navigationViewStyle(StackNavigationViewStyle())
                .tabItem {
                    VStack {
                        Image(systemName: "house")
                        Text("Home")
                    }
                }
                .tag(MenuItem.HOME)
            
            AccountView(selectedMenuItem: $selected)
                .navigationViewStyle(StackNavigationViewStyle())
                .tabItem {
                    VStack {
                        Image(systemName: "gear")
                        Text("Account")
                    }
                }
                .tag(MenuItem.ACCOUNT)
        }
    }
}

enum MenuItem: Int, Codable {
    case HOME
    case ACCOUNT
}

主页视图:

struct HomeView: View {

    @Binding var selectedMenuItem: MenuItem

    var body: some View {
        return Text("Home")
            .onAppear(perform: {
                if MenuItem.HOME == selectedMenuItem {
                    print("-> HomeView")
                }
            })
    }
}

帐户视图:

struct AccountView: View {

    @Binding var selectedMenuItem: MenuItem

    var body: some View {
        return Text("Account")
            .onAppear(perform: {
                if MenuItem.ACCOUNT == selectedMenuItem {
                    print("-> AccountView")
                }
            })
    }
}

【讨论】:

    【解决方案2】:

    我不确定您为什么会在您的应用中看到这种行为。但我可以解释为什么我会在我的应用中看到它。

    我的设置与您非常相似,并且在 iOS14 测试版上运行 iOS13 应用时看到了相同的行为。在我的主屏幕中,我有一个自定义选项卡栏,当显示详细信息屏幕时,它会以动画方式进出。触发 Tab Bar 隐藏的代码是在 Detail 屏幕的 .onAppear 中完成的。这会触发主屏幕重绘并调用 .onAppear。由于这个错误,我删除了动画并找到了更好的设置,并且主屏幕 .onAppear 停止被调用。

    因此,如果您的帐户屏幕 .onAppear 中的某些内容在主屏幕上具有视觉效果,请尝试将其注释掉,看看它是否能解决问题。

    祝你好运。

    【讨论】:

      【解决方案3】:

      对谁有帮助。

      因为这种行为我只能在 iOS 14+ 上重现,所以我最终使用了https://github.com/NicholasBellucci/StatefulTabView(只有在显示时才会正确调用;但不知道it's a bug or not,但它适用于版本 0.1.8)和 iOS 13+ 上的 TabView。

      【讨论】:

      • 对我来说这绝对是 iOS 14 的错误。从功能上讲,不应该再次调用初始视图的onAppear(它没有出现),尤其是在调用新视图的onAppear之后。感谢您指出这个包,我设法在不到 5 分钟的时间内使用 StatefulTabView 解决了这个问题。它甚至可以在 macOS 上运行!