131 lines
5.5 KiB
Swift
131 lines
5.5 KiB
Swift
// Copyright 2018-Present Shin Yamamoto. All rights reserved. MIT license.
|
|
|
|
import UIKit
|
|
|
|
/// An interface for generating behavior information to fine-tune the behavior of a panel.
|
|
@objc
|
|
public protocol FloatingPanelBehavior {
|
|
/// A floating-point value that determines the rate of oscillation magnitude reduction after the user lifts their finger.
|
|
///
|
|
/// The oscillation magnitude to attract a panel to an anchor can be adjusted this value between 0.979 and 1.0
|
|
/// in increments of 0.001. When this value is around 0.979, the attraction uses a critically damped spring system.
|
|
/// When this value is between 0.978 and 1.0, it uses a underdamped spring system with a damping ratio computed by
|
|
/// this value. You shouldn't return less than 0.979 because the system is overdamped. If the pan gesture's velocity
|
|
/// is less than 300, this value is ignored and a panel applies a critically damped system.
|
|
@objc optional
|
|
var springDecelerationRate: CGFloat { get }
|
|
|
|
/// A floating-point value that determines the approximate time until a panel stops to an anchor after the user lifts their finger.
|
|
@objc optional
|
|
var springResponseTime: CGFloat { get }
|
|
|
|
/// Returns a deceleration rate to calculate a target position projected a dragging momentum.
|
|
///
|
|
/// The default implementation of this method returns the normal deceleration rate of UIScrollView.
|
|
@objc optional
|
|
var momentumProjectionRate: CGFloat { get }
|
|
|
|
/// Asks the behavior if a panel should project a momentum of a user interaction to move the
|
|
/// proposed state.
|
|
///
|
|
/// The default implementation of this method returns `false`. This method is called for called
|
|
/// for all states defined by the current layout object.
|
|
@objc optional
|
|
func shouldProjectMomentum(
|
|
_ fpc: FloatingPanelController,
|
|
to proposedState: FloatingPanelState
|
|
) -> Bool
|
|
|
|
/// Returns the progress to redirect to the previous position.
|
|
///
|
|
/// The progress is represented by a floating-point value between 0.0 and 1.0, inclusive, where 1.0 indicates a panel is impossible to move to the next position. The default value is 0.5. Values less than 0.0 and greater than 1.0 are pinned to those limits.
|
|
@objc optional
|
|
func redirectionalProgress(_ fpc: FloatingPanelController, from: FloatingPanelState, to: FloatingPanelState) -> CGFloat
|
|
|
|
/// Asks the behavior whether the rubber band effect is enabled in moving over a given edge of the surface view.
|
|
///
|
|
/// This method allows a panel to activate the rubber band effect to a given edge of the surface view. By default, the effect is disabled.
|
|
@objc optional
|
|
func allowsRubberBanding(for edge: UIRectEdge) -> Bool
|
|
|
|
/// Returns the velocity threshold for the default interactive removal gesture.
|
|
///
|
|
/// In case ``FloatingPanel/FloatingPanelControllerDelegate/floatingPanel(_:shouldRemoveAt:with:)`` is implemented, this value will not be used. The default value of ``FloatingPanelDefaultBehavior`` is 5.5
|
|
@objc optional
|
|
var removalInteractionVelocityThreshold: CGFloat { get }
|
|
}
|
|
|
|
/// The default behavior object for a panel
|
|
///
|
|
/// This behavior object is fine-tuned to behave as a search panel(card) in Apple Maps on iPhone portrait orientation.
|
|
open class FloatingPanelDefaultBehavior: FloatingPanelBehavior {
|
|
public init() {}
|
|
|
|
open var springDecelerationRate: CGFloat {
|
|
return UIScrollView.DecelerationRate.fast.rawValue + 0.001
|
|
}
|
|
|
|
open var springResponseTime: CGFloat {
|
|
return 0.4
|
|
}
|
|
|
|
open var momentumProjectionRate: CGFloat {
|
|
return UIScrollView.DecelerationRate.normal.rawValue
|
|
}
|
|
|
|
open func redirectionalProgress(_ fpc: FloatingPanelController, from: FloatingPanelState, to: FloatingPanelState) -> CGFloat {
|
|
return 0.5
|
|
}
|
|
|
|
open func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
|
|
return false
|
|
}
|
|
|
|
open var removalInteractionVelocityThreshold: CGFloat = 5.5
|
|
}
|
|
|
|
class BehaviorAdapter {
|
|
unowned let vc: FloatingPanelController
|
|
fileprivate var behavior: FloatingPanelBehavior
|
|
|
|
init(vc: FloatingPanelController, behavior: FloatingPanelBehavior) {
|
|
self.vc = vc
|
|
self.behavior = behavior
|
|
}
|
|
|
|
var springDecelerationRate: CGFloat {
|
|
behavior.springDecelerationRate ?? FloatingPanelDefaultBehavior().springDecelerationRate
|
|
}
|
|
|
|
var springResponseTime: CGFloat {
|
|
behavior.springResponseTime ?? FloatingPanelDefaultBehavior().springResponseTime
|
|
}
|
|
|
|
var momentumProjectionRate: CGFloat {
|
|
behavior.momentumProjectionRate ?? FloatingPanelDefaultBehavior().momentumProjectionRate
|
|
}
|
|
|
|
var removalInteractionVelocityThreshold: CGFloat {
|
|
behavior.removalInteractionVelocityThreshold ?? FloatingPanelDefaultBehavior().removalInteractionVelocityThreshold
|
|
}
|
|
|
|
func redirectionalProgress(from: FloatingPanelState, to: FloatingPanelState) -> CGFloat {
|
|
behavior.redirectionalProgress?(vc, from: from, to: to) ?? FloatingPanelDefaultBehavior().redirectionalProgress(vc,from: from, to: to)
|
|
}
|
|
|
|
func shouldProjectMomentum(to: FloatingPanelState) -> Bool {
|
|
behavior.shouldProjectMomentum?(vc, to: to) ?? false
|
|
}
|
|
|
|
func allowsRubberBanding(for edge: UIRectEdge) -> Bool {
|
|
behavior.allowsRubberBanding?(for: edge) ?? false
|
|
}
|
|
}
|
|
|
|
extension FloatingPanelController {
|
|
var _behavior: FloatingPanelBehavior {
|
|
get { floatingPanel.behaviorAdapter.behavior }
|
|
set { floatingPanel.behaviorAdapter.behavior = newValue}
|
|
}
|
|
}
|