根据@State变量的值更改重新加载视图

时间:2020-08-06 05:07:49

标签: swiftui

我的视图上方有一个选项卡。选项卡是动态的,并且在选项卡下方显示的 ListView()取决于选项卡索引。对于标签0索引, ListView()是固定的,但是从索引1开始, ListView()将根据所选标签索引重新加载。因此,我当前选择的标签如下:

@State private var selectedTabIndex = 0

我现在像这样静态地重新加载View:

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)")
               }
       }
    
}
}

1 个答案:

答案 0 :(得分:0)

最后我找到了解决方案。我从 .onAppear {} 调用API,当我单击顶部滑块上的其他选项卡时,我得到一个 id ,并带有该 id 我想使用 id 调用相同的API。即使我为 id 使用了 @State 变量,该视图也没有重新加载。因此,我从 .onTapGesture {} 调用了API。但是我仍然遇到了一些困难,因为 .onTapGesture {} @State 的值是同时变化的。因此,我使用了 DispatchQueue.main.asyncAfter ,然后使用 @State 值调用了API。如果可以帮助某人,我将分享以下代码:

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)
            
        }
    }
}
}