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)
}
}
我在解决这个问题时遇到了一些挑战。在下面的代码中,我创建了一个 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)
}
}