Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ForEach, the view on the interface shows the same two sets #6

Closed
wisepmlin opened this issue Jan 3, 2022 · 6 comments
Closed
Labels
great question Something that can help a lot of people

Comments

@wisepmlin
Copy link

wisepmlin commented Jan 3, 2022

Using ForEach, the view on the interface shows the same two sets

ForEach(self.dates) { date in
    VStack(alignment: .leading, spacing:0)
    {
        HStack(spacing: CGFloat.bl_4)
        {
            HStack(spacing: CGFloat.bl_4)
            {
                Image(systemName: IMAGE_CALENDER)
                DayView(date: date)
            }
            .modifier(SK_14(textColor: sk_current.isToday(date: date.date) ? Color.blue_c : Color.t_t_c, weight: sk_current.isToday(date: date.date) ? .bold : .regular))
            .frame(height: CGFloat.bl_4.custom(num: 8))
            .onTapGesture(perform: {
                present = true
            })
            .onChange(of: sk_current.selectedDate, perform: {value in
                present = false
                self.pageIndex = sk_current.toThisDayIndex()
            })
            .popover(
                present: $present,
                attributes: {
                    $0.presentation.animation = .spring(
                        response: 0.6,
                        dampingFraction: 0.6,
                        blendDuration: 1
                    )
                    $0.presentation.transition = .opacity
                    $0.dismissal.animation = .easeIn(duration: 0.5)
                    $0.dismissal.transition = .move(edge: .bottom).combined(with: .opacity)
                }
            ) {
                VStack(spacing: CGFloat.bl_4.double) {
                    Text("📅 日历")
                        .modifier(SK_16(textColor: Color.t_p_c, weight: .bold))
                    DatePicker("",
                               selection: $sk_current.selectedDate, displayedComponents: .date)
                        .datePickerStyle(GraphicalDatePickerStyle())
                        .accentColor(.blue_c)
                }
                .frame(maxWidth: 320)
                .padding(.top, CGFloat.bl_4.custom(num: 5))
                .padding(.horizontal, CGFloat.bl_4.custom(num: 5))
                .background(.bar)
                .cornerRadius(12)
                .shadow(radius: 20)
                .scaleEffect(expanding ? 0.95 : 1)
             
            }
@aheze
Copy link
Owner

aheze commented Jan 3, 2022

Looks like a state re-rendering problem: see here. You might need to move everything in your popover into a separate View.

.popover(present: $present) {
    PopoverView(sk_current: $sk_current)
}

/// ...

struct PopoverView: View {
    @Binding var sk_current: WhateverSK_CurrentIs
    
    var body: some View {
        VStack(spacing: CGFloat.bl_4.double) {
                            Text("📅 日历")
                                .modifier(SK_16(textColor: Color.t_p_c, weight: .bold))
                            DatePicker("",
                                       selection: $sk_current.selectedDate, displayedComponents: .date)
                                .datePickerStyle(GraphicalDatePickerStyle())
                                .accentColor(.blue_c)
                        }
                        .frame(maxWidth: 320)
                        .padding(.top, CGFloat.bl_4.custom(num: 5))
                        .padding(.horizontal, CGFloat.bl_4.custom(num: 5))
                        .background(.bar)
                        .cornerRadius(12)
                        .shadow(radius: 20)
                        .scaleEffect(expanding ? 0.95 : 1)
    }
}

@aheze
Copy link
Owner

aheze commented Jan 3, 2022

@wisepmlin if that doesn't work, can you show a image of what you're getting currently? Thanks.

@wisepmlin
Copy link
Author

IMG_E87299B10598-1

@aheze
Copy link
Owner

aheze commented Jan 3, 2022

Ah, I see. When you set present to true, all the popovers inside your ForEach will show, since they're all linked to the same property.

@State var present = false /// you have 1 single property for all the popovers!

Here's a simplified example of what you're doing currently.

struct ContentView: View {
    @State var present = false

    var body: some View {
        VStack(spacing: 80) {
            ForEach(0..<3) { index in
                Button {
                    present = true /// when you set this to `true`, all the popovers will present
                } label: {
                    Text("Button #\(index)")
                }
                .popover(present: $present) { /// all of the popovers use the same property
                    Text("Popover #\(index)")
                        .padding()
                        .background(Color.blue)
                        .cornerRadius(16)
                }
            }
        }
    }
}
All popovers present at the same time, no matter which one was tapped.

Instead, since you have multiple popovers, you'll need to use the .popover(selection:tag:attributes:view:) modifier.

struct ContentView: View {
    @State var selection: String? /// keeps track of which popover is being presented.

    var body: some View {
        VStack(spacing: 80) {
            ForEach(0..<3) { index in
                Button {
                    selection = "\(index)" /// set `selection` to the current iteration's tag
                } label: {
                    Text("Button #\(index)")
                }
                
                /// will be presented when `selection` == `tag`
                .popover(selection: $selection, tag: "\(index)") {
                    Text("Popover #\(index)")
                        .padding()
                        .background(Color.blue)
                        .cornerRadius(16)
                }
            }
        }
    }
}
Each button presents its own popover when tapped.

More info if you're interested

  • Popovers behaves similar to SwiftUI's sheet inside a ForEach.
  • Unlike SwiftUI's sheet, multiple popovers can linked to the same @State. They will just all present at the same time, which may or may not be what you want.
  • Meanwhile, if you try linking multiple SwiftUI sheets to the same @State, it will glitch out.
  • .popover(selection:tag:attributes:view:) comes with some other benefits, including support for animating between popovers!

@aheze aheze added the great question Something that can help a lot of people label Jan 3, 2022
@wisepmlin
Copy link
Author

First of all, your toolkit is great! Thanks for the open source
I've written them to the child view, and the new struct has received the independent properties, so there's no duplication

@aheze
Copy link
Owner

aheze commented Jan 3, 2022

Thanks! Yeah that would work too, glad you found a solution.

@aheze aheze closed this as completed Jan 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
great question Something that can help a lot of people
Projects
None yet
Development

No branches or pull requests

2 participants