首页 > 解决方案 > 根据@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 ありません&quot;, tracking: 2.2, frameHeight: 72, fontFamily: "HiraginoSans-W6", fontFamilySize: 22, lineLimit: 2)
                .frame(maxWidth: 218, maxHeight: 72)
            CustomizedText(text: "ここではワーク情報をお届けします。\n 最新のワークを探していますのでお待ちください。&quot;, 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)")
               }
       }
    
}
}

标签: swiftui

解决方案


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: "ワーク&quot;)
        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 ありません&quot;, tracking: 2.2, frameHeight: 72, fontFamily: "HiraginoSans-W6", fontFamilySize: 22, lineLimit: 2)
                .frame(maxWidth: 218, maxHeight: 72)
            CustomizedText(text: "ここではワーク情報をお届けします。\n 最新のワークを探していますのでお待ちください。&quot;, tracking: 1.2, frameHeight: 42, fontFamily: "HiraginoSans-W3", fontFamilySize: 12, lineLimit: 2).frame(maxWidth: 333, maxHeight: 42)
            Spacer().frame(maxHeight: 210)
            
        }
    }
}
}


 

推荐阅读