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

Neg*_*ity 5 swiftui swiftui-navigationview

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

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

我将发布一些代码,希望有人能够看到我做错了什么,因为我肯定不能。

内容视图

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
            }
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

然后我的用户列表

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
                }
            }
        }
    }
}
}
Run Code Online (Sandbox Code Playgroud)

我的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 = ""
}
Run Code Online (Sandbox Code Playgroud)

在 Xcode 的控制台中我看到

Inside the userlist on appear
...(network stuff)
In the success
In the on appear in ChatList
Run Code Online (Sandbox Code Playgroud)

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

Sco*_*man 5

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

您应该.tag在每个选项卡后添加一个修饰符,.tabItem以告诉 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)
}
Run Code Online (Sandbox Code Playgroud)

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