swiftyplace site icon

SwiftUI Popovers and Popup Menus: The Ultimate Guide

A SwiftUI popover is an essential tool in the iOS developer’s toolkit. It’s a type of view that emerges from an existing one, used to present additional information or a list of actions without taking you away from your current context. With SwiftUI, creating and managing popovers has become a surprisingly simple task, enabling you to enhance the user experience of your apps with ease. In this blog post, I will guide you through the intricacies of SwiftUI popovers and how they compare to similar SwiftUI elements like sheets and menu pickers.

I am going to use a Shopping app as an example that I started developing for other tutorials about SwiftUI presentations like sheets and menus.

Download the project files here: https://github.com/gahntpo/Sho…

What is a popover in iOS?

In iOS, a popover is a UI element that appears on top of your existing content, typically used to present a new view to the user in context. Unlike other view controllers that take up the entire screen, a popover appears in a small, focused area, thereby enabling the user to interact with other parts of the app outside of the popover if necessary. It’s an effective way to display additional information or options without disrupting the user’s current flow within the app.

SwiftUI popover vs sheet

In SwiftUI, a popover is a compact floating view that overlays content, typically used for brief tasks or additional information related to a specific screen element. On the other hand, a sheet covers either the full screen or a large part of it, sliding over the existing content, and is generally used for tasks that require more user attention or input, such as filling out a form. See this guide about SwiftUI Sheet for more details.

SwiftUI popover vs menu picker

SwiftUI menu shows a list of button actions when taped and uses a popover style. Menu is equivalent to using a button and a popover. You can see examples in this blog post about SwiftUI Menu and Context Menu. Picker view has a styling option for menu, which also uses a popover. Similarly, ColorPicker and DatePicker offer selection views inside a popover. Read more about SwiftUI Picker here.

How do I add a pop up in SwiftUI?

I am going to use the feedback view of my shopping app example, where I want to open a popover when the user taps on ´Or Select an Emoji´.

SwiftUI Shopping Example App
SwiftUI App for Shopping: Select a product in the main product list view, will open the product detail view. Tap on ´Leave a Review´ will navigate to the feedback view

In order to create a popover, I first have to define a state property:

struct FeedbackView: View {
    @State private var isEmojiPopoverPresented: Bool = false
    @State private var selectedEmoji: Emoji? = nil
    @State private var feedback: String = ""

    var body: some View {
       ..
    }
}

Then you can use the popover view modifier and define what view is shown inside the popover:

struct FeedbackView: View {
    @State private var isEmojiPopoverPresented: Bool = false
    ...

    var body: some View {
       ...

       Button(action: {
                self.isEmojiPopoverPresented = true
       }) {
            HStack {
               Text(emojiSlecetorText)
               Text(selectedEmoji?.emojiSting ?? "")
                   .font(.largeTitle)
           }
       }
       .popover(isPresented: $isEmojiPopoverPresented) {
          EmojiSelectorView(selection: $selectedEmoji)
       }
      ...
    }
}

Notice that I toggle the state property isEmojiPopoverPresented for the ´Or Select an Emoji´ button. Changing this property will cause the popover to open.

SwiftUI Popover example
Popover in compact mode will be displayed as a sheet, e.g. on the iPhone. On the iPad and Mac, it is a popover. The right iPad has a popover view with a size restriction of 300 by 400 pixels.

The size of the popover is determined by the view. If it is an expandable container view like in my example the ScrollView. The view gets a fixed size restriction in the horizontal direction. The popover is as wide as the minimal size, which is in this case the size of the ´Select an Emoji´ text. You can change the size with the frame modifier like so:

.popover(isPresented: $isEmojiPopoverPresented) {
      EmojiSelectorView(selection: $selectedEmoji)
          .frame(minWidth: 300, maxHeight: 400)
}

Popover and sheet appearance

You can now also use a popover instead of a sheet on the iPhone. For iOS16.4, and macOS 13.3 is now presentationCompactAdaptation available:

.popover(isPresented: $isEmojiPopoverPresented) {
    EmojiSelectorView(selection: $selectedEmoji)
        .frame(minWidth: 300, maxHeight: 400)
        .presentationCompactAdaptation(.popover)
}

You can choose these values for the PresentationAdaptation:

  • automatic: default adaptation
  • none: no adaptation for the size class
  • popover: prefer popover
  • sheet: prefers a sheet for adapting size class (like it does per default on the iPhone)
  • fullScreenCover: prefer full screen
swiftui popover examples for presentationCompactAdaptation

How to set the size and location of a popover in SwiftUI?

The size of the popover is determined by the size of the view used. It uses the smaller size value. You can change the size by adding a padding (like I do in the following) or by applying a frame modifier. The position of the view depends on where you attach the popover view modifier to. In the emoji picker example from above, I added the popover to the ´Or Select an Emoji´ button. You can attach the popover to any view e.g. a toolbar item button.

struct ContentView: View {

    @State private var show = false

    var body: some View {
        Button("Open Popover") {
            self.show = true
        }
        .buttonStyle(.borderedProminent)
        .popover(isPresented: self.$show,
                 attachmentAnchor: .point(.center),
                 arrowEdge: .top,
                 content: {
            Text("Hello, World!")
                .padding()
                .presentationCompactAdaptation(.none)
        })
    }
}

Changing the Position of the Popover Arrow

You can change the position of the popover arrow with the attachmentAnchor parameter. In the following example, I set the popover anchor to the center of the ´Open Popover´ button:

struct PopoverArrowExampleView: View {

    @State private var show = false

    var body: some View {
        Button("Open Popover") {
            self.show = true
        }
        .buttonStyle(.borderedProminent)
        .popover(isPresented: self.$show,
                 attachmentAnchor: .point(.center),
                 arrowEdge: .top,
                 content: {
            Text("Hello, World!")
                .padding()
                .presentationCompactAdaptation(.none)
        })
    }
}

You can fully customize the attachmentAnchor by using .rect. In the following funky example, I used an explicit value to set the anchor point inside the colored square:

struct PopoverArrowColorExampleView: View {
    @State private var show = false
    var body: some View {
        ZStack {
            Color.orange
                .frame(width: 100, height: 100)
            Color.pink
                .frame(width: 80, height: 80)
            Color.purple
                .frame(width: 60, height: 60)
            Color.indigo
                .frame(width: 40, height: 40)
            Color.blue
                .frame(width: 20, height: 20)
        }
        .onTapGesture {
            self.show = true
        }
        .buttonStyle(.borderedProminent)
        .popover(isPresented: self.$show,
                 attachmentAnchor: .rect(.rect(CGRect(x: 0, y: 20,
                                        width: 160, height: 100))),
                 arrowEdge: .top,
                 content: {
            Text("Hello, World!")
                .padding()
                .presentationCompactAdaptation(.none)
        })
    }
}
swiftui popover with attachment anchor

Important: On iOS the arrowEdge parameter is ignored and system picks the appropriate value. The arrowEdge value is only used on macOS.

Dismiss a SwiftUI Popover

Like other presentation views (e.g. sheet, action sheet, confirmationDialog), you can use the environment value dismiss. In the above example, I want to dismiss the popover, when the user taps on an emoji icon. You can see below, how I added a on tap gesture to each emoji view where I set the selected emoji. I also call dismiss, which closes the popover.

struct EmojiSelectorView: View {

    @Environment(\.dismiss) var dismiss
    @Binding var selection: Emoji?

    let columns = [GridItem(.adaptive(minimum: 44), spacing: 10)]
    let emojis: [Emoji]  = Emoji.examples()

    var body: some View {
        VStack(alignment: .leading) {
            Text("Select an Emoji")
                .font(.title3)
                .padding(.horizontal)

            Divider()

            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(emojis) { emoji in
                        ZStack {
                            emoji == selection ? Color.blue : Color.clear
                            Text(emoji.emojiSting)
                                .font(.title)
                                .padding(5)
                                .onTapGesture {
                                    selection = emoji
                                    dismiss()
                                }
                        }
                    }
                }.padding()
            }
        }
        .padding(.vertical)
    }
}
swiftui dismiss popover

Conclusion

To summarize, a popover in SwiftUI is a user interface element that overlays additional content onto the existing view, providing contextual information or options without disturbing the user’s current workflow. This is distinct from a sheet, which takes up a larger portion of the screen and is typically used for more complex tasks requiring user input. Both popovers and sheets are essential tools in SwiftUI, allowing for more dynamic, intuitive, and user-friendly app designs. Understanding when and how to use these features effectively can greatly enhance the overall user experience of your iOS applications.

Further Reading

Share:

Subscribe To My Newsletter

Table of Contents

One Response

  1. Thanks for another informative web site. The place else
    could I get that kind of information written in such an ideal way?
    I’ve a mission that I’m simply now operating on, and I’ve been on the look out for
    such information.

Leave a Reply

Your email address will not be published. Required fields are marked *

More Posts