{"id":15050888,"url":"https://github.com/leonatan/lnpopupcontroller","last_synced_at":"2025-05-13T21:09:10.952Z","repository":{"id":36928709,"uuid":"41235941","full_name":"LeoNatan/LNPopupController","owner":"LeoNatan","description":"A framework for presenting view controllers as popups of other view controllers, much like the Apple Music and Podcasts apps.","archived":false,"fork":false,"pushed_at":"2025-04-27T11:41:54.000Z","size":267951,"stargazers_count":3075,"open_issues_count":0,"forks_count":344,"subscribers_count":64,"default_branch":"master","last_synced_at":"2025-04-28T14:05:03.927Z","etag":null,"topics":["cocoa-touch","ios","lnpopupcontroller","mac-catalyst","objective-c","popup","swift","uiviewcontroller","xcode"],"latest_commit_sha":null,"homepage":"","language":"Objective-C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LeoNatan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"LeoNatan","custom":["paypal.me/LeoNatan25"]}},"created_at":"2015-08-23T04:42:20.000Z","updated_at":"2025-04-24T07:49:37.000Z","dependencies_parsed_at":"2022-08-08T18:30:22.478Z","dependency_job_id":"30af833e-8892-40eb-b4e3-6eefa59856b8","html_url":"https://github.com/LeoNatan/LNPopupController","commit_stats":{"total_commits":598,"total_committers":22,"mean_commits":"27.181818181818183","dds":0.3110367892976589,"last_synced_commit":"0dfea7c146953f34ce3eb3d0793e90df17640891"},"previous_names":[],"tags_count":232,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoNatan%2FLNPopupController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoNatan%2FLNPopupController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoNatan%2FLNPopupController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeoNatan%2FLNPopupController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LeoNatan","download_url":"https://codeload.github.com/LeoNatan/LNPopupController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251326836,"owners_count":21571634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cocoa-touch","ios","lnpopupcontroller","mac-catalyst","objective-c","popup","swift","uiviewcontroller","xcode"],"created_at":"2024-09-24T21:29:40.202Z","updated_at":"2025-04-28T14:05:06.706Z","avatar_url":"https://github.com/LeoNatan.png","language":"Objective-C++","readme":"# LNPopupController\n\nLNPopupController is a framework for presenting view controllers as popups of other view controllers, similar to the Apple Music and Podcasts mini-players.\n\nFor SwiftUI, check out the [LNPopupUI library](https://github.com/LeoNatan/LNPopupUI).\n\n[![GitHub release](https://img.shields.io/github/release/LeoNatan/LNPopupController.svg)](https://github.com/LeoNatan/LNPopupController/releases) [![GitHub stars](https://img.shields.io/github/stars/LeoNatan/LNPopupController.svg)](https://github.com/LeoNatan/LNPopupController/stargazers) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/LeoNatan/LNPopupController/master/LICENSE) \u003cspan class=\"badge-paypal\"\u003e\u003ca href=\"https://paypal.me/LeoNatan25\" title=\"Donate to this project using PayPal\"\u003e\u003cimg src=\"https://img.shields.io/badge/paypal-donate-yellow.svg?style=flat\" alt=\"PayPal Donation Button\" /\u003e\u003c/a\u003e\u003c/span\u003e\n\n[![GitHub issues](https://img.shields.io/github/issues-raw/LeoNatan/LNPopupController.svg)](https://github.com/LeoNatan/LNPopupController/issues) [![GitHub contributors](https://img.shields.io/github/contributors/LeoNatan/LNPopupController.svg)](https://github.com/LeoNatan/LNPopupController/graphs/contributors) [![Swift Package Manager compatible](https://img.shields.io/badge/swift%20package%20manager-compatible-green)](https://swift.org/package-manager/) [![Carthage compatible](https://img.shields.io/badge/carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/open_floating_popup.gif\"/\u003e\u003c/p\u003e\n\nOnce a popup bar is presented with a content view controller, the user can swipe or tap the popup bar at any point to present the popup. After finishing, the user dismisses the popup by either swiping or tapping the popup close button.\n\nThe framework is intended to be very generic and work in most situations, so it is implemented as a category over `UIViewController`. Each view controller can present a popup bar, docked to a bottom view. \nFor `UITabBarController` subclasses, the default docking view is the tab bar. \nFor `UINavigationController` subclasses, the default docking view is the toolbar.\nFor other classes, the popup bar is presented at the bottom of the screen. View controller subclasses can provide their own docking views.\n\nThe framework correctly maintains the safe area insets of the container controller’s view and its child controllers, as the popup bar is presented and dismissed.\n\nThe information displayed on the popup bar is provided dynamically with popup item objects (instances of the `LNPopupItem` class) associated with the popup content view controllers. To change this information, update the popup item of the view controller.\n\nGenerally, it is recommended to present the popup bar on the outermost container controller. So if you have a view controller contained in a navigation controller, which is in turn contained in a tab bar controller, it is recommended to present the popup bar on the tab bar controller.\n\nCheck the demo project for many common use cases of the framework in various scenarios. It contains examples in Swift and Objective C.\n\n### Features\n\n* Available for iOS 13 and above, as an Xcode framework or an SPM package\n* Good citizen in modern UIKit world\n* Modern Objective C syntax and great Swift interoperability\n* For SwiftUI, check out the [LNPopupUI library](https://github.com/LeoNatan/LNPopupUI).\n\n## Adding to Your Project\n\n### Swift Package Manager\n\nSwift Package Manager is the recommended way to integrate `LNPopupController` in your project.\n\n`LNPopupController` supports SPM versions 5.5 and above. To use SPM, you should use Xcode 13 to open your project. Click `File` -\u003e `Swift Packages` -\u003e `Add Package Dependency`, enter `https://github.com/LeoNatan/LNPopupController`. Select the version you’d like to use.\n\nYou can also manually add the package to your Package.swift file:\n\n```swift\n.package(url: \"https://github.com/LeoNatan/LNPopupController.git\", from: \"2.15.0\")\n```\n\nAnd the dependency in your target:\n\n```swift\n.target(name: \"MyExampleApp\", dependencies: [\"LNPopupController\"]),\n```\n\n### Carthage\n\nAdd the following to your Cartfile:\n\n```github \"LeoNatan/LNPopupController\"```\n\nMake sure you follow the Carthage integration instructions [here](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos).\n\n### Manual\n\nDrag the `LNPopupController.xcodeproj` project to your project, and add `LNPopupController.framework` to **Embedded Binaries** in your project target's **General** tab. Xcode should sort everything else on its own.\n\n### CocoaPods\n\nCocoaPods is not supported. There are many reasons for this. Instead of CocoaPods, use Swift Package Manager from within Xcode. You can continue using CocoaPods for for your other dependencies and Swift Package Manager for `LNPopupController`.\n\n## Using the Framework\n\n### Swift\n\nWhile the framework is written in Objective C, it uses modern Objective C syntax, so using the framework in Swift is very easy and intuitive.\n\n### Project Integration\n\nImport the module in your project:\n\n```swift\nimport LNPopupController\n```\n\n### Popup Items\n\nA popup item should always reflect the popup information about the view controller with which it is associated. The popup item should provide a title and subtitles to display in the popup bar, when the view controller is presented as a popup content controller. In addition, the item may contain additional buttons to display on the leading and/or trailing edges of the popup bar using `leadingBarButtonItems` and `trailingBarButtonItems`.\n\n### Managing the Popup Bar\n\nTo present the popup bar, create a content controller, update its popup item and present the popup bar using `presentPopupBar(with:animated:completion:)`.\n\n```swift\nlet demoVC = DemoPopupContentViewController()\ndemoVC.view.backgroundColor = .red\ndemoVC.popupItem.title = \"Hello World\"\ndemoVC.popupItem.subtitle = \"And a subtitle!\"\ndemoVC.popupItem.progress = 0.34\n    \ntabBarController?.presentPopupBar(with: demoVC, animated: true, completion: nil)\n```\n\nYou can present a new content controller while the popup bar is presented and when the popup itself is open.\n\nTo open and close the popup programatically, use `openPopup(animated:completion:)` and `closePopup(animated:completion:)` respectively.\n\n```swift\ntabBarController?.openPopup(animated: true, completion: nil)\n```\n\nAlternatively, you can present the popup bar and open the popup in one animation, using `presentPopupBar(with:openPopup:animated:completion:)`.\n\n```swift\ntabBarController?.presentPopupBar(with: demoVC, openPopup:true, animated: true, completion: nil)\n```\n\nTo dismiss the popup bar, use `dismissPopupBar(animated:completion:)`.\n\n```swift\ntabBarController?.dismissPopupBar(animated: true, completion: nil)\n```\n\nIf the popup is open when dismissing the popup bar, the popup content will also be dismissed.\n\n### Popup Container View Controllers\n\nAny `UIViewController` subclasses can be popup container view controllers. The popup bar is attached to a bottom docking view. By default, `UITabBarController` and `UINavigationController` subclasses return their bottom bars as docking view, while other controllers return a hidden 0pt height view on the bottom of the view. In your subclass, override `bottomDockingViewForPopupBar` and `defaultFrameForBottomDockingView` and return your view and frame accordingly. **The returned view must be attached to the bottom of the view controller's view, or results are undefined.**\n\n```swift\noverride var bottomDockingViewForPopupBar: UIView? {\n  return myCoolBottomView\n}\n\noverride var defaultFrameForBottomDockingView: CGRect {\n  var bottomViewFrame = myCoolBottomView.frame\n  \n  if isMyCoolBottomViewHidden {\n    bottomViewFrame.origin = CGPoint(x: bottomViewFrame.x, y: view.bounds.height)\n  } else {\n    bottomViewFrame.origin = CGPoint(x: bottomViewFrame.x, y: view.bounds.height - bottomViewFrame.height)\n  }\n  \n  return bottomViewFrame\n}\n```\n\n### Appearance and Behavior\n\n`LNPopupController` provides three distinct styles of popup look and feel, each based on Music app looks and feels, that Apple has introduced over the years. Popup bar styles are labeled \"floating”, “prominent\" and \"compact\", matching the appropriate Apple style. Popup interaction styles are labeled \"snap\" for modern style snapping popups and \"drag\" for iOS 9 interactive popup interaction. Popup close buttons styles are labeled \"chevron\" for modern style chevron close button and \"round\" for iOS 9-style close buttons. For each, there is a \"default\" style for choosing the most suitable one for the current platform and operating system version.\n\nThe defaults are:\n\n- iOS 17:\n\n  * Floating bar style\n\n  * Snap interaction style\n\n  * Grabber close button style\n\n  * No progress view style\n\n- iOS 16 and below:\n\n  - Prominent bar style\n\n  * Snap interaction style\n\n  * Chevron close button style\n\n  * No progress view style\n\nYou can also present completely custom popup bars. For more information, see [Custom Popup Bars](#custom-popup-bars).\n\nBy default, for navigation and tab bar container controllers, the appearance of the popup bar is determined according to the bottom bar's appearance. For other container controllers, a default appearance is used, most suitable for the current environment.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/floating_bar_style.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\nTo disable inheriting the bottom bar’s appearance, set the `inheritsAppearanceFromDockingView` property to `false`.\n\n#### Bar Style\n\nCustomizing the popup bar style is achieved by setting the popup bar's `barStyle` property.\n\n```swift\nnavigationController?.popupBar.barStyle = .compact\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/floating_no_scroll.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/modern_no_scroll.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/scroll.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Interaction Style\n\nCustomizing the popup interaction style is achieved by setting the popup presentation containing controller's `popupInteractionStyle` property.\n\n```swift\nnavigationController?.popupInteractionStyle = .drag\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/interaction_snap.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/interaction_drag.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Progress View Style\n\nCustomizing the popup bar progress view style is achieved by setting the popup bar's `progressViewStyle` property.\n\n```swift\nnavigationController?.popupBar.progressViewStyle = .top\n```\n\nTo hide the progress view, set the `progressViewStyle` property to `LNPopupBar.ProgressViewStyle.none`.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/progress_view_none.png\" width=\"360\"/\u003e\u003cbr/\u003e\u003cbr/\u003e\u003cimg src=\"./Supplements/progress_view_top.png\" width=\"360\"/\u003e\u003cbr/\u003e\u003cbr/\u003e\u003cimg src=\"./Supplements/progress_view_bottom.png\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Close Button Style\n\nCustomizing the popup close button style is achieved by setting the popup content view's `popupCloseButtonStyle` property.\n\n```swift\nnavigationController.popupContentView.popupCloseButtonStyle = .round\n```\n\nTo hide the popup close button, set the `popupCloseButtonStyle` property to `LNPopupCloseButton.Style.none`.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/close_button_none.png\" width=\"360\"/\u003e\u003cbr/\u003e\u003cbr/\u003e\u003cimg src=\"./Supplements/close_button_chevron.png\" width=\"360\"/\u003e\u003cbr/\u003e\u003cbr/\u003e\u003cimg src=\"./Supplements/close_button_round.png\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Text Marquee Scroll\n\nSupplying long text for the title and/or subtitle will result in a scrolling text, if text marquee is enabled. Otherwise, the text will be truncated.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/floating_no_scroll.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/scroll.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Popup Transitions\n\nThe framework supports popup image transitions:\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/popup_transitions.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\nTransitions are opt-in and require you either use an `LNPopupImageView` image view in your popup content, which is discovered automatically by the system, or provide a view that will serve as the transition target/source by implementing `viewForPopupTransition(from:to:)` in popup content controller.\n\nFor optimal results, always use `LNPopupImageView` instances that displays the same image displayed in the popup bar's image view. By default, the system discovers the `LNPopupImageView` image view in your content controller's view hierarchy automatically, and will use that as the transition target/source. The system will smoothly transition between the popup bar's image view and the `LNPopupImageView` instance, taking into account the corner radii and shadows of the views.\n\n\u003e [!TIP]\n\u003e When using automatic discovery, there must be only a single `LNPopupImageView` instance in your content controller's view hierarchy, or results will be undefined. For more advanced scenarios where automatic discovery fails, implement `viewForPopupTransition(from:to:)` in your content controller to return the correct instance.\n\nYou can return any custom view in `viewForPopupTransition(from:to:)` to serve as the transition target/source. The system will attempt to match the attributes of the provided view and the popup bar's image view as closely as possible to transition smoothly between them. Implement the `LNPopupTransitionView` protocol in your custom view to allow the system to smoothly transition between your custom view and the popup bar image view.\n\n\u003e [!CAUTION]\n\u003e Views returned from `viewForPopupTransition(from:to:)` must be part of the content controller's view hierarchy, or they will be ignored by the system and no transition will take place.\n\nTransitions are only available for prominent and floating popup bar styles with drag interaction style. Any other combination will result in no transition and this method will not be called by the system.\n\n#### Popup Bar Customization\n\n`LNPopupBar` exposes many APIs to customize the default popup bar's appearance. Use `LNPopupBarAppearance` objects to define the standard appearance of the bar.\n\nRemember to set the `inheritsAppearanceFromDockingView` property to `false`, or some of your customizations might be overridden by the bottom bar’s appearance.\n\n```swift\nlet appearance = LNPopupBarAppearance()\nappearance.titleTextAttributes = AttributeContainer()\n    .font(UIFontMetrics(forTextStyle: .headline).scaledFont(for: UIFont(name: \"Chalkduster\", size: 14)!))\n    .foregroundColor(UIColor.yellow)\nappearance.subtitleTextAttributes = AttributeContainer()\n    .font(UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: UIFont(name: \"Chalkduster\", size: 12)!))\n    .foregroundColor(UIColor.green)\n\nlet floatingBarBackgroundShadow = NSShadow()\nfloatingBarBackgroundShadow.shadowColor = UIColor.red\nfloatingBarBackgroundShadow.shadowOffset = .zero\nfloatingBarBackgroundShadow.shadowBlurRadius = 8.0\nappearance.floatingBarBackgroundShadow = floatingBarBackgroundShadow\n\nlet imageShadow = NSShadow()\nimageShadow.shadowColor = UIColor.yellow\nimageShadow.shadowOffset = .zero\nimageShadow.shadowBlurRadius = 3.0\nappearance.imageShadow = imageShadow\n\nif navigationController?.popupBar.barStyle == .floating {\n    appearance.floatingBackgroundEffect = UIBlurEffect(style: .dark)\n} else {\n    appearance.backgroundEffect = UIBlurEffect(style: .dark)\n    navigationController?.popupBar.inheritsAppearanceFromDockingView = false\n}\n\nnavigationController?.popupBar.standardAppearance = appearance\nnavigationController?.popupBar.tintColor = .yellow\n```\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/floating_custom.png\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/modern_custom.png\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/compact_custom.png\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### System Interactions\n\n##### Bar Transitions\n\nThe `hidesBottomBarWhenPushed` property is supported for navigation and tab bar controllers. When set to `true`, the popup bar will transition to the bottom of the pushed controller's view. Setting  `isToolbarHidden = true` and calling `setToolbarHidden(_:animated:)` are also supported.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/hidesBottomBar_TabBar.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/hidesBottomBar_Toolbar.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\n##### Tab Bar Sidebar\n\nStarting with iPadOS 18, the framework supports `UITabBarController` sidebars. When the sidebar displaces the underlying content, the popup bar moves out of the way.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/tab_bar_sidebar_floating.gif\" width=\"600\"/\u003e\u003cbr /\u003e\u003cbr /\u003e\u003cimg src=\"./Supplements/tab_bar_sidebar_prominent.gif\" width=\"600\"/\u003e\u003c/p\u003e\n\nWhen the sidebar overlays the underlying content, the popup bar dims together with the content:\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/tab_bar_sidebar_landscape_floating.png\" width=\"360\"/\u003e\u003c/p\u003e\n\n\n\n##### Status Bar Management\n\nStatus bar management of the popup content view controller is respected and applied when appropriate.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/statusbar_style.gif\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/statusbar_hidden.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\nHome indicator visibility control is respected and applied when appropriate.\n\n##### Context Menu Interactions\n\nContext menus are supported. Add a `UIContextMenuInteraction` interaction object to the popup bar, and it will behave as expected.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/popup_bar_context_menu.png\" width=\"360\"/\u003e\u003c/p\u003e\n\n##### Pointer Interactions\n\nPointer interactions are supported, and a default implementation is provided for system bar styles.\n\nFor custom popup bar controllers, the `LNPopupCustomBarViewController` class implements the `UIPointerInteractionDelegate` protocol. Implement the protocol's methods inside your subclass to implement custom pointer interactions.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/pointer_interaction.gif\"/\u003e\u003c/p\u003e\n\n##### Scroll-edge Appearance\n\nStarting with iOS 15, scroll-edge appearance is automatically disabled for toolbars and tab bars when a popup bar is presented, regardless of the scroll position of the content. Once the popup bar is dismissed, the scroll-edge appearance is restored.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/scroll_edge_appearance.gif\" width=\"360\"/\u003e\u003c/p\u003e\n\n#### Custom Popup Bars\n\nThe framework supports implementing custom popup bars:\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/custom_bar.png\" width=\"360\"/\u003e\u003c/p\u003e\n\nTo implement a custom popup bar, you subclass `LNPopupCustomBarViewController`.\n\nIn your `LNPopupCustomBarViewController` subclass, build your popup bar's view hierarchy and set the controller's `preferredContentSize` property with the preferred popup bar height. Override any of the `wantsDefaultTapGestureRecognizer`, `wantsDefaultPanGestureRecognizer` and/or `wantsDefaultHighlightGestureRecognizer` properties to disable the default gesture recognizers functionality in your custom popup bar.\n\nIn your subclass, implement the `popupItemDidUpdate()` method to be notified of updates to the popup content view controller's item, or when a new popup content view controller is presented (with a new popup item). You must call the `super` implementation of this method.\n\nFinally, set the `customBarViewController` property of the popup bar object to an instance of your `LNPopupCustomBarViewController` subclass. This will automatically change the bar style to `LNPopupBar.Style.custom`.\n\nThe included demo project includes two example custom popup bar scenes.\n\n\u003e [!TIP]\n\u003e Only implement a custom popup bar if you need a design that is significantly different than the provided [standard popup bar styles](#bar-style). A lot of care and effort has been put into integrating these popup bar styles with the UIKit system, including look, feel, transitions and interactions. Custom bars provide a blank canvas for you to implement a bar of your own, but if you end up recreating a bar design that is similar to a standard bar style, you are more than likely losing subtleties that have been added and perfected over the years in the standard implementations. Instead, consider using the [many customization APIs](#popup-bar-customization) to tweak the standard bar styles to fit your app’s design.\n\n#### ProMotion Support\n\n`LNPopupController` fully supports ProMotion on iPhone and iPad.\n\nFor iPhone 13 Pro and above, you need to add the `CADisableMinimumFrameDurationOnPhone` key to your Info.plist and set it to `true`. See [Optimizing ProMotion Refresh Rates for iPhone 13 Pro and iPad Pro](https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro?language=objc) for more information. `LNPopupController` will log a single warning message in the console if this key is missing, or is set to `false`.\n\n#### Interaction Gesture Recognizer\n\n`LNPopupContentView` exposes access to the popup interaction gesture recognizer in the way of the `popupInteractionGestureRecognizer` property. This gesture recognizer is shared for opening the popup, by swiping or panning the popup bar up (when the popup is closed), and closing the popup, by swiping or panning the popup content view down (when the popup bar is open).\n\nWhen opening the popup, the system queries the `viewForPopupInteractionGestureRecognizer` property of the popup content view controller to determine which view to add the interaction gesture recognizer. By default, the property returns the content controller's root view. Override the property's getter to change this behavior.\n\nThe system attempts to cooperate as best it can with other gestures, including system gestures, controls and scrolling. When the user scrolls inside the popup content view hierarchy, the system will do its best not to interfere with the user’s gestures, and will only react when at the edge of scrolled content.\n\nFor vertically scrolling content, the popup will close only when the user swipes or drags past the scroll content’s edge.\n\nFor horizontal scrolling content, the popup will close only when user swipes or drags down and there there is no horizontal scroll.\n\nFor both multidirectional scrolling content, only `isDirectionalLockEnabled = true` is supported. In that case, the popup will close if both conditions above are met.\n\nFor multidirectional scroll content, the system will not attempt to close the popup at any point. The user can still close the popup by tapping the close button or swiping or dragging outside of the scrollable area.\n\nYou can implement the delegate of the interaction gesture recognizer in order to influence its behavior, such as preventing popup interaction when the user is interacting with other controls or views inside the popup content view hierarchy.\n\n\u003e [!CAUTION]\n\u003e If you disable the gesture recognizer after opening the popup, you must monitor the state of the popup and reenable the gesture recognizer once closed by the user or through code. Instead, consider implementing the gesture recognizer's delegate and providing custom logic to disable the interaction.\n\n#### Full Right-to-Left Support\n\nThe framework has full right-to-left support.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/rtl_english.png\" width=\"360\"/\u003e \u003cimg src=\"./Supplements/rtl_hebrew.png\" width=\"360\"/\u003e\u003c/p\u003e\n\nBy default, the popup bar will follow the system's user interface layout direction, but will preserve the bar button items' order.\nTo customize this behavior, modify the popup bar's ```semanticContentAttribute``` and ```barItemsSemanticContentAttribute``` properties.\n\n#### Accessibility\n\nThe framework supports accessibility and will honor accessibility labels, traits, hints and values. By default, the accessibility label of the popup bar is the title and subtitle provided by the popup item.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"./Supplements/default_bar_accessibility_label.png\" width=\"360\"/\u003e\u003c/p\u003e\n\nTo modify the accessibility label and hint of the popup bar, set the `accessibilityLabel` and `accessibilityHint` properties of the `LNPopupItem` object of the popup content view controller.\n\n```swift\ndemoVC.popupItem.accessibilityLabel = NSLocalizedString(\"Custom popup bar accessibility label\", comment: \"\")\ndemoVC.popupItem.accessibilityHint = NSLocalizedString(\"Custom popup bar accessibility hint\", comment: \"\")\n```\n\nTo add accessibility labels and hints to buttons, set the `accessibilityLabel` and `accessibilityHint` properties of the `UIBarButtonItem` objects.\n\n```swift\nlet upNext = UIBarButtonItem(image: UIImage(named: \"next\"), style: .plain, target: self, action: #selector(nextItem))\nupNext.accessibilityLabel = NSLocalizedString(\"Up Next\", comment: \"\")\nupNext.accessibilityHint = NSLocalizedString(\"Double tap to show up next list\", comment: \"\")\n```\nTo modify the accessibility label and hint of the popup close button, set the `accessibilityLabel` and `accessibilityHint` properties of the `LNPopupCloseButton` object of the popup container view controller.\n\n```swift\ntabBarController?.popupContentView.popupCloseButton.accessibilityLabel = NSLocalizedString(\"Custom popup close button accessibility label\", comment: \"\")\ntabBarController?.popupContentView.popupCloseButton.accessibilityHint = NSLocalizedString(\"Custom popup close button accessibility hint\", comment: \"\")\n```\n\nTo modify the accessibility label and value of the popup bar progress view, set the `accessibilityProgressLabel` and `accessibilityProgressValue` properties of the `LNPopupItem` object of the popup content view controller.\n\n```swift\ndemoVC.popupItem.accessibilityImageLabel = NSLocalizedString(\"Custom image label\", comment: \"\")\ndemoVC.popupItem.accessibilityProgressLabel = NSLocalizedString(\"Custom accessibility progress label\", comment: \"\")\ndemoVC.popupItem.accessibilityProgressValue = \"\\(accessibilityDateComponentsFormatter.stringFromTimeInterval(NSTimeInterval(popupItem.progress) * totalTime)!) \\(NSLocalizedString(\"of\", comment: \"\")) \\(accessibilityDateComponentsFormatter.stringFromTimeInterval(totalTime)!)\"\n```\n\n## Notes\n\n* Legacy non-translucent tab bar and toolbars are not supported and can cause visual artifacts or layout glitches. Apple has many problem with such bars, and supporting those is not a priority for `LNPopupController`.\n  * The correct way to achieve an opaque bar is to use the `UIBarAppearance.configureWithOpaqueBackground()` API, which is supported by `LNPopupController`.\n* Manually setting bottom bar properties, such as setting a tab bar’s or a toolbar’s `isHidden = true` **is explicitly discouraged by Apple and not supported by the framework**; it will lead to undefined behavior by the framework.\n\n## Acknowledgements\n\nThe framework uses:\n* [MarqueeLabel](https://github.com/cbpowell/MarqueeLabel) Copyright (c) 2011-2023 Charles Powell\n* [smooth-gradient](https://github.com/janselv/smooth-gradient) Copyright (c) 2016 Jansel Valentin\n\nAdditionally, the demo project uses:\n\n* [LoremIpsum](https://github.com/lukaskubanek/LoremIpsum) Copyright (c) 2013 Lukas Kubanek\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=LeoNatan/LNPopupController\u0026type=Date)](https://star-history.com/#LeoNatan/LNPopupController\u0026Date)\n","funding_links":["https://github.com/sponsors/LeoNatan","paypal.me/LeoNatan25","https://paypal.me/LeoNatan25"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleonatan%2Flnpopupcontroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleonatan%2Flnpopupcontroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleonatan%2Flnpopupcontroller/lists"}