我有重复的调查步骤代码,我试图使其可重用:
private var workoutTypeSection: some View {
VStack(spacing: 20) {
header("What Is Your Preferred Workout?")
ForEach(WorkoutType.allCases) { workoutType in
Text(workoutType.rawValue)
.font(.headline)
.foregroundColor(.purple)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(selectedWorkoutTypes.contains(workoutType) ?
Color.blue : Color.white)
.cornerRadius(10)
.onTapGesture {
workoutButtonPressed(workout: workoutType)
}
}
}
.padding(30)
}
private var bodyPartSection: some View {
VStack(spacing: 20) {
header("Select a Body Part to Strengthen:")
ForEach(BodyPart.allCases) { bodyPart in
Text(bodyPart.rawValue)
.font(.headline)
.foregroundColor(.purple)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(selectedBodyParts.contains(bodyPart) ?
Color.blue : Color.white)
.cornerRadius(10)
.onTapGesture {
bodyPartButtonPressed(part: bodyPart)
}
}
}
.padding(30)
}
OnTapGesture 选择或取消选择调查选项:
func workoutButtonPressed(workout: WorkoutType) {
if selectedWorkoutTypes.contains(workout) {
if let index = selectedWorkoutTypes.firstIndex(of: workout) {
selectedWorkoutTypes.remove(at: index)
}
} else {
selectedWorkoutTypes.append(workout)
}
}
func bodyPartButtonPressed(part: BodyPart) {
if selectedBodyParts.contains(part) {
if let index = selectedBodyParts.firstIndex(of: part) {
selectedBodyParts.remove(at: index)
}
} else {
selectedBodyParts.append(part)
}
}
哪里
@State var selectedWorkoutTypes: [WorkoutType] = []
@State var selectedBodyParts: [BodyPart] = []
枚举类型为:
enum WorkoutType: String, CaseIterable, Codable, Identifiable {
case yoga = "Yoga"
case mobility = "Mobility"
case strength = "Strength"
case cardio = "Cardio"
var id: WorkoutType { self }}
enum BodyPart: String, CaseIterable, Codable, Identifiable {
case legs = "Legs"
case core = "Core/Abs"
case back = "Back"
case chest = "Chest/Arms"
case neck = "Neck/Shoulders"
case body = "Whole Body"
var id: BodyPart { self }}
我创建了一个通用函数,使代码可重用:
func surveyStepOptions<T : Identifiable & Hashable & CaseIterable & RawRepresentable >(_ enumType: T.Type, selected: [T])
-> some View
where T.RawValue == String
{
ForEach(Array(enumType.allCases)) {option in
Text(option.rawValue).font(.headline)
.foregroundColor(.purple)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(selected.contains(option) ?
Color.blue : Color.white)
.cornerRadius(10)
.onTapGesture {
if selected.contains(option) {
if let index = selected.firstIndex(of: option) {
selected.remove(at: index)
}
} else {
selected.append(option)
}
}
}
}
我很难改变 onTapGesture 中的状态变量,因为它说“选定的”参数是不可变的,但是当我使它可变时,它说我不能在转义闭包中使用 inout 。怎么解决?
您已经快完成了,只需将参数更改为
Binding
,这样您就可以在 ForEach
函数中更改选择
@ViewBuilder func surveyStepOptions<EnumType>(selected: Binding<[EnumType]>) -> some View where EnumType: CaseIterable & RawRepresentable<String> & Equatable{
ForEach(Array(EnumType.allCases), id:\.rawValue) {option in
Text(option.rawValue).font(.headline)
.foregroundColor(.purple)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(selected.wrappedValue.contains(option) ?
Color.blue : Color.white)
.cornerRadius(10)
.onTapGesture {
if selected.wrappedValue.contains(option) {
if let index = selected.wrappedValue.firstIndex(of: option) {
selected.wrappedValue.remove(at: index)
}
} else {
selected.wrappedValue.append(option)
}
}
}
}
注意,我还删除了显式类型参数,Swift 中不需要它。
这是完整的代码。
import SwiftUI
struct ReusableEnumParentView: View {
@State var selectedWorkoutTypes: [WorkoutType] = []
@State var selectedBodyParts: [BodyPart] = []
var body: some View {
ScrollView{
LazyVStack {
workoutTypeSection
bodyPartSection
}
}
}
@ViewBuilder var workoutTypeSection: some View {
VStack(spacing: 20) {
header("What Is Your Preferred Workout?")
surveyStepOptions(selected: $selectedWorkoutTypes)
}
.padding(30)
}
@ViewBuilder var bodyPartSection: some View {
VStack(spacing: 20) {
header("What Is Your Preferred Workout?")
surveyStepOptions(selected: $selectedBodyParts)
}
.padding(30)
}
@ViewBuilder func header(_ title: String) -> some View {
Text(title)
}
@ViewBuilder func surveyStepOptions<EnumType>(selected: Binding<[EnumType]>) -> some View where EnumType: CaseIterable & RawRepresentable<String> & Equatable{
ForEach(Array(EnumType.allCases), id:\.rawValue) {option in
Text(option.rawValue).font(.headline)
.foregroundColor(.purple)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(selected.wrappedValue.contains(option) ?
Color.blue : Color.white)
.cornerRadius(10)
.onTapGesture {
if selected.wrappedValue.contains(option) {
if let index = selected.wrappedValue.firstIndex(of: option) {
selected.wrappedValue.remove(at: index)
}
} else {
selected.wrappedValue.append(option)
}
}
}
}
}
#Preview {
ReusableEnumParentView()
}
enum WorkoutType: String, CaseIterable, Codable, Identifiable {
case yoga = "Yoga"
case mobility = "Mobility"
case strength = "Strength"
case cardio = "Cardio"
var id: WorkoutType { self }}
enum BodyPart: String, CaseIterable, Codable, Identifiable {
case legs = "Legs"
case core = "Core/Abs"
case back = "Back"
case chest = "Chest/Arms"
case neck = "Neck/Shoulders"
case body = "Whole Body"
var id: BodyPart { self }
}