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
参数来获得相同的效果全部.
我有一个奇怪的问题,我似乎无法用 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
参数来获得相同的效果全部.