TabView 无法在 SwiftUI 中正确切换选项卡

TabView is not switching tabs properly in SwiftUI

我有一个奇怪的问题,我似乎无法用 SwiftUI 解决。我的 ContentView 中有一个 TabView,应用程序在聊天列表选项卡上加载了 3 个选项卡(聊天列表、用户列表和个人资料)。问题是,当我 select 第二个选项卡(用户列表)时,它会瞬间转到该选项卡,然后立即返回聊天列表。这对我来说没有任何意义。 2 个主要选项卡进行 API 调用以从服务器获取信息,一切正常,除了第一次点击。

应用程序加载完毕,我单击用户列表选项卡,它显示了一秒钟,然后返回到聊天列表选项卡。然后我可以再次单击用户列表选项卡,它会转到该选项卡并停留在那里,但第一次单击该选项卡总是会返回到聊天列表选项卡。

我会 post 编写一些代码,希望有人能够看到我做错了什么,因为我确实看不到。

内容视图

struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext

@State var selectedTab = 0
@State var setup: Bool = false
@State var notificationChatID: String = ""

@ObservedObject var userModel: UserModel = UserModel()
@ObservedObject var chatModel: ChatModel = ChatModel()
@ObservedObject var appState: AppState = AppState.shared

var pushNavigationBinding : Binding<Bool> {
    .init { () -> Bool in
        appState.selectedChatID != nil
    }
    set: { (newValue) in
        if !newValue {
            appState.selectedChatID = nil
        }
    }
}

let settings = UserDefaults.standard

var body: some View {
    ZStack {
        if setup {
            TabView(selection: $selectedTab) {
                ChatList(launchedChatID: appState.selectedChatID ?? "", userModel: userModel, chatModel: chatModel)
                    .tabItem {
                        Image(systemName: "message.circle.fill")
                        Text("Active Chats")
                    }
                UserList(userModel: userModel)
                    .tabItem {
                        Image(systemName: "person.3.fill")
                        Text("User List")
                    }
                ProfileView()
                    .tabItem {
                        Image(systemName: "person.crop.circle")
                        Text("Profile")
                    }
            }
        } else {
            Onboarding(userModel: userModel, isSetup: $setup)
        }
    }
    .onReceive(NotificationCenter.default.publisher(for: Notification.Name.NewMessage), perform: { notification in
        if let info = notification.userInfo {
            let chatID = info["chatID"] as? String ?? ""
            if chatID != "" {
                chatModel.selectedChat = chatID
                appState.selectedChatID = chatID
                self.notificationChatID = chatID
            }
        }
    })
}

然后是我的用户列表

struct UserList: View {

@ObservedObject var userModel: UserModel
@ObservedObject var chatModel: ChatModel = ChatModel()

@State var action: Int? = -1

var body: some View {
    NavigationView {
        VStack {
            List {
                ForEach(0..<userModel.arrayOfUsers.count, id: \.self) { index in
                    ZStack(alignment: .leading) {
                        NavigationLink(
                            destination: ChatView(model: chatModel, userModel: userModel, item: $action),
                            tag: index,
                            selection: $action
                        ) {
                            EmptyView().frame(width: .zero, height: .zero, alignment: .center)
                        }
                        Button(action: {
                            print("You selected \(userModel.arrayOfUsers[index].name)")
                            userModel.selectedUserName = userModel.arrayOfUsers[index].name
                            userModel.selectedUserID = userModel.arrayOfUsers[index].id
                            self.action = index
                        }) {
                            Text(userModel.arrayOfUsers[index].name)
                        }
                    }
                }
            }
            Spacer()
        }.navigationBarTitle(Text("Users"), displayMode: .inline)
    }.onAppear() {
        print("Inside the userlist on appear")
        if userModel.arrayOfUsers.count == 0 {
            ApiService.getUsers() { (res) in
                switch(res) {
                case .success(let response):
                    print("In success")
                    let users = response!.users!
                    DispatchQueue.main.async {
                        for user in users.users {
                            userModel.arrayOfUsers.append(user)
                        }
                    }
                    break
                case .failure(let error):
                    print("Error getting users")
                    print(error)
                    break
                }
            }
        }
    }
}
}

我的 userModel.arrayOfUsers@Published 用户模型

class UserModel: ObservableObject {
    var name: String = ""
    var id: String = ""
    var myUserID: String = ""
    @Published var arrayOfUsers: [User] = []
    var selectedUserID: String = ""
    var selectedUserName: String = ""
}

在控制台中 Xcode 我看到了

Inside the userlist on appear
...(network stuff)
In the success
In the on appear in ChatList

所以它正在加载 UserList,它显示了对我的 API 的网络调用,它显示了来自 UserList 中 API 调用的 In the success,然后是接下来的事情回到 In the on appear in ChatList 我不明白为什么它把我踢回聊天列表。

您将 TabView 的当前选项卡绑定到 $selectedTab,但未向 SwiftUI 提供任何有关在用户更改选项卡时如何更改该值的信息。所以,因为 selectedTab 没有改变,当绘图系统来审查你的视图结构时,它仍然得出你想看到第一个选项卡的结论。

你应该在每个 .tabItem 之后添加一个 .tag 修饰符来告诉 SwiftUI 什么值代表每个选项卡。然后,当用户选择每个选项卡时,selectedTab 将更新并且选项卡选择将“固定”。

例如:

TabView(selection: $selectedTab) {
    ChatList(launchedChatID: appState.selectedChatID ?? "", userModel: userModel, chatModel: chatModel)
        .tabItem {
            Image(systemName: "message.circle.fill")
            Text("Active Chats")
        }
        .tag(0)
    UserList(userModel: userModel)
        .tabItem {
            Image(systemName: "person.3.fill")
            Text("User List")
        }
        .tag(1)
    ProfileView()
        .tabItem {
            Image(systemName: "person.crop.circle")
            Text("Profile")
        }
        .tag(2)
}

请注意,除非您以某种方式坚持用户的选择(例如,通过使用 @SceneStorage 声明您的状态变量),否则您可以通过不使用 selection 参数来获得相同的效果全部.