Swiftpack.co - Package - SCENEE/FloatingPanel
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.

Build Status Version Carthage compatible Platform Swift 5


FloatingPanel is a simple and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app. The new interface displays the related contents and utilities in parallel as a user wants.

Maps Stocks



  • ☑ Simple container view controller
  • ☑ Fluid behavior using numeric springing
  • ☑ Scroll view tracking
  • ☑ Removal interaction
  • ☑ Multi panel support
  • ☑ Modal presentation
  • ☑ 4 positioning support(top, left, bottom, right)
  • ☑ 1~3 magnetic anchors(full, half, tip)
  • ☑ Layout support for all trait environments(i.e. Landscape orientation)
  • ☑ Common UI elements: surface, backdrop and grabber handle
  • ☑ Free from common issues of Auto Layout and gesture handling
  • ☑ Compatible with Objective-C

Examples are here.


FloatingPanel is written in Swift 5.0+. Compatible with iOS 11.0+.

The deployment is still iOS 10, but it is recommended to use this library on iOS 11+.

:pencil2: You would like to use Swift 4.0. Please use FloatingPanel v1.



FloatingPanel is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'FloatingPanel'

:pencil2: FloatingPanel v1.7.0 or later requires CocoaPods v1.7.0+ for swift_versions support.


For Carthage, add the following to your Cartfile:

github "scenee/FloatingPanel"

Swift Package Manager

Follow this doc.

Getting Started

Add a floating panel as a child view controller

import UIKit
import FloatingPanel

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    var fpc: FloatingPanelController!

    override func viewDidLoad() {
        // Initialize a `FloatingPanelController` object.
        fpc = FloatingPanelController()

        // Assign self as the delegate of the controller.
        fpc.delegate = self // Optional

        // Set a content view controller.
        let contentVC = ContentViewController()
        fpc.set(contentViewController: contentVC)

        // Track a scroll view(or the siblings) in the content view controller.
        fpc.track(scrollView: contentVC.tableView)

        // Add and show the views managed by the `FloatingPanelController` object to self.view.
        fpc.addPanel(toParent: self)

Present a floating panel as a modality

let fpc = FloatingPanelController()
let contentVC = ...
fpc.set(contentViewController: contentVC)

fpc.isRemovalInteractionEnabled = true // Optional: Let it removable by a swipe-down

self.present(fpc, animated: true, completion: nil)

You can show a floating panel over UINavigationController from the container view controllers as a modality of .overCurrentContext style.

:pencil2: FloatingPanelController has the custom presentation controller. If you would like to customize the presentation/dismissal, please see Transitioning.

View hierarchy

FloatingPanelController manages the views as the following view hierarchy.

FloatingPanelController.view (FloatingPanelPassThroughView)
 ├─ .backdropView (FloatingPanelBackdropView)
 └─ .surfaceView (FloatingPanelSurfaceView)
    ├─ .containerView (UIView)
    │  └─ .contentView (FloatingPanelController.contentViewController.view)
    └─ .grabber (FloatingPanelGrabberView)


Show/Hide a floating panel in a view with your view hierarchy

If you need more control over showing and hiding the floating panel, you can forgo the addPanel and removePanelFromParent methods. These methods are a convenience wrapper for FloatingPanel's show and hide methods along with some required setup.

There are two ways to work with the FloatingPanelController:

  1. Add it to the hierarchy once and then call show and hide methods to make it appear/disappear.
  2. Add it to the hierarchy when needed and remove afterwards.

The following example shows how to add the controller to your UIViewController and how to remove it. Make sure that you never add the same FloatingPanelController to the hierarchy before removing it.

NOTE: self. prefix is not required, nor recommended. It's used here to make it clearer where do the functions used come from. self is an instance of a custom UIViewController in your code.

// Add the floating panel view to the controller's view on top of other views.

// REQUIRED. It makes the floating panel view have the same size as the controller's view.
fpc.view.frame = self.view.bounds

// In addition, Auto Layout constraints are highly recommended.
// Constraint the fpc.view to all four edges of your controller's view.
// It makes the layout more robust on trait collection change.
fpc.view.translatesAutoresizingMaskIntoConstraints = false
  fpc.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0),
  fpc.view.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0.0),
  fpc.view.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0.0),
  fpc.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0),

// Add the floating panel controller to the controller hierarchy.

// Show the floating panel at the initial position defined in your `FloatingPanelLayout` object.
fpc.show(animated: true) {
    // Inform the floating panel controller that the transition to the controller hierarchy has completed.
    fpc.didMove(toParent: self)

After you add the FloatingPanelController as seen above, you can call fpc.show(animated: true) { } to show the panel and fpc.hide(animated: true) { } to hide it.

To remove the FloatingPanelController from the hierarchy, follow the example below.

// Inform the panel controller that it will be removed from the hierarchy.
fpc.willMove(toParent: nil)
// Hide the floating panel.
fpc.hide(animated: true) {
    // Remove the floating panel view from your controller's view.
    // Remove the floating panel controller from the controller hierarchy.

Scale the content view when the surface position changes

Specify the contentMode to .fitToBounds if the surface height fits the bounds of FloatingPanelController.view when the surface position changes

fpc.contentMode = .fitToBounds

Otherwise, FloatingPanelController fixes the content by the height of the top most position.

:pencil2: In .fitToBounds mode, the surface height changes as following a user interaction so that you have a responsibility to configure Auto Layout constrains not to break the layout of a content view by the elastic surface height.

Customize the layout with FloatingPanelLayout protocol

Change the initial layout

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    ... {
        fpc = FloatingPanelController(delegate: self)
        fpc.layout = MyFloatingPanelLayout()

class MyFloatingPanelLayout: FloatingPanelLayout {
    let position: FloatingPanelPosition = .bottom
    let initialState: FloatingPanelState = .tip
    var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
        return [
            .full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
            .half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .safeArea),
            .tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea),

Update your panel layout

There are 2 ways to update the panel layout.

  1. Manually set FloatingPanelController.layout to the new layout object directly.
fpc.layout = MyPanelLayout()
fpc.invalidateLayout() // If needed
  1. Returns an appropriate layout object in one of 2 floatingPanel(_:layoutFor:) delegates.
class ViewController: UIViewController, FloatingPanelControllerDelegate {
    func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
        return MyFloatingPanelLayout()

    // OR
    func floatingPanel(_ vc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout {
        return MyFloatingPanelLayout()

Support your landscape layout

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout {
        return (newCollection.verticalSizeClass == .compact) ? LandscapePanelLayout() : FloatingPanelBottomLayout()

class LandscapePanelLayout: FloatingPanelLayout {
    let position: FloatingPanelPosition = .bottom
    let initialState: FloatingPanelState = .tip
    var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
        return [
            .full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
            .tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom, referenceGuide: .safeArea),
    func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] {
        return [
            surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 8.0),
            surfaceView.widthAnchor.constraint(equalToConstant: 291),

Use the intrinsic size of a content in your panel layout

  1. Lay out your content View with the intrinsic height size. For example, see "Detail View Controller scene"/"Intrinsic View Controller scene" of Main.storyboard. The 'Stack View.bottom' constraint determines the intrinsic height.
  2. Specify layout anchors using FloatingPanelIntrinsicLayoutAnchor.
class IntrinsicPanelLayout: FloatingPanelLayout {
    let position: FloatingPanelPosition = .bottom
    let initialState: FloatingPanelState = .full
    var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
        return [
            .full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 0, referenceGuide: .safeArea),
            .half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),

:pencil2: FloatingPanelIntrinsicLayout is deprecated on v1.

Specify an anchor for each state by an inset of the FloatingPanelController.view frame

Use .superview reference guide in your anchors.

class MyFullScreenLayout: FloatingPanelLayout {
    var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
        return [
            .full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .superview),
            .half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
            .tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .superview),


:pencil2: FloatingPanelFullScreenLayout is deprecated on v1.

Change the backdrop alpha

You can change the backdrop alpha by FloatingPanelLayout.backdropAlpha(for:) for each state(.full, .half and .tip).

For instance, if a panel seems like the backdrop view isn't there on .half state, it's time to implement the backdropAlpha API and return a value for the state as below.

class MyPanelLayout: FloatingPanelLayout {
    func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
        switch state {
        case .full, .half: return 0.3
        default: return 0.0

Customize the behavior with FloatingPanelBehavior protocol

Modify your floating panel's interaction

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    func viewDidLoad() {
        fpc.behavior =  CustomPanelBehavior()

class CustomPanelBehavior: FloatingPanelBehavior {
    let springDecelerationRate = UIScrollView.DecelerationRate.fast.rawValue + 0.02
    let springResponseTime = 0.4
    func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelState) -> Bool {
        return true

:pencil2: floatingPanel(_ vc:behaviorFor:) is deprecated on v1.

Activate the rubber-band effect on panel edges

class MyPanelBehavior: FloatingPanelBehavior {
    func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
        return true

Manage the projection of a pan gesture momentum

This allows full projectional panel behavior. For example, a user can swipe up a panel from tip to full nearby the tip position.

class MyPanelBehavior: FloatingPanelBehavior {
    func shouldProjectMomentum(_ fpc: FloatingPanelController, to proposedTargetPosition: FloatingPanelPosition) -> Bool {
        return true

Specify the panel move's boundary

FloatingPanelController.surfaceLocation in floatingPanelDidMove(_:) delegate method behaves like UIScrollView.contentOffset in scrollViewDidScroll(_:). As a result, you can specify the boundary of a panel move as below.

func floatingPanelDidMove(_ vc: FloatingPanelController) {
    if vc.isAttracting == false {
        let loc = vc.surfaceLocation
        let minY = vc.surfaceLocation(for: .full).y - 6.0
        let maxY = vc.surfaceLocation(for: .tip).y + 6.0
        vc.surfaceLocation = CGPoint(x: loc.x, y: min(max(loc.y, minY), maxY))

:pencil2: {top,bottom}InteractionBuffer property is removed from FloatingPanelLayout since v2.

Customize the surface design

Modify your surface appearance

// Create a new appearance.
let appearance = SurfaceAppearance()

// Define shadows
let shadow = SurfaceAppearance.Shadow()
shadow.color = UIColor.black
shadow.offset = CGSize(width: 0, height: 16)
shadow.radius = 16
shadow.spread = 8
appearance.shadows = [shadow]

// Define corner radius and background color
appearance.cornerRadius = 8.0
appearance.backgroundColor = .clear

// Set the new appearance
fpc.surfaceView.appearance = appearance

Use a custom grabber handle

let myGrabberHandleView = MyGrabberHandleView()
fpc.surfaceView.grabberHandle.isHidden = true

Customize layout of the grabber handle

fpc.surfaceView.grabberHandlePadding = 10.0
fpc.surfaceView.grabberHandleSize = .init(width: 44.0, height: 12.0)

:pencil2: Note that grabberHandleSize width and height are reversed in the left/right position.

Customize content padding from surface edges

fpc.surfaceView.contentPadding = .init(top: 20, left: 20, bottom: 20, right: 20)

Customize margins of the surface edges

fpc.surfaceView.containerMargins = .init(top: 20.0, left: 16.0, bottom: 16.0, right: 16.0)

The feature can be used for these 2 kind panels

  • Facebook/Slack-like panel whose surface top edge is separated from the grabber handle.
  • iOS native panel to display AirPods information, for example.

Customize gestures

Suppress the panel interaction

You can disable the pan gesture recognizer directly

fpc.panGestureRecognizer.isEnabled = false

Or use this FloatingPanelControllerDelegate method.

func floatingPanelShouldBeginDragging(_ vc: FloatingPanelController) -> Bool {
    return aCondition ?  false : true

Add tap gestures to the surface view

override func viewDidLoad() {
    let surfaceTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSurface(tapGesture:)))
    surfaceTapGesture.isEnabled = (fpc.position == .tip)

// Enable `surfaceTapGesture` only at `tip` position
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
    surfaceTapGesture.isEnabled = (vc.position == .tip)

Interrupt the delegate methods of FloatingPanelController.panGestureRecognizer

If you are set FloatingPanelController.panGestureRecognizer.delegateProxy to an object adopting UIGestureRecognizerDelegate, it overrides delegate methods of the pan gesture recognizer.

class MyGestureRecognizerDelegate: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return false

class ViewController: UIViewController {
    let myGestureDelegate = MyGestureRecognizerDelegate()

    func setUpFpc() {
        fpc.panGestureRecognizer.delegateProxy = myGestureDelegate

Create an additional floating panel for a detail

override func viewDidLoad() {
    // Setup Search panel
    self.searchPanelVC = FloatingPanelController()

    let searchVC = SearchViewController()
    self.searchPanelVC.set(contentViewController: searchVC)
    self.searchPanelVC.track(scrollView: contentVC.tableView)

    self.searchPanelVC.addPanel(toParent: self)

    // Setup Detail panel
    self.detailPanelVC = FloatingPanelController()

    let contentVC = ContentViewController()
    self.detailPanelVC.set(contentViewController: contentVC)
    self.detailPanelVC.track(scrollView: contentVC.scrollView)

    self.detailPanelVC.addPanel(toParent: self)

Move a position with an animation

In the following example, I move a floating panel to full or half position while opening or closing a search bar like Apple Maps.

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
    fpc.move(to: .half, animated: true)

func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
    fpc.move(to: .full, animated: true)

You can also use a view animation to move a panel.

UIView.animate(withDuration: 0.25) {
    self.fpc.move(to: .half, animated: false)

Work your contents together with a floating panel behavior

class ViewController: UIViewController, FloatingPanelControllerDelegate {
    func floatingPanelWillBeginDragging(_ vc: FloatingPanelController) {
        if vc.position == .full {
            searchVC.searchBar.showsCancelButton = false

    func floatingPanelWillEndDragging(_ vc: FloatingPanelController, withVelocity velocity: CGPoint, targetState: UnsafeMutablePointer<FloatingPanelState>) {
        if targetState.pointee != .full {


'Show' or 'Show Detail' Segues from FloatingPanelController's content view controller

'Show' or 'Show Detail' segues from a content view controller will be managed by a view controller(hereinafter called 'master VC') adding a floating panel. Because a floating panel is just a subview of the master VC(except for modality).

FloatingPanelController has no way to manage a stack of view controllers like UINavigationController. If so, it would be so complicated and the interface will become UINavigationController. This component should not have the responsibility to manage the stack.

By the way, a content view controller can present a view controller modally with present(_:animated:completion:) or 'Present Modally' segue.

However, sometimes you want to show a destination view controller of 'Show' or 'Show Detail' segue with another floating panel. It's possible to override show(_:sender) of the master VC!

Here is an example.

class ViewController: UIViewController {
    var fpc: FloatingPanelController!
    var secondFpc: FloatingPanelController!

    override func show(_ vc: UIViewController, sender: Any?) {
        secondFpc = FloatingPanelController()

        secondFpc.set(contentViewController: vc)

        secondFpc.addPanel(toParent: self)

A FloatingPanelController object proxies an action for show(_:sender) to the master VC. That's why the master VC can handle a destination view controller of a 'Show' or 'Show Detail' segue and you can hook show(_:sender) to show a secondary floating panel set the destination view controller to the content.

It's a great way to decouple between a floating panel and the content VC.

UISearchController issue

UISearchController isn't able to be used with FloatingPanelController by the system design.

Because UISearchController automatically presents itself modally when a user interacts with the search bar, and then it swaps the superview of the search bar to the view managed by itself while it displays. As a result, FloatingPanelController can't control the search bar when it's active, as you can see from the screen shot.

FloatingPanelSurfaceView's issue on iOS 10

  • On iOS 10, FloatingPanelSurfaceView.cornerRadius isn't not automatically masked with the top rounded corners because of UIVisualEffectView issue. See https://forums.developer.apple.com/thread/50854. So you need to draw top rounding corners of your content. Here is an example in Examples/Maps.
override func viewDidLayoutSubviews() {
    if #available(iOS 10, *) {
        visualEffectView.layer.cornerRadius = 9.0
        visualEffectView.clipsToBounds = true
  • If you sets clear color to FloatingPanelSurfaceView.backgroundColor, please note the bottom overflow of your content on bouncing at full position. To prevent it, you need to expand your content. For example, See Example/Maps App's Auto Layout settings of UIVisualEffectView in Main.storyboard.


Shin Yamamoto shin@scenee.com | @scenee


FloatingPanel is available under the MIT license. See the LICENSE file for more info.


Stars: 4088


2.1.0 - 2020-12-12T02:57:46

v2.1.0 introduces some new features. I welcome your feedback!

New features

  • FloatingPanelAdaptiveLayoutAnchor
    • This new anchor layouts a panel respecting the content size represented by an UILayoutGuide object.
    • This is a great potential up to you. See 'Show Adaptive Panel' example in the Samples app.
  • Add the default behavior for the escape action of Accessibility.


  • Open FloatingPanelDefaultBehavior class to allow to inherit it.


  • Update readme url for Transitioning.swift (#398)

Thank you to @warpling, @takaoh717, @groue and @kevinrenskers for this release!

2.0.1 - 2020-10-21T11:27:40

This version includes some major bug fixes so it is recommended to upgrade from 2.0.0.

Bug fixes

  • Add missing commas in migration guide #387 by @zntfdr
  • Ease the default velocity for a panel removal #395
  • Reset the moveAnimator prop after a move animation #396

Thanks you to @zntfdr, @aclassen, @sipersso and @mstana for this release!

2.0.0 - 2020-10-02T23:51:34

FloatingPanel 2.0 is the latest major release of FloatingPanel. As a major release, following Semantic Versioning conventions, 2.0 introduces API-breaking changes.

The migration guide is provided in order to ease the transition of existing applications using FloatingPanel 1.x to the latest APIs, as well as explain the design and structure of new and updated functionality.

Thank you for all users and contributors of this library. This version has been completed with your contributions and feedback. I continue to welcome your feedback.

Environment updates

  • Swift 5.0 or later
  • iOS 11 or later(iOS 10 might be working)
  • Update the directory structure and file names

Feature updates

  • Support top, left and right positions of the panel
  • Add ObjC compatibility
  • Improve the layout customization
  • Improve the behavior customization to handle it like UIScrollView
  • Improve the remove interaction
  • Fix many issues depending on API design

API updates

  • FloatingPanelController
    • Replace surfaceOriginY with surfaceLocation
    • Enable to update layout and behavior directly
    • Update scroll tracking API a bit to support multiple scroll view tracking in the future.
  • FloatingPanelControllerDelegate
    • Change floatingPanel(_:didMove:) behavior which is also called in the spring animation.
    • Update removal interaction delegate
      • Enable a panel to invoke the removal action at any state(position)
      • Add 
floatingPanel(_:shouldRemoveAt:with:) to determine whether it invokes the removal interaction.
    • Add floatingPanelDidEndDragging(_ vc:willAttract:)
  • FloatingPanelLayout
    • Introduce FloatingPanelLayoutAnchoring for the more flexible layout.
      • Add a factional panel position in the FloatingPanelController view
      • Replace FloatingPanelFullScreenLayout and FloatingPanelIntrinsicLayout
  • FloatingPanelBehavior
    • Introduce the new spring effect using Numeric springing
    • Add properties to contorl numeric springing
    • Remove .topInteractionBuffer/.bottomInteractionBuffer
      • You can control the panel max/min position in floatingPanel(_:didMove:) delegate method
  • SurfaceView
    • UseSurfaceAppearnace to specify the rounding corners, shadow and background color.
  • FloatingPanelPanGestureRecognizer
    • Add delegateProxy to intercept the gesture recognizer delegate.
  • Improve API naming
    • FloatingPanelPosition is now FloatingPanelState
      • Now FloatingPanelPosition is used to specify the panel positioning in a screen.
    • FloatingPanelGrabberHandleView is now GrabberView (Swift only)
    • FloatingPanelSurfaceView is now SurfaceView (Swift only)
    • FloatingPanelBackdropView is now BackdropView (Swift only)
    • "decelerate" term is replaced with "attract" because the spring behavior is actually magnetic
  • Misc
    • Add UISpringTimingParameters(decelerationRate:frequencyResponse:initialVelocity:)

Behavior updates

  • Layout
    • FloatingPanel doesn't disable constraints to Safe area in a content view so that the behavior of a content view always respects Safe area which is global in a screen. If it's not clear, please try Samples.app > Detail Panel and swipe it up to full state.
  • BackdropView
    • Disable the dismissal action of backdrop by default
      • You can enable it to set fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true

1.7.6 - 2020-09-19T03:00:30

This is the last release of v1. This includes some bug fixes and the removal interaction update.

Behavior change

  • Enable the removal interaction at any positions upon the conditions (#335)

Bug fixes

  • Fix quick pull down (#385)
  • Use 'prominent' blur effect in Maps sample (#379)
  • Small typo fixes (#378)
  • Update README.md sample code (#371, #367)

Thank you to @lekom, @knchst, @jstart, @ghazel and @michalraska for this release!

1.7.5 - 2020-06-03T23:20:36

v1.7.5 has a bunch of bug fixes. There is no update to change the behavior and specs. So it's recommended to upgrade from v1.7.x

Bug fixes

  • Fix a build error on Xcode 11.4 (#337)
  • Support the initial hidden position not including the supported positions (#344, #345)
  • Fix an invalid indicator insets of the tracking scroll view (#346)
  • Fix the memory leak of FloatingPanelController object (#347, #350)
  • Avoid the invalid safe area insets in a table view with static cells (#330, #353)
  • Fix the animation velocity's sign (#354)
  • Fix {top,bottom} constant's boundary in updating panel interactively (#349, #352)
  • Fix the panel behavior in a sheet modal (#358)
  • Fix the constraints break on fitToBounds mode (#359)

Thank you to @Isuru-Nanayakkara, @alexander-lsvk, @mhdhejazi, @kohei1218, @chriskilpin, @FlashTang and @GrigoryUlanov for this release!

1.7.4 - 2020-02-29T09:43:03

This release has a hotfix for v1.7.3 as follows. (v1.7.3 has already been removed v1.7.3 from GitHub and CocoaPods. )


  • Ignore .swiftpm/ folde by @zntfdr


  • Fix floatingPanel(_:contentOffsetForPinning:) delegate name

Thank you so much to @zntfdr.


This release includes a new delegate method and bug fixes. It is highly recommended to upgrade if you use v1.7.2 or display multiple panels overlapping.

New APIs

  • Add floatingPanel(_:contentOffsetForPinning:) delegate method (#314)
func floatingPanel(_ vc: FloatingPanelController, contentOffsetForPinning trackedScrollView: UIScrollView) -> CGPoint

Bug fixes

  • Fix an issue where the pan gesture recognizer doesn't work well when multiple panels are displayed #308, #320.
  • Fix a breaking content offset of the tracked scroll view #293
  • Minor README.md typo #316
  • Fix a run script error #319

Thank you to @fedorpashin, @kohei1218, @lucio1392, @jacksonjude and @richardgroves for this release!

1.7.2 - 2020-01-30T00:19:21

This release includes a new API and many bug fixes. As usual this is recommended to upgrade. Thank you.


  • Add FloatingPanelController.nearbyPosition by @ramrcram #303


  • Return the child view controller to consult #311


  • Fix Maps.app's crash on device after the second launch
  • Fix a panel's move-up in dragging it down
  • Fix grabber area behavior
  • Fix not calling floatingPanelDidEndDecelerating when an decelerating animation interrupted
  • Make floatingPanelDidEndDragging's velocity zero when it won't be decelerated.

Thank you to @ramrcram and @lucio1392 for this release!

1.7.1 - 2019-11-23T03:36:24

I'm proud to announce the release of FloatingPanel 1.7.1. Some great improvements has been made. I recommend to upgrade to this version. Thank you.


  • Enable the swift module interfaces #287
    • If you build a framework from the source code on Xcode 11 or later, it has the module stability in Swift.
  • Improve the surface position evaluation and top scroll bouncing of content by @dmytrofrolov1 #272
  • Polish README by @peka2, @TadeasKriz


  • Fix addPanel(toParent:) #290

Thank you to @dmytrofrolov1, @peka2 and @TadeasKriz for this release!

v1.7.0: Add new and enhanced APIs - 2019-10-05T04:53:39

This release includes the new APIs to enhance the use cases. ✨ 🎉 ✨

And now FloatingPanel.podspec uses swift_versions! So CocoaPods v1.7.0 or later is required for the installation.

I would like to continue to enhance this UI component with you. Welcome to your report and PR.

New APIs


var contentMode: FloatingPanelController.ContentMode
    public enum ContentMode: Int {
        case `static`
        case fitToBounds

This allows to change the surface height as following a user interaction and fix the bottom of a panel. For example, you can display a fixed footer view upon Auto Layout constraints.

See also Show Detail Panel sample in Samples.app.

✏️ In .fitToBounds mode, you have a responsibility to configure Auto Layout constrains not to break the layout of a content view by the elastic surface height.


var containerMargins: UIEdgeInsets

This allows to modify the surface container margins. For example, the following use cases.

  • Facebook/Slack-like panel whose surface top edge is separated from the grabber handle.
  • iOS native panel to display AirPods information.

See also showContainerMargins sample in Samples.app.


var dismissalTapGestureRecognizer: UITapGestureRecognizer!

This allows to disable tap on backdrop view for panel dismissal. by @nderkach


var positionReference: FloatingPanelLayoutReference
public enum FloatingPanelLayoutReference: Int {
    case fromSafeArea = 0
    case fromSuperview = 1

This allows to change the referred bounds used to calculate each position from FloatingPanelLayout.insetFor(_:)


  • Add fitToBounds content mode
  • Add support for CocoaPods version 1.7 swift_version by @joshuafinch
  • Add 2 samples, showPageContentView and showContainerMargins in Samples.app
  • Add test cases for new features


  • Call floatingPanelDidEndRemove when dismiss with tap on backdrop view by @rikusouda

Thank you to @joshuafinch @nderkach and @rikusouda for this release!

v1.6.6 - 2019-09-28T02:17:13

It includes some maintenance updates. It is the last release in 1.6.x.


  • Clean up code
  • Support Xcode 11 and Swift 5.1


  • Fix iOS 13 issues

v1.6.5 - 2019-08-31T04:48:10


  • Return true for FloatingPanelSurfaceView.requiresConstraintBasedLayout
  • Update README for UISearchController issue #248
  • Add move-to-hidden tests
  • Don't unregister safeAreaInsetsObservation in hide()
  • Improve floatingPanelDidChangePosition and tigger it on removal by David Hart
  • Support bottom content inset for container view (#257) by @nderkach


  • Samples App: Fix UISearchBar's _searchField access #243

Thank you to David Hart, @nderkach, @swiftymf and @roblabs for this release!

v1.6.4 - 2019-08-09T14:50:45

This release contains some bugfixes for the animation interruption.


  • Fix not calling floatingPanelDidEndDecelerating delegate after interruption
  • Always call startInteraction before endInteraction to keep the delegate call convention.
  • Fix stopping a panel b/w anchors after an interruption

v1.6.3: Revamp the core logic - 2019-07-26T14:00:32

This has a bunch of bugfixes and improvements. It's more stable than the earlier versions.

The core logic was refactored along with many new unit tests and then the position handling, scroll lock and animation interruption become robust. I highly recommend to upgrade to the version.

This is a big milestone to go to the next step, which is the initial version supporting the elastic height and top-to-bottom layout.


  • Add unit tests for the layout and the animation logic.
  • Refactor the core logic for the next features.
  • Revise the doc comments.


  • Fix the .hidden position handling. Now you can use .hidden position as well as other positions.
  • Fix the scroll lock and unlock when an animation interrupted.
  • Fix a bug of the removal interaction.
  • Fix the behavior when the animation is interrupted over the top most position.
  • Fix an ambiguous layout error after swiping down from full to half position.
  • Stop the top edge bounce when a tracking scroll is decelerating using an interruptible animator.
  • Fix the SafeArea update
  • Fix scroll lock just before/after dragging down in the grabber area

v1.6.2 - 2019-07-25T08:10:24


  • Fix the scroll indicator lock on a contentVC reset
  • Refactor FloatingPanel and FloatingPanelLayoutAdapter a bit by the new unit tests.
  • Prevent potential 'unexpectedly found nil' fatal error

v1.6.1: Support SwiftPM on Xcode11! - 2019-06-29T00:27:18


  • Add Swift Package Manager support by @robbiet480


  • Fix the crash while closeing via dragging #216
  • Fix closing panel during internal scroll view bounce #218
  • Remove workaround for tableView(_:didSelectRowAt:) issue #225
  • Fix an unexpected layout update in the background on iOS13 #228

Thank you to @robbiet480 for your SwiftPM support! Thank you to @cozzin, @Heltisace and @Isuru-Nanayakkara for your issues and cooperations!

v1.6.0: Enhanced FloatingPanelSurfaceView - 2019-06-03T13:23:19

New features


Add new excellent APIs by @nderkach and @SvenTiigi to make the layout flexible

  • Customize the grabber layout
+ public var grabberTopPadding: CGFloat
+ public var grabberHandleWidth: CGFloat
+ public var grabberHandleHeight: CGFloat
  • Customize the container top inset.
+ public var containerTopInset: CGFloat
  • Customize a content layout from the container.
+ public var contentInsets: UIEdgeInsets


  • Add rubber band effect for top and bottom edge
+ func allowsRubberBanding(for edge: UIRectEdge) -> Bool



  • Replace some properties as a constant stored property
-    public var grabberHandle: GrabberHandleView!
+    public let grabberHandle: GrabberHandleView = GrabberHandleView()
-    public var containerView: UIView!
+    public let containerView: UIView = UIView()
  • Modify containerView.layer.masksToBounds behavior on cornerRadius changed. See here more detail.


  • Add some unit tests on iOS 10, 11 and 12 in CI.


  • Calling FloatingPanel.move(to:animated:completion:) locks scroll view #198
  • Weird crashes happen when FloatingPanel was updated from v1.4.1 to 1.5.x #206
  • A UIVisualEffectView in a content VC isn't working on iOS10

Thank you to @nderkach and @SvenTiigi for your proposal and PR! Thank you to @mattt, @Isuru-Nanayakkara and @mergesort for your helpful issues and cooperations!

v1.5.1 - 2019-04-26T04:43:05

This release includes some improvements and bug fixes✨ 🎉 ✨


  • Allow subclassing FloatingPanelController #173
  • Add a sample code for panels in PageViewController #172
  • Rename backgroudView with containerView in FloatingPanelSurfaceView
- public var backgroundView: UIView!
+ public var containerView: UIView!
  • Remove obsolete SWIFT_WHOLE_MODULE_OPTIMIZATION setting from FloatingPanel.podspec
  • Fix typo


  • Fix the mask bounds of the surface view #190
  • Fix touch cancelling on first tap after a moving animation #164

Thank you to @zntfdr for your improvements! Thank you to @xysverma, @GerdC, @peterstojanowski, @RpX974, @congmn and @ThangTruong921 for your helpful issue reports and cooperations!

v1.5.0: Support Swift 5.0 - 2019-04-06T01:54:31

Now FloatingPanel supports Swift 5.0. 🎉✨✨ #177 See also README > Requirements 👍

All examples are upgraded to Swift 5.0, but the default Swift version of the library leaves Swift 4.0 because it avoids build errors with Carthage on each Xcode version from the source compatibility between Swift 4.0, 4.2 and 5.0. (All of Xcode 9.4.1+ support Swift 4.0)

With regard to CocoaPods, for now, a user needs to override SWIFT_VERSION appropriately in Podfile. But after CocoaPods v1.7.0, the pod spec is going to support swift_versions key and then you won't need it.

v1.4.1 - 2019-04-04T00:42:12

This release includes many improvements and bug fixes✨ 🎉 ✨


  • Remove swapping scroll delegate #128
  • Allow to use .hidden position as an initial one #150
var initialPosition: FloatingPanelPosition {
    return .hidden
  • Open FloatingPanelSurfaceView.backgroundView #169
-  private var backgroundView: UIView!
+  public var backgroundView: UIView!
  • Adds support for respecting initial safeAreaInsets. #155
  • Notify retain cycle #110


  • Fix retain cycle in FloatingPanel #158
  • Fix interruption #159
  • Fix UI Extension #165
  • Fix scroll fitting #180
  • Fix typo #162
  • Fix Jump while dragged up or down #154
  • Fix half mode panel is dragged to the top of the screen #167

Thank you to @trispo, @bryansum, @zntfdr for your improvements and bugfixes! Thank you to @djdance, @muukii , @vovkaprigarin, @andreaslindahl, @musa-almatri for your helpful issue reports and cooperations!

v1.4.0 - 2019-02-27T01:09:51

This release includes some new features and improvements to enhance the panel interaction ✨ 🎉 ✨

Now you can..

  • Change a panel's width with AutoLayout in moving it(See Samples.app -> Show Tab Bar -> Tab 3). Because a panel frame is always changed using Auto Layout. It will be able to let you configure more flexible layout of a moving panel with FloatingPanelLayout improvement in the future.
  • Stop beginning a panel dragging by floatingPanelShouldBeginDragging delegate method
  • Jump over a half position by interaction(i.e. from tip to full and vise versa) using FloatingPanelBehavior.shouldProjectMomentum(_:for:)
  • Configure a panel layout using FloatingPanelFullScreenLayout without bothering about SafeArea stuffs.

New Features


Add floatingPanelShouldBeginDragging(_:). You can stop to begin dragging on the conditions. It's useful to handle other pan gestures or touches simultaneously with a floating panel.

+ public func floatingPanelShouldBeginDragging(_:)


Add 2 methods to configure the momentum projection

+ func shouldProjectMomentum(_ fpc: FloatingPanelController, for proposedTargetPosition: FloatingPanelPosition) -> Bool`
+ func momentumProjectionRate(_ fpc: FloatingPanelController) -> CGFloat


You can configure all insets of the surface from the superview(FloatingPanelController.view), not from the safe area.



Enable to cancel the scroll view tracking

- public func track(scrollView: UIScrollView) {
+ public func track(scrollView: UIScrollView?) {


  • Move the surface view by a top layout constraint, not by modifying the frame directly.
  • Normalize the projected position
  • Modify disabling the bottom constraint on top overflow.
  • Revamp Scroll draw-down interaction by a tracking scroll view. It becomes more smooth


  • Open the default layout/behavior


  • Add a sample of the advanced layout animation


  • Fix #133: FloatingPanel can be dragged outside safe area (with new logic)

v1.3.5 - 2019-02-23T02:18:37

This release has major bugfixes and improvements for the panel interaction 👍 This is going to be last release for v1.3.x. v1.4.0 will be released soon to support sparking-joy features requested in issues.


  • Don't block tap/long press gestures(Always recognize them in parallel)
  • Stop interrupting a content offset of a tracking scroll view at the end of an interaction


  • Fix the wrong layout update in iOS10
  • Fix a buggy scroll offset of a tracking scroll view
  • Fix the animation interruption (Now a user can use an interruptive animation safely)
  • Fix backdrop handling on an animation interrupt
  • Fix dragging outside safe area #133

v1.3.4 - 2019-02-14T00:45:21

This release has some bugfixes Thanks to @modestman and @g00m contributions 👍


  • Fix issue with jumping floating panel while dragging #126
  • Fix typo

v1.3.3 - 2019-02-02T03:20:14

This release has some bugfixes💪 Thanks to @mergesort, @datwelk, @lohenyumnam contributions 🥇


  • Expand the surface mask's height #123 to prevent cutting off a content #112
  • Fix the presentation modally when fpc is reused #122
  • Restore original scroll view delegate when updating content VC #114 @datwelk

Experimental feature

  • FloatingPanelFullScreenLayout
    • Display a surface view with a full inset from the superview.

v1.3.2 - 2019-01-18T00:31:18

This release includes some bugfixes💪 Thanks to @mergesort, @Pivovar63, @fmessina, @CedricGatay contributions 🥇


  • Add FloatingPanelController.init(delegate:)


  • Support Swift 4.1 & Xcode 9.4


  • Fix the modal presentation #92
  • Fix the event handling on presentation modally #99
  • Fix a crash on tap dismissal with a second touch #100
  • fix(delegateIgnored): Allow to set delegate on init #105
  • Fix unsatisfiable constraints error for safe area bottom on full state

v1.3.1 - 2018-12-30T00:53:01

This release includes many bugfixes of layout problems on v1.3.0 💪
Please update v1.3.0 to this version.


  • I recommend to clean the framework with '⌘ + Shift + K' & rebuild it. Xcode's new build system doesn't rebuild it automatically so that you could use the old binary of this library.



func floatingPanel(_ vc: FloatingPanelController, shouldRecognizeSimultaneouslyWith gestureRecognizer: UIGestureRecognizer) -> Bool

See also Issue #76 and this sample code, https://github.com/SCENEE/FloatingPanel/blob/v1.3.1/Examples/Samples/Sources/ViewController.swift#L284


Now the view hierarchy is as below. (This is also written on 'View hierarchy' in README)

 ├─ .backdropView(FloatingPanelBackdropView)
 └─ .surfaceView(FloatingPanelSurfaceView)
          ├─ .contentView(FloatingPanelController.contentViewController.view)
          └─ .grabberHandle(GrabberHandleView)
  • Remove FloatingPanelSurfaceWrapperView.
  • Remove the content wrapper view in FloatingPanelSurfaceView
  • Modify the logic of the surface view's height using AutoLayout entirely


  • Any gestures don't wait for FloatingPanelController.panGestureRecognizer #76, #88
    • Add new delegate method to customize the failure requirements and simultaneousness of FloatingPanelController.panGestureRecognizer
  • Fix a layout problem on SafeArea.Top on a navigation bar with search bar
  • Fix content offsets of a tracking scroll view
  • Fix intrinsic height
  • Fix layout on presentation modally
  • Fix UI freeze on the presentation modally from the second time
  • Fix bugs on iOS 10
  • Fix swizzling UIViewController.dismiss(animated:)


  • Improve Samples app
    • Add "Settings" panel
    • Add "Reorder" button to show a sample to disable a panel's pan gesture

v1.3.0 - 2018-12-15T00:32:33

This release adds 3 great features. 💪💯 Thanks to @Durkslol, @TSCombine , @attheodo, @scandiumza for proposing new features. 🥇


  1. Please see Backward compatibility section in this release note at first.
  2. I recommend to clean the framework with '⌘ + Shift + K' & rebuild it after you update it.
    • Xcode's new build system doesn't rebuild it automatically so that there is a possible to use the old binary of this library.

New features

  • Present it as a modality
  • Intrinsic height(content fitting height) #57
  • Show/Hide a panel without adding/removing it every time #64

New APIs


  • show(animated:completion:)
  • hide(animated:completion:)


  • FloatingPanelIntrinsicLayout

See "Use Intrinsic height layout" for detail,


  • .hidden

Use cases

Present a floating panel as a Modality

let fpc = FloatingPanelController()
self.present(fpc, animated: true)

Add a floating panel to a container view.

let fpc = FloatingPanelController()
fpc.view.frame = aContainer.bounds // MUST
fpc.show(animated: true) { 
	self.didMove(toParent: self)
let fpc = FloatingPanelController()
fpc.hide(animated: true) {
        // If needed
	self.willMove(toParent: nil)

Backward compatibility

Please note 2 points for this release. These might make you change your code, but I believe and hope it would make this library better for you.

  • View hierarchy change
  • .hidden position

View hierarchy change

From this release, the view hierarchy managed FloatignPanelController is changed.

  • FloatignPanelController.view isn't surfaceView anymore.
  • The backdropView isn't inserted to the parent view.

FloatignPanelController.view is a root container view adding the surfaceView and backdropView.

.hidden position

  • The initial value of FloatignPanelController.position is .hidden
  • FloatignPanelController.position is set to .hidden after calling hide(animated:completion:) as below.
fpc.hide(animated: true) {
       print(fpc.position) // .hidden
  • FloatingPanelLayout.insetFor(position:) might need the following change.
class ModalSecondLayout: FloatingPanelLayout {
    func insetFor(position: FloatingPanelPosition) -> CGFloat? {
        switch position {
        case .full: return 18.0
        case .half: return 262.0
        case .tip: return 44.0
+        case .hidden: return nil

v1.2.3 - 2018-12-07T10:10:29


  • Fix a wobbling at the animation start
    • Let the default interaction animator be uninterruptible
  • Fix invalid backdrop alpha

V1.2.2 - 2018-12-03T02:12:55


  • Fix an invalid content offset on height change Ref #71
  • Fix panning at grabber area Ref #34, #71
    • It's a recovery to #34 behavior.

v1.2.1 - 2018-11-26T04:20:37



func floatingPanelDidChangePosition(_ vc: FloatingPanelController) Ref #46


  • Fix the gesture handling to make UITableViewRowAction work.
  • Fix the interactive animation interruption to make it interruptible
  • Fix removing backdrop view

v1.2.0 - 2018-11-17T01:12:29

I've released v1.2.0!! This release has many improvements and bugfixes. As a result, the floating panel is more robust and fluid. I highly recommend to update it from the previous version.

It's all thank to you reports and contributions.

I would like to continue to enhance this UI component with you. Welcome to your report and PR.

New APIs

  • Add a removal interaction invoked when a floating pane is at the bottom position. Ref #8
  • Update the layout programmatically. Ref #39
  • Enable to customize the redirectional behavior.


  • isRemovalInteractionEnabled: Enable the removal interaction(swipe down)
  • updateLayout(): Update the layout object from the delegate and apply it to the views. It might be useful in an animation.


  • floatingPanelDidEndDraggingToRemove(_:withVelocity)
  • floatingPanelDidEndRemove(_:)


  • removalVelocity, removalProgress, removalInteractionAnimator(_:with): Customize the removal interaction
  • redirectionalProgress(_:from:to:) -> CGFloat: Customize the redirectional max progress.

Modified APIs


- show(contentVC, sender: nil)
+ set(contentViewController: contentVC)

If you use v1.1.0, a build will be broken, but it's easy to fix it. See #21 for the reason of this change.


  • Now FloatingPanelController allows to display a floating panel on only 1 supported position.
  • Improve scroll view handling to make it robust and more consistent between scrolling a scroll view and dragging a floating panel.
  • Enable to drag the panel using the grab handler regardless of the scrolling position. Thanks, @ffittschen!
  • Improved shadow, not darkening content background. Thanks, @futuretap.


  • Add missing constraint of the title in Stocks app Thanks, @kingcos!
  • Fix the surface view's background color is not updated when a value is set to FloatingPanelSurfaceView.backgroundColor. Ref #22
  • Fix a blackout of the backdrop when tip and half supported positions. Ref #27
  • Fix issues on a large title or translucent of UINavigationBar Ref #26, #28
  • Fix scrollview jumps after it moved programmatically Ref #35
  • Fix infinite recursion in FloatingPanel.responds(to:)


  • Update preconditions of the parent VC
  • Update Samples App to test issues.