Reputation: 201
I have an array of views that I need to get values from in order to save them to my context. When trying to loop over these when a button is clicked I get a "Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols" exception. Im very new to swift and I am wondering if there is an easier way to do this. Thank you in advanced!
This is the complete view:
struct DetailedView: View {
@Environment(\.managedObjectContext) var moc
var targetMuscle : String = "Chest"
let today = Date()
@State public var exerciseCards : [ExerciseCard] = []
@State public var exercise : String = "Bench Press"
@State public var exercises : Int = 0
@State public var showPassedWorkouts : Bool = false
static let taskDateFormat : DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}()
var body: some View {
ZStack{
VStack{
HStack{
VStack{
Text(targetMuscle).font(.system(size:40)).fontWeight(.medium)
Text("\(today, formatter: Self.taskDateFormat)")
.font(.system(size:20))
}.frame(width: 250, height: 30, alignment: .topLeading)
.navigationBarTitle("")
.navigationBarHidden(true)
.padding(.bottom, -7)
Button(action: {
self.showPassedWorkouts.toggle()
}) {
Text("Passed Workouts")
.multilineTextAlignment(.center)
}.offset(x: -75, y: 25)
.sheet(isPresented: $showPassedWorkouts){
PassedWorkoutList()
}
Button(action: {
let workout = Workout(context: self.moc)
workout.muscle = self.targetMuscle
workout.date = formattedDateString(day: self.today)
ForEach(0..<exerciseCards.count, id: \.self){number in
let exercise = Exercise(context: self.moc)
ForEach(0..<self.exerciseCards[number].tableRows.count, id: \.self){innerNum in
let exerciseSet = ExerciseSet(context: self.moc)
exerciseSet.reps = self.exerciseCards[number].tableRows[innerNum].reps
exerciseSet.weight = self.exerciseCards[number].tableRows[innerNum].weight
exerciseSet.set = self.exerciseCards[number].tableRows[innerNum].set
exercise.addToExerciseSet(exerciseSet)
}
workout.addToExercise(exercise)
}
try? self.moc.save()
}) {
Text("Finish")
}.offset(x: -20, y: 20)
}.padding(.bottom, 35)
.padding(.leading)
ScrollView{
ForEach(0..<exerciseCards.count, id: \.self){ number in
self.exerciseCards[number]
}
Button(action: {
self.exerciseCards.append(ExerciseCard())
}) {
Text("Add Exercise")
.frame(minWidth: 325)
.padding()
.foregroundColor(.white)
.background(Color.blue.opacity(0.7))
.cornerRadius(20)
}.padding(.top)
.frame(width: 400)
}
}
}.background(Color.white)
}
}
func formattedDateString(format: String? = "MMM d, h:mm", day: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.string(from: day)
}
The above view also references two views the first one being the ExerciseCard view
struct ExerciseCard: View {
@State public var exercise : String = "Bench Press"
@State public var tableRows : [ExerciseTableRow] = []
var body: some View {
VStack{
TextField("Enter Exercise", text: $exercise).textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 300)
.multilineTextAlignment(.center)
HStack{
Group{
Text("Set")
Text("Weight")
Text("Reps")
}.padding(.horizontal, 30)
.offset(x: -20, y: 0)
}
VStack{
ForEach(0..<tableRows.count, id: \.self){ number in
self.tableRows[number]
}
}.padding(.bottom, 5)
HStack{
Button(action: {
if self.tableRows.count > 1{
self.tableRows.remove(at: self.tableRows.count-1)
}
}) {
Text("Remove Set")
.frame(minWidth: 150)
.padding(.vertical, 5)
.foregroundColor(.white)
.background(Color.red)
.cornerRadius(20)
}
Button(action: {
self.tableRows.append(ExerciseTableRow(set: 2, readOnly: false, setWeight: 2, setReps: 2))
}) {
Text("Add Set")
.frame(minWidth: 150)
.padding(.vertical, 5)
.foregroundColor(.white)
.background(Color.green)
.cornerRadius(20)
}
}
}
.padding()
.padding(.vertical)
.background(Color.offWhite)
.cornerRadius(20)
.shadow(color: Color.black.opacity(0.2), radius: 10, x:10, y:10)
.shadow(color: Color.white.opacity(0.7), radius: 10, x:-5, y:-5)
}
}
The second one being the ExerciseTableRow view
struct ExerciseTableRow: View {
@State public var weight : String = "0"
@State public var reps : String = "0"
var set : Int16
var readOnly : Bool
var setWeight : Int16
var setReps : Int16
var body: some View {
HStack{
Text(String(set))
.padding(.trailing, 40)
.padding(.leading, 10)
if readOnly == false{
Group{
TextField("0", text: $weight)
TextField("0", text: $reps)
}.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 50)
.multilineTextAlignment(.center)
.keyboardType(.numberPad)
.padding(.horizontal, 30)
}
else{
Group{
Text(String(setWeight))
Text(String(setReps))
}
.frame(width: 50)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 1)
)
.padding(.bottom, 5)
.padding(.horizontal, 30)
}
}
}
}
Upvotes: 0
Views: 7792
Reputation: 257693
The ForEach
is a SwiftUI dynamic View container, to build views in cycle. For action you have to use regular swift for .. in
expression, like
Button(action: {
let workout = Workout(context: self.moc)
workout.muscle = self.targetMuscle
workout.date = formattedDateString(day: self.today)
for number in 0..<exerciseCards.count {
let exercise = Exercise(context: self.moc)
for innerNum in 0..<self.exerciseCards[number].tableRows.count {
let exerciseSet = ExerciseSet(context: self.moc)
exerciseSet.reps = self.exerciseCards[number].tableRows[innerNum].reps
exerciseSet.weight = self.exerciseCards[number].tableRows[innerNum].weight
exerciseSet.set = self.exerciseCards[number].tableRows[innerNum].set
exercise.addToExerciseSet(exerciseSet)
}
workout.addToExercise(exercise)
}
try? self.moc.save()
}) {
Text("Finish")
}.offset(x: -20, y: 20)
Upvotes: 8