5

I'm trying to pass an item to a sheet, if I change the values by selecting another item. The sheet keeps displaying the first item, even when I never called the sheet with the first item.

Is this a bug or am I doing something wrong?

WorkoutList.swift

import SwiftUI

struct WorkoutList: View {
    private var workouts = workoutData
    @State var showWorkoutDetails = false

    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                ForEach(workouts) { workoutCard in
                    GeometryReader { geometry in
                        Button(action: {self.showWorkoutDetails.toggle()}) {
                            WorkoutCardView(workout: workoutCard)
                                .rotation3DEffect(Angle(degrees: Double((geometry.frame(in: .global).minX - 30) / -30)), axis: (x: 0, y: 10, z: 0))
                                .sheet(isPresented: self.$showWorkoutDetails) {
                                    WorkoutDetails(workout: workoutCard)
                                }
                        }
                    }
                    .frame(width:246, height: 360)

                }
            }
        .padding(30)
        }
    }
}

private let workoutData = [
    Workout(name: "Workout #1", imageName: "workoutImage", color: Color.orange),
    Workout(name: "Workout #2", imageName: "workoutImage", color: Color.green),
    Workout(name: "Workout #3", imageName: "workoutImage", color: Color.red),
    Workout(name: "Workout #4", imageName: "workoutImage", color: Color.blue)
]

WorkoutDetails.swift

import SwiftUI

struct WorkoutDetails: View {
    var workout = Workout(name: "", imageName: "", color: Color.black)

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .fill(self.workout.color)

                VStack(alignment: .leading) {
                    VStack(alignment: .leading) {
                        Text(self.workout.name)
                            .foregroundColor(.white)
                            .font(.title)
                            .fontWeight(.heavy)
                            .lineLimit(nil)
                            .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
                            .frame(height: 70)
                    }
                    .frame(width: 180)

                    Image(self.workout.imageName)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: geometry.size.width-60, height: 200)
                        .padding()

                    Spacer()
                }
                .padding(.top, 40)
                .padding(.leading, 10)
            }
        }
        .edgesIgnoringSafeArea(.bottom)
    }
}

Here I want to display a workout. But whatever I change it, I always get "workout #1" and I don't know what I'm doing wrong.

1

5 Answers 5

5

Create a binding with an optional workout. Nil will be the case that the sheet is not presented, assigning a workout to this variable displays the corresponding sheet.

struct WorkoutList: View {
  private var workouts = workoutData
  @State var selectedWorkout: Workout?

  var body: some View {
    ScrollView(.horizontal, showsIndicators: false) {
      HStack(spacing: 20) {
        ForEach(workouts) { workoutCard in
          GeometryReader { geometry in
            Button(action: { self.selectedWorkout = workoutCard }) {
              WorkoutCardView(workout: workoutCard).rotation3DEffect(Angle(degrees: Double((geometry.frame(in: .global).minX - 30) / -30)), axis: (x: 0, y: 10, z: 0)).sheet(item: self.$selectedWorkout) { workout in
                WorkoutDetails(workout: workout)
              }
            }
          }.frame(width:246, height: 360) 
        }
      }.padding(30)
    }
  }
}
Sign up to request clarification or add additional context in comments.

Comments

2
  1. Pass a Binding instead of the value of the @State variable
  2. Use the value of the Binding in the body of the view included in the sheet
struct WorkoutList: View {
    @State var isDetailPresented = false
    @State var selectedWorkout: Workout?

    var body: some View {
        Button(action: {
            self.selectedWorkout = workouts[yourIndex]
            self.showWorkoutDetails.toggle()
        }) {
            Text("Test")
        }
        .sheet(isPresented: self.$showWorkoutDetails) {
            WorkoutDetails(workout: selectedWorkout)
        }
    }
}
struct WorkoutDetails: View {
    @Binding var workout: Workout?

    var body: some View {
        ZStack {
            Rectangle()
                .fill(self.workout.color)
            // your code
        }
    }
}

3 Comments

perfect. just what i needed.
wouldn't it ask for $ sign in WorkoutDetails(workout: selectedWorkout) ?
also, should isDetailPresented be renamed to showWorkoutDetails ?
2

I think this is much better explained here SwiftUI sheet doesn't access the latest value of state variables on first appearance

struct DemoView: View {
  @State var showDetails: Bool = false
  var body: some View {
    VStack {
        Button(action: {
          showDetails = true
        }) {
            Text("Show sheet")
        }
    }.sheet(isPresented: $showDetails){
        SheetDetailView(flag: $showDetails)
    }
  }
}

struct SheetDetailView: View {
  @Binding var flag: Bool
     var body: some View {
       VStack {
          Text("showDetails: \(flag ? "yes" : "no")")
           }
     }
}

Just make the parameter in the sheet view binding, and send in a binding parameter, and you can still conserve the boolean toggle.

Comments

0

I had similar problem. You want to assign workoutCard to @State and inside .sheet have some logic that decides what view to show

Here is more on your problem: Multiple sheet(isPresented:) doesn't work in SwiftUI

Comments

0

Basically we need to store selectedWorkout when the button tapped.

@State var isDetailPresented = false
@State var selectedWorkout: Workout?

...

Button(action: {
    self.selectedWorkout = workoutCard
    self.isDetailPresented.toggle()
}, label: {
    //Your view here
}).buttonStyle(PlainButtonStyle())
  .sheet(isPresented: $isDetailPresented) {
    WorkoutDetails(workout: self.selectedWorkout!)
}
...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.