FloatingPanel icon indicating copy to clipboard operation
FloatingPanel copied to clipboard

FloatingPanel not working as expected via Modal

Open pedrommcarrasco opened this issue 1 year ago • 1 comments

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

pedrommcarrasco avatar Jan 10 '25 18:01 pedrommcarrasco

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), ] }

I'm wondering why the hidden anchor's fractional inset is set to 1.0 🤔 If you want to hide the panel, there is no need to set a hidden anchor.

scenee avatar Jan 28 '25 11:01 scenee