1

I previously asked a question about how to link an array of TabButtons to .tag() here: .tag() in TabView in SwiftUI challenge, but now I want to customize each TabView to have different information and not just the one line of text that reads from the enum cases that was created. In the below code I added additional views that I would like to populate with each TabView. I want to be able to insert additional tabs/views in the future. I've created the custom views at the bottom, but not sure how to link with the enums and then use a ForEach in TabView.

import SwiftUI

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

struct CustomTabView: View {

   @State var selectedTab = ViewSelect.HomeView // 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
   var text : Int

}

public enum ViewSelect: String, CaseIterable {
   case HomeView, EnvelopeView, FolderView, SettingsView

   var tabIcon: String {
       switch self {
       case .HomeView:
           return "house"
       case .EnvelopeView:
           return "envelope"
       case .FolderView:
           return "folder"
       case .SettingsView:
           return "gear"

       }
   }

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

struct DataForViews : Identifiable {
   
   var id = UUID().uuidString
   var someText : SomeText
   var someImage : SomeImage
   var someData : String
   var moreText : String
   
   enum SomeText : String, CaseIterable {
       case friends
       case enemies
       case neutral
       case both
   }
   
   enum SomeImage : String, CaseIterable {
       case friends = "person.3"
       case enemies = "person.fill.xmark"
       case neutral = "person"
       case both = "person.circle"
   }
}


struct HomeView: View {
   
   @State var viewData = DataForViews(someText: .friends, someImage: .friends, someData: "Some Data", moreText: "More Text")
   
   var body: some View {
       VStack {
           Text(viewData.someText.rawValue)
           Image(systemName: viewData.someImage.rawValue)
               .resizable()
               .frame(width: 40, height: 40)
           
           Text(viewData.someData)
           Text(viewData.moreText)
       }
   }
}

struct EnvelopeView: View {
   
   @State var viewData = DataForViews(someText: .enemies, someImage: .enemies, someData: "Some Data", moreText: "More Text")
   
   var body: some View {
       VStack {
           Text(viewData.someText.rawValue)
           Image(systemName: viewData.someImage.rawValue)
               .resizable()
               .frame(width: 40, height: 40)
           
           
           Text(viewData.moreText)
       }
   }
}

struct FolderView: View {
   
   @State var viewData = DataForViews(someText: .neutral, someImage: .neutral, someData: "Some Data", moreText: "More Text")
   
   var body: some View {
       VStack {
           Text(viewData.someText.rawValue)
           Image(systemName: viewData.someImage.rawValue)
               .resizable()
               .frame(width: 40, height: 40)
           
           Text(viewData.someData)
           Text(viewData.moreText)
       }
   }
}

struct SettingsView: View {
   
   @State var viewData = DataForViews(someText: .both, someImage: .both, someData: "Some Data", moreText: "More Text")
   
   var body: some View {
       VStack {
           Text(viewData.someText.rawValue)
           Image(systemName: viewData.someImage.rawValue)
               .resizable()
               .frame(width: 40, height: 40)
           
           Text(viewData.someData)
           
       }
   }
} 
Asperi
  • 228,894
  • 20
  • 464
  • 690
Laren
  • 37
  • 1
  • 8
  • The problem is not clear - what stops you do that? – Asperi Dec 20 '21 at 06:43
  • I actually don't know how to do it. I don't know how to implement the HomeView(), EnvelopeView(), FolderView(), and SettingsView() into the TabView() ForEach loop or is there a different way to approach this? I understand you can do it via TabView() then call the view, then the tabItem, then finally the .tag(). But if I were to insert new views along with the tabs refactoring the .tag() becomes cumbersome. @Asperi – Laren Dec 20 '21 at 09:03

1 Answers1

0

I think I figured this out. It took me a lot of tinkering and I came here to stack overflow to try and get further clarity. But the below solution should work. I created a variable in the HomeView() that linked the ViewSelect as an enum, then I initialized the different options with an array in the DataForViews struct and linked the selection to the ViewSelect enum which is tied to the tabItems.

Then in the ContentView, I followed the same pattern except used the ForEach loop on the HomeView() and for the .tag() it is tied to the selection within the DataForViews.

struct CustomTabView: View {

    @State var selectedTab = ViewSelect.HomeView // 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(viewDataStuff) { view in
                    HomeView(viewData: view)
                        .tag(view.selection)
                }
struct DataForViews : Identifiable, Hashable {
    
    var id = UUID().uuidString
    var someText : SomeText
    var someImage : SomeImage
    var someData : String
    var moreText : String
    var selection : ViewSelect
    
    enum SomeText : String, CaseIterable {
        case friends
        case enemies
        case neutral
        case both
    }
    
    enum SomeImage : String, CaseIterable {
        case friends = "person.3"
        case enemies = "person.fill.xmark"
        case neutral = "person"
        case both = "person.circle"
    }
}
var viewDataStuff = [
    
    DataForViews(someText: .friends, someImage: .friends, someData: "Text", moreText: "Text", selection: .HomeView),
    DataForViews(someText: .enemies, someImage: .enemies, someData: "Text", moreText: "Text", selection: .EnvelopeView),
    DataForViews(someText: .neutral, someImage: .neutral, someData: "Text", moreText: "Text", selection: .FolderView),
    DataForViews(someText: .both, someImage: .both, someData: "Text", moreText: "Text", selection: .SettingsView),
]


struct HomeView: View {
    
    @State var viewData : DataForViews
    
    var body: some View {
        VStack {
            Text(viewData.someText.rawValue)
            Image(systemName: viewData.someImage.rawValue)
                .resizable()
                .frame(width: 40, height: 40)
            
            Text(viewData.someData)
            Text(viewData.moreText)
        }
    }
}
'''
Laren
  • 37
  • 1
  • 8