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