0

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.

2 Answers 2

1

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).

Sign up to request clarification or add additional context in comments.

Comments

1

You need to change the popover's content to a View struct instead of a computed var and have a binding in it. Also, it is a good idea to combine related vars into their own struct. e.g.

struct PopoverConfig {
    var isPresented = false
    var choice = 0
}

struct DropdownTestView: View {
    @State var popover = PopoverConfig()

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

struct PopoverView: View {
    @Binding var choice: Int

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

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

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

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.