SwiftUI 挑战中 TabView 中的 .tag()

.tag() in TabView in SwiftUI challenge

我在解决这个问题时遇到了一些挑战。在下面的代码中,我创建了一个 TabView 并能够使用 .tag() 切换哪个选项卡处于活动状态,但我有一个水平滚动视图的选项卡项目。每个选项卡屏幕都显示类似的信息,因此我使用单个视图来创建它,然后使用 ForEach 循环遍历这些项目。但它破坏了 .tag() 函数,我似乎无法弄清楚如何使用 ForEach 循环让它工作。关于如何解决这个问题或我缺少什么的任何想法?

import SwiftUI
\ Working code with normal Swipable tabs using .tag()
struct ContentView: View {
    var body: some View {
        
        CustomTabView()
        
    }
}

struct CustomTabView: View {
    
    @State var selectedTab = "house"
    
    var body: some View {
        
        ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
            
            TabView(selection: $selectedTab) {
                
                HouseView()
                    .tag("house")
                
                EnvelopeView()
                    .tag("envelope")
                
                FolderView()
                    .tag("folder")
                
                SettingsView()
                    .tag("gear")
                
                SliderView()
                    .tag("slider.horizontal.3")

                VideoView()
                    .tag("video")

                UpView()
                    .tag("arrow.up.doc")

            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .ignoresSafeArea(.all, edges: .bottom)
            
            ScrollView(.horizontal, showsIndicators: false, content: {
                
                HStack {
                    
                    ForEach(tabs,id: \.self){image in
                        
                        TabButton(image: image, selectedTab: $selectedTab)

                    }
                }

            })
            .padding(.horizontal, 25)
            .padding(.vertical, 5)
            .background(Color.white)
            .clipShape(Capsule())
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: 5, y: 5)
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: -5, y: -5)
            .padding(.horizontal)
        }
        .ignoresSafeArea(.keyboard, edges: .bottom)
        .background(Color.black.opacity(0.05).ignoresSafeArea(.all, edges: .all))
    }
}

// tab...

var tabs = ["house", "envelope", "folder", "gear", "slider.horizontal.3", "video", "arrow.up.doc"]

struct TabButton: View {
    
    var image : String
    @Binding var selectedTab : String
    
    var body: some View {
        
        Button(action: {selectedTab =  image}) {
            
            Image(systemName: image)
                .renderingMode(.template)
                .opacity(self.selectedTab == image ? (1) : (0.5))
                .padding()
        }
    }
}

struct HouseView: View {

    var body: some View {
  
         VStack {
            Text("House View")

      }
   }
}

struct EnvelopeView: View {

    var body: some View {
  
         VStack {
            Text("Envelope View")

      }
   }
}

struct FolderView: View {

    var body: some View {
  
         VStack {
            Text("Folder View")

      }
   }
}

struct SettingsView: View {

    var body: some View {
  
         VStack {
            Text("Settings View")

      }
   }
}

struct SliderView: View {

    var body: some View {
  
         VStack {
            Text("Slider View")

      }
   }
}

struct VideoView: View {

    var body: some View {
  
         VStack {
            Text("Video View")

      }
   }
}

struct UpView: View {

    var body: some View {
  
         VStack {
            Text("Up View")

      }
   }
}

在 TabView 中的视图上的 ForEach 循环之后的问题代码。视图填充但它破坏了 .tag() 功能,我不知道如何让它再次工作。

import SwiftUI

struct ContentView: View {
    var body: some View {
        
        CustomTabView()
        
    }
}

struct CustomTabView: View {

    @State var selectedTab = "house"
    @State var viewData : AllViewData!

    var body: some View {

        ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {

            TabView(selection: $selectedTab) {

                ForEach(allViews){views in

                    ViewsCardView(viewData: views)
                        .tag("//not sure if this is the right place for the .tag()")
                }

            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .ignoresSafeArea(.all, edges: .bottom)

            ScrollView(.horizontal, showsIndicators: false, content: {

                HStack {

                    ForEach(tabs,id: \.self){image in

                        TabButton(image: image, selectedTab: $selectedTab)

                    }
                }

            })
            .padding(.horizontal, 25)
            .padding(.vertical, 5)
            .background(Color.white)
            .clipShape(Capsule())
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: 5, y: 5)
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: -5, y: -5)
            .padding(.horizontal)
        }
        .background(Color.black.opacity(0.05).ignoresSafeArea(.all, edges: .all))
    }
}

// tab...

var tabs = ["house", "envelope", "folder", "gear", "slider.horizontal.3", "video", "arrow.up.doc"]

struct TabButton: View {

    var image : String
    @Binding var selectedTab : String

    var body: some View {

        Button(action: {selectedTab =  image}) {

            Image(systemName: image)
                .renderingMode(.template)
                .opacity(self.selectedTab == image ? (1) : (0.5))
                .padding()
        }
    }
}

struct ViewsCardView: View {

     @State var viewData : AllViewData!

     var body: some View {

         VStack {

           Text(viewData.name)

       }
    }
}

struct AllViewData : Identifiable {

    var id = UUID().uuidString
    var name : String

}

var allViews = [

    AllViewData(name: "House"),
    AllViewData(name: "Envelope"),
    AllViewData(name: "Folder"),
    AllViewData(name: "Settings"),
    AllViewData(name: "Slider"),
    AllViewData(name: "Video"),
    AllViewData(name: "Up"),

]


您遇到的问题是您输入的所有内容都是字符串。因此,您只是无法访问与标签栏中每个视图关联的图像。您的 ForEach 遍历您的 allViews,它只有视图的名称,没有图像。因此,当您需要设置选项卡时,您没有可用的图像名称。我将您的字符串更改为一个枚举,它为您提供了一组有限的选择,并且可以更好地控制您要完成的任务。很多教程在尝试演示某些内容时都使用强类型,但这是一个坏习惯,会造成难以发现的错误。

struct ContentView: View {
    var body: some View {
        
        CustomTabView()
        
    }
}

struct CustomTabView: View {
    
    @State var selectedTab = ViewSelect.House // This is an enum defined below.
    @State var viewData : AllViewData!
    
    var body: some View {
        
        ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
            
            TabView(selection: $selectedTab) {
                ForEach(ViewSelect.allCases, id: \.self) { view in // This iterates through all of the enum cases.
                    ViewsCardView(viewData: view.allViewData)
                        .tag(view.rawValue) // by having the tag be the enum's raw value,
                                            // you can always compare enum to enum.
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .ignoresSafeArea(.all, edges: .bottom)
            
            ScrollView(.horizontal, showsIndicators: false, content: {
                
                HStack {
                    
                    ForEach(ViewSelect.allCases ,id: \.self){viewSelect in
                        
                        TabButton(viewSelect: viewSelect, selectedTab: $selectedTab)
                        
                    }
                }
                
            })
            .padding(.horizontal, 25)
            .padding(.vertical, 5)
            .background(Color.white)
            .clipShape(Capsule())
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: 5, y: 5)
            .shadow(color: Color.black.opacity(0.15), radius: 5, x: -5, y: -5)
            .padding(.horizontal)
        }
        .background(Color.black.opacity(0.05).ignoresSafeArea(.all, edges: .all))
    }
}

struct TabButton: View {
    let viewSelect: ViewSelect
    @Binding var selectedTab: ViewSelect
    
    var body: some View {
        
        Button(action: {selectedTab = viewSelect}) {
            
            Image(systemName: viewSelect.tabIcon)
                .renderingMode(.template)
                // this compares the selection to the button's associated enum.
                // The enum provides the image name to the button,
                // but we are always dealing with a case of the enum.
                .opacity(selectedTab == viewSelect ? (1) : (0.5))
                .padding()
        }
    }
}

struct ViewsCardView: View {
    
    @State var viewData: AllViewData
    
    var body: some View {
        
        VStack {
            
            Text(viewData.name)
            
        }
    }
}

struct AllViewData : Identifiable, Hashable {
    
    var id = UUID().uuidString
    var name : String
    
}

public enum ViewSelect: String, CaseIterable {
    case House, Envelope, Folder, Settings, Slider, Video, Up
    
    var tabIcon: String {
        switch self {
        case .House:
            return "house"
        case .Envelope:
            return "envelope"
        case .Folder:
            return "folder"
        case .Settings:
            return "gear"
        case .Slider:
            return "slider.horizontal.3"
        case .Video:
            return "video"
        case .Up:
            return "arrow.up.doc"
        }
    }

    var allViewData: AllViewData {
        return AllViewData(name: self.rawValue)
    }
}