swiftui - 根据@State 变量的值变化重新加载视图
问题描述
我的视图顶部有一个标签。选项卡是动态的,选项卡下方显示的ListView()取决于选项卡索引。对于选项卡 0 索引,ListView()是固定的,但从索引 1 开始,ListView()将根据选定的选项卡索引重新加载。所以我当前选择的选项卡如下所示:
@State private var selectedTabIndex = 0
我现在像这样静态地重新加载视图:
if self.selectedTabIndex == 0 {
ListView(id: selectedTabIndex)
} else if self.selectedTabIndex == 1 {
ListView(id: selectedTabIndex)
} else if self.selectedTabIndex == 2 {
ListView(id: selectedTabIndex)
} else if self.selectedTabIndex == 3 {
ListView(id: selectedTabIndex)
} else if self.selectedTabIndex == 4 {
ListView(id: selectedTabIndex)
} else if self.selectedTabIndex == 5 {
ListView(id: selectedTabIndex)
}
这很好用,也可以重新加载 ListView() ,但这不是正确的方法,因为选项卡索引或多或少是动态的。所以我尝试了这样的事情,因为当我单击选项卡时,我的 selectedTabIndex 值正在发生变化:
if self.selectedTabIndex == 0 {
ListView(id: selectedTabIndex)
} else {
ListView(id: selectedTabIndex)
}
但它只重新加载第一次单击的选项卡而不是其他选项卡。请问我该如何解决?由于我是 SwiftUI 的新手,我有时会觉得很难。请帮忙。
更多详情:
import SwiftUI
struct WorkView: View {
@EnvironmentObject var navBarPreference: NavBarPreferences
@State private var selectedTabIndex = 0
@State private var isEmpty: Bool = true //
@State private var showWorkDetailView:Bool = false
@State var isCategoryAvailable:Bool = false
@ObservedObject var WorkVM = WorkViewModel() //
@State var workCategoryName: [String] = [" "]
@State var workCategoryID: [Int] = [0]
@State var selectedID: Int = 0
@State var workContentModel = [WorkContentModel]() //
var body: some View {
VStack(spacing: 0) {
HeaderView(title: "ワーク")
VStack {
if self.workCategoryName.isEmpty == false {
SlidingTab(selection: $selectedTabIndex, tabs: self.workCategoryName).padding(.top, 10)
}
}
Spacer().frame(maxHeight: 24)
//(selectedTabIndex == 0 ? SelectedFirstTab() : Text("Second View")).padding()
if self.selectedTabIndex == 0 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$selectedID)
} else if self.selectedTabIndex == 1 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$workCategoryID[self.selectedTabIndex])
} else if self.selectedTabIndex == 2 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$workCategoryID[self.selectedTabIndex])
} else if self.selectedTabIndex == 3 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$workCategoryID[self.selectedTabIndex])
} else if self.selectedTabIndex == 4 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$workCategoryID[self.selectedTabIndex])
} else if self.selectedTabIndex == 5 {
SelectedFirstTab(selectedTabIndex: self.selectedTabIndex, isCategoryAvailable: self.$isCategoryAvailable, workCategoryName: self.$workCategoryName, workCategoryID: self.$workCategoryID, selectedID: self.$workCategoryID[self.selectedTabIndex])
}
//Spacer()
}.frame(minWidth: SCREEN_WIDTH)
//.position(x: SCREEN_WIDTH/2, y: SCREEN_HEIGHT/2)
// .background(
// Image("PPImage")
// .resizable()
// .frame(minWidth: 375, minHeight: 945)
// //.aspectRatio(contentMode: .fit)
// )
.edgesIgnoringSafeArea(.top)
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(self.navBarPreference.navBarIsHidden)
.navigationBarBackButtonHidden(self.navBarPreference.navigationBarBackButtonHidden)
.onAppear{ self.navBarPreference.navBarIsHidden = true
self.navBarPreference.navigationBarBackButtonHidden = true }
}
}
struct SelectedFirstTab: View {
@State private var isEmpty: Bool = true
@ObservedObject var WorkVM = WorkViewModel()
@State var workContentModel = [WorkContentModel]()
var selectedTabIndex: Int = 0
@Binding var isCategoryAvailable:Bool
@Binding var workCategoryName: [String]
@Binding var workCategoryID: [Int]
@Binding var selectedID: Int
var body: some View {
VStack {
if self.workContentModel.isEmpty {
EmptyContent()
} else {
ScrollView(showsIndicators: false) {
ForEach(self.workContentModel) { content in
NavigationLink(destination: WorkDetailView(data: content)) {
WorkViewRow(data: content)
}.frame(minWidth: SCREEN_WIDTH, minHeight: 118)
.padding(.top, 5)
.padding(.bottom, 5)
}
}.frame(maxHeight: 600)
}
}
.onAppear {
if self.isCategoryAvailable == false {
self.WorkVM.getWorkList() { workCategory, content in
for i in 0..<workCategory.count {
self.workCategoryName.append(workCategory[i].name ?? " ")
self.workCategoryID.append(workCategory[i].id ?? 0)
}
self.workCategoryName.removeFirst()
self.workContentModel = content
self.isCategoryAvailable = true
self.isEmpty = false
// print("myContent:\(content)")
// print("workCategoryID:\(self.workCategoryID)")
self.selectedID = self.workCategoryID[self.selectedTabIndex]
print("selectedID:\(self.selectedID)")
}
} else if self.isCategoryAvailable == true {
self.WorkVM.getCategory(id: self.selectedID) { content in
self.workContentModel = content
self.isEmpty = false
self.selectedID = self.workCategoryID[self.selectedTabIndex]
print("selectedID:\(self.selectedID)")
}
}
}
}
}
struct EmptyContent: View {
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.clear)
.frame(maxHeight: 600)
VStack {
Image("EmptyBackgroundImage")
.resizable()
.frame(maxWidth: 150, maxHeight: 146)
CustomizedText(text: "表示されるワークは \n ありません", tracking: 2.2, frameHeight: 72, fontFamily: "HiraginoSans-W6", fontFamilySize: 22, lineLimit: 2)
.frame(maxWidth: 218, maxHeight: 72)
CustomizedText(text: "ここではワーク情報をお届けします。\n 最新のワークを探していますのでお待ちください。", tracking: 1.2, frameHeight: 42, fontFamily: "HiraginoSans-W3", fontFamilySize: 12, lineLimit: 2).frame(maxWidth: 333, maxHeight: 42)
Spacer().frame(maxHeight: 210)
}
}
}
}
滑块:_
import SwiftUI
struct SlidingTab: View {
// Internal state to keep track of the selection index
@State private var selectionState: Int = 0 {
didSet {
selection = selectionState
}
}
// Binding the selection index which will re-render the consuming view
@Binding var selection: Int
// The title of the tabs
private var tabs: [String]
init(selection: Binding<Int>, tabs: [String]) {
self._selection = selection
self.tabs = tabs
}
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 24.scale()) {
ForEach(self.tabs, id: \.self) { tab in
VStack {
Text(tab)
.frame(maxHeight: 55)
.font(.custom("HiraginoSans-W6", size: 15))
.foregroundColor(self.selection == self.tabs.firstIndex(of: tab) ? APP_TEXT_COLOR : APP_TEXT_COLOR.opacity(0.5))
Rectangle()
.frame(maxHeight: 3)
.foregroundColor(self.selection == self.tabs.firstIndex(of: tab) ? APP_COLOR : Color.clear)
.animation(.linear(duration: 0.25))
}.fixedSize()
.onTapGesture {
withAnimation {
let selection = self.tabs.firstIndex(of: tab) ?? 0
self.selectionState = selection
}
}
}
}.padding(.horizontal, 20.scale())
}
}
}
}
还有我的ViewModel:
import Foundation
import Combine
import Alamofire
import SwiftyJSON
class WorkViewModel: ObservableObject {
var updateValue = PassthroughSubject<WorkViewModel, Never>()
@Published var workCategory = [WorkCategoryModel]() {
willSet {
updateValue.send(self)
}
}
@Published var workContent = [WorkContentModel]() {
willSet {
updateValue.send(self)
}
}
func getWorkList(completionHandler: @escaping ([WorkCategoryModel], [WorkContentModel]) -> Void) {
AF.request(Router.get(endpoint: "/mobile/works/top"))
.responseDecodable(of: WorkTabModel.self) { response in
print("Original URL request WorkList Tab:\(String(describing: response.request))")
switch response.result {
case let .success(value):
if let workCategory = value.data.workCategories {
self.workCategory.append(contentsOf: workCategory)
}
// print("workCategory**:\(self.workCategory)")
if let workContent = value.data.work?.content {
self.workContent.append(contentsOf: workContent)
}
print("workContent**:\(self.workContent)")
completionHandler(self.workCategory, self.workContent)
case let .failure(error):
print("WorkModel Error**:\(error)")
}
}
}
func getCategory(id: Int, completionHandler: @escaping ([WorkContentModel]) -> Void) {
let urlString = "\(baseUrl)/mobile/works/top?work_category_id=\(id)"
let userToken = UserDefaults.standard.value(forKey: "access_token") ?? ""
let token = "Bearer \(userToken)"
let headers: HTTPHeaders = [
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": token
]
AF.request(urlString, headers: headers)
.responseJSON { response in
print("Original URL request WorkList Category:\(String(describing: response.request))")
print("Category JSON response:\(response)")
}
.responseDecodable(of: WorkTabModel.self) { response in
print("Original URL request WorkList Tab:\(String(describing: response.request))")
switch response.result {
case let .success(value):
if let workContent = value.data.work?.content {
self.workContent.append(contentsOf: workContent)
}
print("workContent**:\(self.workContent)")
completionHandler(self.workContent)
case let .failure(error):
print("WorkModel Error**:\(error)")
}
}
}
}
解决方案
Finally I got the solution. I was calling an API from .onAppear{} and when I clicked on different tab on the top slider I was getting an id and with that id I wanted to call the same API but with id. Even though I used @State variable for the id, the view was not reloading. So I called API from .onTapGesture{} . But still I had some difficulty, because .onTapGesture{} and @State value was changing simultaneously. So I used DispatchQueue.main.asyncAfter and called the API using the @State value from there. I am sharing the code below if that might help someone:
struct WorkView: View {
@EnvironmentObject var navBarPreference: NavBarPreferences
@State private var selectedTabIndex = 0
@State private var showWorkDetailView:Bool = false
@State var isCategoryAvailable:Bool = false
@ObservedObject var WorkVM = WorkViewModel() //
@State var workCategoryName: [String] = [" "]
@State var workCategoryID: [Int] = [0]
@State var workContentModel = [WorkContentModel]()
@State var hasShown: Bool = false
var body: some View {
VStack(spacing: 0) {
HeaderView(title: "ワーク")
VStack {
if self.workCategoryName.isEmpty == false {
SlidingTab(selection: $selectedTabIndex, tabs: self.workCategoryName).padding(.top, 10)
.onTapGesture {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
if self.isCategoryAvailable == true {
self.hasShown = true
//self.workContentModel.removeAll()
print("myWorkCategoryIDs:\(self.workCategoryID)")
print("mySelectedIndex:\(self.selectedTabIndex)")
let mySelectedID = self.workCategoryID[self.selectedTabIndex]
self.WorkVM.workContent.removeAll()
self.workContentModel.removeAll()
self.getContent(id: mySelectedID)
}
}
}
}
}
Spacer().frame(maxHeight: 24)
//(selectedTabIndex == 0 ? SelectedFirstTab() : Text("Second View")).padding()
VStack {
if self.workContentModel.isEmpty {
EmptyContent()
} else {
ScrollView(showsIndicators: false) {
ForEach(self.workContentModel) { content in
//Text("hihihihihihihihihi").frame(minWidth: 100, minHeight: 118)
NavigationLink(destination: WorkDetailView(data: content)) {
WorkViewRow(data: content)
}.frame(minWidth: SCREEN_WIDTH, minHeight: 118)
.padding(.top, 5)
.padding(.bottom, 5)
}
}.frame(maxHeight: 600)
}
}
//Spacer()
}.frame(minWidth: SCREEN_WIDTH)
.edgesIgnoringSafeArea(.top)
.navigationBarTitle("", displayMode: .inline)
.navigationBarHidden(self.navBarPreference.navBarIsHidden)
.navigationBarBackButtonHidden(self.navBarPreference.navigationBarBackButtonHidden)
.onAppear{ self.navBarPreference.navBarIsHidden = true
self.navBarPreference.navigationBarBackButtonHidden = true
if (self.hasShown == false && self.isCategoryAvailable == false) {
self.WorkVM.workContent.removeAll()
self.workContentModel.removeAll()
self.getCategoryWithContent()
}
}
}
func getCategoryWithContent() {
self.WorkVM.getWorkList() { workCategory, content in
for i in 0..<workCategory.count {
self.workCategoryName.append(workCategory[i].name ?? " ")
self.workCategoryID.append(workCategory[i].id ?? 0)
}
self.workCategoryName.removeFirst()
self.workContentModel = content
self.isCategoryAvailable = true
print("selectedTabIndex:\(self.selectedTabIndex)")
print("getCategoryWithContent called")
}
}
func getContent(id: Int) {
self.WorkVM.getCategory(id: id) { content in
self.workContentModel = content
print("selectedTabIndex:\(self.selectedTabIndex)")
print("getContent called")
}
}
}
struct EmptyContent: View {
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.clear)
.frame(maxHeight: 600)
VStack {
Image("EmptyBackgroundImage")
.resizable()
.frame(maxWidth: 150, maxHeight: 146)
CustomizedText(text: "表示されるワークは \n ありません", tracking: 2.2, frameHeight: 72, fontFamily: "HiraginoSans-W6", fontFamilySize: 22, lineLimit: 2)
.frame(maxWidth: 218, maxHeight: 72)
CustomizedText(text: "ここではワーク情報をお届けします。\n 最新のワークを探していますのでお待ちください。", tracking: 1.2, frameHeight: 42, fontFamily: "HiraginoSans-W3", fontFamilySize: 12, lineLimit: 2).frame(maxWidth: 333, maxHeight: 42)
Spacer().frame(maxHeight: 210)
}
}
}
}
推荐阅读
- mongodb - 有没有办法将mongodb与phoenix framework 1.4版本连接起来
- security - 无法通过谷歌云计算引擎实例中的外部 IP 连接
- php - 多个 foreach 使第一个数组为空
- python - 列出重复 3 次,为什么?
- javascript - 仅单击元素时如何停止触发onMouseDown?
- business-objects - OBIEE 11g 中的换行符
- llvm - 在 centos 7 上安装 LLVM / Clang 6+
- macos - 如何解决由于 Anaconda 和 Homebrew 引起的潜在冲突?
- django - Django - 如何访问 ForeignKey 父级的属性?
- ios - 使用 Google Places API 时出现错误“获取输出帧失败,状态 8196”