UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to pass data to a popup view?

Forums > SwiftUI

I followed the instructions here: https://www.vadimbulavin.com/swiftui-popup-sheet-popover/ to build a nice popup view. I've got it all styled the way I want, and it works great, except I can't pass it data.

Is there a way to pass data with this setup? I have a struct I'd like to pass as well as the isPresented value so that I can put a button on the popup to dismiss rather than relying on a button on the main view. Is this possible? Could I redefine the popup function to not be a T view so that I can pass the data? If so, how?

Thanks for any help you can give.

2      

You can do something like this:

struct PopupItem<T: View, U: Identifiable>: ViewModifier {
    let popup: (U) -> T
    let item: Binding<U?>

    init(item: Binding<U?>, @ViewBuilder content: @escaping (U) -> T) {
        self.item = item
        popup = content
    }

    func body(content: Content) -> some View {
        content
            .overlay(popupContent())
    }

    @ViewBuilder private func popupContent() -> some View {
        if let item = item.wrappedValue {
            GeometryReader { geometry in
                popup(item)
                    .frame(width: geometry.size.width, height: geometry.size.height)
            }
        }
    }
}

extension View {
    func popup<T: View, U: Identifiable>(item: Binding<U?>, @ViewBuilder content: @escaping (U) -> T) -> some View {
        self.modifier(PopupItem(item: item, content: content))
    }
}

struct Thing: Identifiable {
    let id = UUID()
    let name: String
}

struct PopupTestView: View {
    @State private var thing: Thing?

    var body: some View {
        Button("Show Popup") {
            thing = Thing(name: "George")
            //setting thing to non-nil value triggers display of popup
        }
        .popup(item: $thing) { item in
            VStack(spacing: 20) {
                Text(item.name)
                Button {
                    thing = nil
                    //setting thing to nil dismisses the popup
                } label: {
                    Text("Dismiss Popup")
                }
            }
            .frame(width: 200)
            .padding()
            .background(Color.gray)
            .cornerRadius(8)
        }
    }
}

2      

That's much closer, but I left out an important bit in my original problem, sorry! I need to pass it a binding struct because the new view needs to be able to make changes to that data as well, so creating a non-nil/nil instance copy of the data won't work. Other thoughts?

To provide a clearer picture (maybe?) of the problem, I'm creating a D&D-esque character sheet. The main view has it's lower half as three tabbing pages of separate subviews all containing different stats. One of those subviews needs to call the popup for a dice rolling mechanic using the character's stats (which has been passed in as a binding struct) as bonuses and potentially changing a character stat in the process. Additionally complicating things is that the subview of character stats has subviews, but I did eventually find the right spot to put the .popup call.

So, the flowchart is as such:

1) Character selection initial view: all data for all characters is read from a json file. 2) User selects a character, and all the character info is sent as a struct to the main view (character sheet) where the stats are contained in a subview. 3) User clicks a button in the subview to roll dice which should trigger the popup but needs to be able to read and change the characters stats. 4) User dismisses the popup to go back to the now changed character stats.

Does that make sense? Thank you so much for trying to help! I think I'm almost there if you've got any other ideas. Thank you, thank you!

2      

Slight modification of the original example from Vadim Bulavin's site...

struct Popup<D, V: View>: ViewModifier {
    let popup: (Binding<D>) -> V
    let isPresented: Binding<Bool>
    let data: Binding<D>

    init(isPresented: Binding<Bool>, with data: Binding<D>, @ViewBuilder content: @escaping (Binding<D>) -> V) {
        self.isPresented = isPresented
        popup = content
        self.data = data
    }

    func body(content: Content) -> some View {
        content
            .overlay(popupContent())
    }

    @ViewBuilder private func popupContent() -> some View {
        GeometryReader { geometry in
            if isPresented.wrappedValue {
                popup(data)
                    .frame(width: geometry.size.width, height: geometry.size.height)
            }
        }
    }
}

extension View {
    func popup<D, V: View>(isPresented: Binding<Bool>, with data: Binding<D>, @ViewBuilder content: @escaping (Binding<D>) -> V) -> some View {
        self.modifier(Popup(isPresented: isPresented, with: data, content: content))
    }
}

struct Thing: Identifiable {
    let id = UUID()
    var name: String
}

struct PopupTestView: View {
    @State private var thing: Thing = Thing(name: "George")
    @State private var showPopup: Bool = false

    var body: some View {
        Button("Show Popup") {
            showPopup = true
        }
        .popup(isPresented: $showPopup, with: $thing) { item in
            VStack(spacing: 20) {
                TextField("Name", text: item.name)
                Button {
                    showPopup = false
                } label: {
                    Text("Dismiss Popup")
                }
            }
            .frame(width: 200)
            .padding()
            .background(Color.gray)
            .cornerRadius(8)
        }
    }
}

Edit: Oops, forgot about making the data item editable. Fixed.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.