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

Popover doesn't re-render its view when identity changes #72

Open
raysarebest opened this issue Feb 17, 2023 · 2 comments
Open

Popover doesn't re-render its view when identity changes #72

raysarebest opened this issue Feb 17, 2023 · 2 comments

Comments

@raysarebest
Copy link

raysarebest commented Feb 17, 2023

Let's say I have a custom-made text field that updates its content binding on every user keystroke. Let's also say I have a popover that displays some options based on what the user has typed into that text field:

let states = ["Alabama", "Alaska", ... ]

CustomTextField("US State", $selectedState)
    .popover(present: .constant(true)) {
        VStack {
            ForEach(states.map({ state in state.hasPrefix(selectedState) })) { state in
                Text(state)
            }
        }
    }

I would expect that the list of states displayed in the popover would update with every keystroke, but that doesn't seem to be the case. Strategically inserting debugger statements shows that the $selectedState is definitely getting updated for every keystroke, but the view that the popover renders never updates to include the narrowed list of possible states. How can I get it to update whenever its displayed view's identity changes so I can make something like this work?

@raysarebest
Copy link
Author

I came to a partial solution for this an PRed it with #73, but that strictly supports cases where the view that's presented in the popover has its own properties defined with @State, @StateObject, @Binding, @ObservedObject, @Environment, or @EnvironmentObject properties

It seems that supporting the specific case originally laid out in this issue is pretty non-trivial, because the popover is rendered in its own UIHostingController, which means it's effectively an entirely different context from the code that presents the popover. This means that even though the code that presents the popover knows that the popover's view needs to be updated, the hosting controller that actually renders the popover has no clue

That has proved a rather difficult challenge to overcome. I've tried to no avail:

  • Hiding and re-presenting the popover on every render
  • Forcing the PopoverGestureContainer to re-lay out its subviews on every render
  • Creating a new PopoverGestureContainer and re-setting the popover's UIHostingController's rootView on every render

It seems that SwiftUI needs to expose an API for updating a UIHostingController whenever a related SwiftUI host re-renders its view, which doesn't seem to currently be possible. I wonder if it might be possible if we add the popover's hosting controller as a child of the source hosting controller, but that would require SwiftUI exposing a proper way to get a reference to the source hosting controller. Would love to see someone prove me wrong on either of those, though

@aheze
Copy link
Owner

aheze commented Mar 20, 2023

I wonder if it might be possible if we add the popover's hosting controller as a child of the source hosting controller, but that would require SwiftUI exposing a proper way to get a reference to the source hosting controller.

Presenting the popover has been one of the hardest challenges since the start of this library. At first I was adding a new UIWindow, but that stole the status bar color (#5). And now it's been so long since I edited the code that I forgot what exactly I'm doing... so I'm working on a complete refactor right now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants