SkillAgentSearch skills...

LNPopupUI

A SwiftUI library for presenting views as popups, much like the Apple Music and Podcasts apps.

Install / Use

/learn @LeoNatan/LNPopupUI
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

LNPopupUI

LNPopupUI is a SwiftUI library for presenting views as popups, much like the Apple Music and Podcasts apps.

This is a SwiftUI wrapper of the LNPopupController framework, adapted to work with SwiftUI.

GitHub release GitHub stars GitHub license <span class="badge-paypal"><a href="https://paypal.me/LeoNatan25" title="Donate to this project using PayPal"><img src="https://img.shields.io/badge/paypal-donate-yellow.svg?style=flat" alt="PayPal Donation Button" /></a></span>

GitHub issues GitHub contributors Swift Package Manager compatible

<p align="center"><img style="border: 1px solid #555555;" src="./Supplements/intro.gif"/></p>

Once a popup bar is presented with a content view, the user can swipe or tap the popup bar at any point to present the content view. After finishing, the user dismisses the popup by either swiping the content view or tapping the popup close button.

The library extends SwiftUI’s View with new functionality for presenting and customizing popups with content views, as well as setting information such as the popup bar’s title, image and bar button items. When a popup bar is presented, the popup bar automatically adapts to the view it was presented on for best appearance.

Generally, it is recommended to present the popup bar on the outermost view, such as TabView or NavigationStack. For example, if you have a view contained in a navigation stack, which is in turn contained in a tab view, it is recommended to present the popup bar on the tab view.

Check the demo project for a quick recreation of Apple’s music app.

[!NOTE] To run the example project, don't forget to update submodules by running: git submodule update --init --recursive

Features

  • Supports iOS 26 glass design, while maintaining a system-apropriate look and feel on previous iOS versions
  • Available for iOS 14 and above, as an SPM package for SwiftUI
  • For UIKit, check out the LNPopupController framework

Adding to Your Project and Using the Framework

Swift Package Manager

LNPopupUI supports SPM versions 6.0 (Xcode 16) and above. In Xcode, click FileAdd Package Dependencies…, enter https://github.com/LeoNatan/LNPopupUI. Select the version you’d like to use.

You can also manually add the package to your Package.swift file:

.package(url: "https://github.com/LeoNatan/LNPopupUI.git", from: "2.5.0")

And the dependency in your target:

.target(name: "MyExampleApp", dependencies: ["LNPopupUI"]),

Import the module in your project:

import LNPopupUI

Managing a Popup Presentation

A popup presentation consists of the following concepts:

  • Popup container view —the View that hosts the popup presentation. Normally this is the outer-most tab or navigation view, but can be any view.
  • Popup content controller—a View that represents the content, when the popup is open.
  • Popup bar—a bar, docked to the bottom of the container view, either above the container’s bottom bar (tab bar or toolbar) or directly at the bottom of the screen, presenting at-a-glance information to the user and allows interaction by the user. Can be a default system popup bar style or a completely custom implementation.
  • Popup items—the source of data that is displayed on the popup bar at any given time.
  • Custom popup bar view—optional, when presenting a custom popup bar
<picture> <source media="(prefers-color-scheme: dark)" srcset="./Supplements/overview-dark.png"> <source media="(prefers-color-scheme: light)" srcset="./Supplements/overview.png"> <img src="./Supplements/overview.png"> </picture>

To add a popup presentation to your scene, you use the popup(isBarPresented:isPopupOpen:content:) modifier. The user is then able to interact with the popup bar and popup content.

To present and dismiss the popup bar programmatically, toggle the isPopupBarPresented bound boolean var. To open or close the popup programmatically, toggle the isPopupOpen bound boolean var.

For more information, see the documentation in LNPopupUI.swift.

//Container view
TabView {
  //Container content  
  AlbumViews()
}
.popup(isBarPresented: $isPopupBarPresented, isPopupOpen: $isPopupOpen) {
  //Popup content view, visible when the popup opens
  PlayerView(song: currentSong)
  	.popupItems(selection: $currentSong) {
  	  for song in playlist {
	    // Create a popup item for each song in the playlist, with the song's art, name, album and playback controls.
	    PopupItem(id: song, title: song.name, subtitle: song.albumName, image: song.art, progress: playbackState.progress) {
	      playbackButtons(for: song, with: playbackState)
	    }
  	  }
    }
}
<p align="center"><img src="./Supplements/floating_compact_no_scroll.gif" width="414"/></p>

Popup Items

Popup items provide the information that is displayed in the popup bar. In LNPopupUI, you can provide popup item information with three different family if View modifiers. You place a call to one of these modifier families inside your popup content hierarchy.

If you do not provide a popup item, the popup bar will remain empty.

Popup items have no effect when presenging a custom popup bar.

[!WARNING] Never mix between the different popup item modifier families in the same popup content hierarchy. Either use a single popup item providing modifier, such as popupItem(popupItem:), a multiple popup item providing modifier, such as popupItems(selection:items:) or modifiers to update the default popup item, such as popupTitle(_:subtitle:).

Single Popup Item

This family of modifies allows providing an instance of the PopupItem model. PopupItem encapsulates all information necessary for your app to display information on a popup bar. Popup items allow setting string, AttributedString and custom view titles and subtitles.

Providing popup items with these modifiers does not enable popup item paging.

TabView {
  //Container content
}.popup(isBarPresented: $isPopupBarPresented, isPopupOpen: $isPopupOpen) {
  popupContent()
    .popupItem {
      // Create a popup item with an image, a custom view title and a button.
      PopupItem(id: "intro", image: Image("MyImage")) {
        Text("Welcome to ") + Text("LNPopupUI").fontWeight(.heavy) + Text("!")
      } buttons: {
        ToolbarItemGroup(placement: .popupBar) {
          Link(destination: url) {
            Label("LNPopupUI", systemImage: "suit.heart.fill")
          }
        }
      }
    }
}

Multiple Popup Items With Paging Support

This family of modifiers allows provding one or more popup items, representing a collection of data. A single popup item is displayed on a popup bar at a time, and the user can page between popup items by swiping left and right on the popup bar. If a single item is provided, paging is disabled.

<p align="center"><img style="border: 1px solid #555555;" src="./Supplements/floating_paging.gif" width="414"/></p>

When a user pages to a different popup item, the identifier of that popup item is reflected in the selection binding.

TabView {
  //Container content
}
.popup(isBarPresented: $isPopupPresented, isPopupOpen: $isPopupOpen) {
  ContentView()
    .popupItems(selection: $currentSong) {
      for song in playlist {
        // Create a popup item for each song in the playlist, with the song's art, name, album and playback controls.
        PopupItem(id: song, title: song.name, subtitle: song.albumName, image: song.art, progress: playbackState.progress) {
          playbackButtons(for: song, with: playbackState)
        }
      }
    }
}

[!TIP] Providing an empty list of popup items using this API is considered a developer error, and will result in an empty popup bar.

Default Popup Item

This family of modifiers updates the default popup item for the popup content hierarchy.

TabView {
  //Container content
}.popup(isBarPresented: $isPopupBarPresented, isPopupOpen: $isPopupOpen) {
  popupContent()
    .popupTitle("Welcome to LNPopupUI!", subtitle: "Enjoy!")
  	.popupImage(Image("MyImage"))
  	.popupBarButtons {
      ToolbarItemGroup(placement: .popupBar) {
        Link(destination: url) {
          Label("LNPopupUI", systemImage: "suit.heart.fill")
        }
      }
    }
}

[!NOTE]

This API is considered legacy, and while fully supported, it is recommended to switch to using either popupItem(popupItem:) or popupItems(selection:items:).

Appearance and Behavior

LNPopupUI provides functionality to present users with popup bars, open popups and let the user interact with them. By default, the framework chooses styles to match the user’s current operating system version, but can all be customized as required.

<p align="center"><img src="./Supplements/floating_bar_style.gif" width="414"/></p>

The defaults are:

  • iOS 26:

    • Floating compact bar style
    • Snap interaction style
    • Grabber close button style
  • iOS 17-18:

    • Floating bar style
    • Snap interaction style
    • Grabber close button style
  • iOS 1

View on GitHub
GitHub Stars573
CategoryDevelopment
Updated3h ago
Forks35

Languages

Swift

Security Score

100/100

Audited on Apr 7, 2026

No findings