iosanimationswiftuipopover

Modifying content in a popover results in strange reload and scrolling animation instead of a smooth one


I am trying to make a popover whose content dynamically expands based on choices above. I would settle for no animation or ideally the popover gradually expanding for its new content but instead it appears like I put a scroll view in the popover and you can see the menu reload.

Interestingly if you do the same thing with a Button just modifying a state property this animation does not happen.

I am guessing in some sense I am confusing the animation system with non stable IDs but I have tried putting .id() arguments on everything and that is not fixing it.

enter image description here

struct DropdownTestView: View {
    @State var presentPopover: Bool = false
    @State var choice: Int = 0

    var body: some View {
        VStack {
            Button {
                presentPopover = true
            } label: {
                Text("Press To Choose")
            }
            .popover(isPresented: $presentPopover) {
                popoverView
                    .padding()
            }
            Spacer()
        }
        .padding()
    }

    var popoverView: some View {
        VStack(alignment: .leading) {
            Divider()

            Menu {
                ForEach(0..<6) { num in
                    Button {
                        choice = num
                    } label: {
                        Text("\(num)")
                    }
                }
            } label: {
                Text("Make A Choice")
            }

            if choice != 0 {
                Divider()
                HStack {
                    Text("WRONG CHOICE")
                }
            }
        }
        .frame(width: 220)
    }
}

I have tried something like this:

HStack {
    Text("WRONG CHOICE")
}
.opacity(choice != 0 ? 1.0 : 0)
.frame(choice != 0 ? 30, 0)

But this does not work either.


Solution

  • It looks like the change in content size takes effect immediately, but the change in popover size is animated. This would be fine if the anchor for the reveal was .top, but it looks like it may be something like .center instead.

    As a workaround, try wrapping the popover content in a ScrollView. The ScrollView provides "buffering" for the change in height:

    .popover(isPresented: $presentPopover) {
        ScrollView {
            popoverView
                .padding()
        }
    }
    

    Animation


    Ps. Another workaround would be to use a custom popover instead. You can then style the popover any way you like and also control the way that animations work. The answer to iOS SwiftUI Need to Display Popover Without "Arrow" provides an example implementation (it was my answer).