Skip to content

FloatingPanel not working as expected via Modal #657

@pedrommcarrasco

Description

@pedrommcarrasco

Description

When showing a panel modally, I'm expecting it to be completely removed from the view hierarchy once dismissed with a swipe animation. However this does not seem to be the case.

Expected behavior

Once dismissed, I'm able to present the same view controller again

Actual behavior

When trying to show a view controller that I've just dismissed, it doesn't appear and we get the following error on console:

2025-01-10 18:03:25.796459+0000 Dex[6371:130758] [Presentation] Attempt to present <FloatingPanel.FloatingPanelController: 0x10d1ffa00> on <DemoProject.TabBarController: 0x1071b7a00> (from <DemoProject.PokedexViewController: 0x107939e00>) which is already presenting <FloatingPanel.FloatingPanelController: 0x12481c800>.

Steps to reproduce

let floatingPanelController = FloatingPanelController.make(
    size: .expanded,
    position: .leading
)
floatingPanelController.isRemovalInteractionEnabled = true
floatingPanelController.set(contentViewController: navigationController)
present(floatingPanelController, animated: true, completion: nil)

extension FloatingPanelController {
    enum Position {
        case leading
        case trailing
    }

    enum Size {
        case compact
        case expanded

        var value: CGFloat {
            switch self {
            case .compact:
                return 0.4
            case .expanded:
                return 0.7
            }
        }

        var backdropAlpha: CGFloat {
            switch self {
            case .compact:
                return 0.0
            case .expanded:
                return 0.25
            }
        }
    }

    static func make(
        size: Size = .compact,
        position: Position = .trailing
    ) -> FloatingPanelController {
        let appearance = SurfaceAppearance()

        // Define shadows
        let shadow = SurfaceAppearance.Shadow()
        shadow.color = .black
        shadow.opacity = 0.15
        shadow.radius = 48.0
        shadow.spread = 8.0
        appearance.shadows = [shadow]

        // Define corner radius and background color
        appearance.cornerRadius = 12.0
        appearance.cornerCurve = .continuous
        appearance.backgroundColor = .clear

        let floatingPanelController = FloatingPanelController()

        switch position {
        case .leading:
            floatingPanelController.layout = LeadingFloatingPanelLayout(fraction: size.value, backdropAlpha: size.backdropAlpha)
        case .trailing:
            floatingPanelController.layout = TrailingFloatingPanelLayout(fraction: size.value, backdropAlpha: size.backdropAlpha)
        }

        floatingPanelController.behavior = RubberFloatingPanelBehavior()

        floatingPanelController.surfaceView.appearance = appearance
        floatingPanelController.surfaceView.grabberHandle.isHidden = true
        floatingPanelController.surfaceView.containerMargins = .init(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0)

        return floatingPanelController
    }
}

class TrailingFloatingPanelLayout: FloatingPanelLayout {
    let position: FloatingPanelPosition = .right
    let initialState: FloatingPanelState = .full
    let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring]
    private let backdropAlpha: CGFloat
    func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
        backdropAlpha
    }

    init(fraction: CGFloat = 0.4, backdropAlpha: CGFloat = 0.0) {
        self.backdropAlpha = backdropAlpha
        anchors = [
            .full: FloatingPanelLayoutAnchor(fractionalInset: 1.0 - fraction, edge: .left, referenceGuide: .safeArea),
            .hidden: FloatingPanelLayoutAnchor(fractionalInset: 1.0, edge: .left, referenceGuide: .safeArea),
        ]
    }
}

class LeadingFloatingPanelLayout: FloatingPanelLayout {
    let position: FloatingPanelPosition = .left
    let initialState: FloatingPanelState = .full
    let anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring]
    private let backdropAlpha: CGFloat
    func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
        backdropAlpha
    }

    init(fraction: CGFloat = 0.4, backdropAlpha: CGFloat = 0.0) {
        self.backdropAlpha = backdropAlpha
        anchors = [
            .full: FloatingPanelLayoutAnchor(fractionalInset: 1.0 - fraction, edge: .right, referenceGuide: .safeArea),
            .hidden: FloatingPanelLayoutAnchor(fractionalInset: 1.0, edge: .right, referenceGuide: .safeArea),
        ]
    }
}

class RubberFloatingPanelBehavior: FloatingPanelBehavior {
    func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
        true
    }
}

How do you display panel(s)?

  • Present modally

How many panels do you displays?

  • 1

Environment

Library version

2.8.6

Installation method

  • Swift Package Manager

iOS version(s)

18.2

Xcode version

16.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions