Documentation/FloatingPanel 2.0 Migration Guide.md
FloatingPanel 2.0 is the latest major release of FloatingPanel. As a major release, following Semantic Versioning conventions, 2.0 introduces API-breaking changes.
This 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.
FloatingPanelLayout is redesigned. There is no implicit rules to lay out a panel anymore.floatingPanelDidMove(_:) delegate method is also called while a panel is moving. The method behavior becomes same as scrollViewDidScroll(_:) in UIScrollViewDelegate. And in the method a library consumer is able to change a panel location.FloatingPanelPosition is now FloatingPanelState.
FloatingPanelPosition in v2 is used to specify a panel position(top, left, bottom and right) in a screen.FloatingPanelSurfaceView is SurfaceView only in Swift.FloatingPanelBackdropView is BackdropView only in Swift.FloatingPanelGrabberHandleView is GrabberView only in Swift.FloatingPanelControllerlayout and behavior properties can be changed directly without using the delegate methods.fpc.behavior = SearchPaneliPadBehavior()
fpc.layout = SearchPaneliPadLayout()
fpc.invalidateLayout() // If needed
addPanel(toParent:) changes to specify an index of subviews of a view in which a panel is added.- public func addPanel(toParent parent: UIViewController, belowView: UIView? = nil, animated: Bool = false) {
+ public func addPanel(toParent parent: UIViewController, at viewIndex: Int = -1, animated: Bool = false) {
surfaceOriginY is now surfaceLocation.updateLayout is now invalidateLayout.untrack(scrollView:) is used to disable the scroll tracking.FloatingPanelControllerDelegatefloatingPanelDidEndDragging(_ vc:willAttract:) is added to check whether a panel will continue to move after dragging.floatingPanelDidMove(_:) behavior changes. The method is also called in the spring animation.floatingPanel(_:shouldRemoveAt:with:) is added to determine whether it invokes the removal interaction in any state.floatingPanelWillRemove(_:) is added.floatingPanel(_: FloatingPanelController, layoutFor size: CGSize) is added to respond to a layout change in regular size classes on iPad.func floatingPanel(_ fpc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout {
if aCondition(for: size) {
return SearchPanelLayout()
}
return SearchPanel2Layout()
}
targetState argument type of floatingPanelWillEndDragging(_:withVelocity:targetState:) is changed from FloatingPanelState to UnsafeMutablePointer<FloatingPanelState> to modify a target state on demand.func floatingPanelWillEndDragging(_ vc: FloatingPanelController, withVelocity velocity: CGPoint, targetState: UnsafeMutablePointer<FloatingPanelState>) {
switch targetState.pointee {
case .full:
// do something...
case .half:
if aCondition {
targetState.pointee = .tip
}
default:
break
}
}
floatingPanel(_:behaviorFor:)
FloatingPanelController.behavior directly.floatingPanel(_:shouldRecognizeSimultaneouslyWith:)
FloatingPanelController.panGestureRecognizer.delegateProxy.FloatingPanelLayoutposition property is added to determine a panel position.initialPosition is now initialState.supportedPositions and insetFor(position:) are replaced with anchors property.backdropAlphaFor(position:) is now backdropAlpha(for:).class SearchPanelPadLayout: FloatingPanelLayout {
let position: FloatingPanelPosition = .top
let initialState: FloatingPanelState = .tip
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
...
]
}
func backdropAlpha(for state: FloatingPanelState) -> CGFloat {
return 0.3
}
...
}
FloatingPanelLayoutAnchoring classesThe following objects adopting FloatingPanelLayoutAnchoring protocol are added to configure the flexible and explicit layout.
FloatingPanelLayoutAnchorThis class is used to specify a panel layout using insets from a rectangle area of the superview or safe area.
FloatingPanelFullScreenLayout is replaced with anchors using .superview reference guide.FloatingPanelLayoutAnchor(fractionalInset:edge:referenceGuide:) lets you lay out a panel at a relative position in a reference rectangle area.// Before:
class MyPanelLayout: FloatingPanelLayout {
var initialPosition: FloatingPanelPosition {
return .half
}
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
switch position {
case .full: return 16.0
case .half: return 262.0
case .tip: return 44.0
case .hidden: return nil
}
}
}
// After:
class MyPanelLayout: FloatingPanelLayout {
var position: FloatingPanelPosition = .bottom
var initialState: FloatingPanelState { .half }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelLayoutAnchor(absoluteInset: 16.0, edge: .top, referenceGuide: .safeArea),
.half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom, referenceGuide: .superview),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
]
}
}
FloatingPanelIntrinsicLayoutAnchorThis class is used to specify a panel layout using offsets from the intrinsic size layout.
FloatingPanelIntrinsicLayout.// Before:
class MyPanelIntrinsicLayout: FloatingPanelIntrinsicLayout {
var initialPosition: FloatingPanelPosition {
return .half
}
func insetFor(position: FloatingPanelPosition) -> CGFloat? {
switch position {
case .full: return 16.0
case .half: return 262.0
case .tip: return 44.0
case .hidden: return nil
}
}
}
// After:
class MyPanelIntrinsicLayout: FloatingPanelLayout {
var position: FloatingPanelPosition = .bottom
var initialState: FloatingPanelState { .full }
var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] {
return [
.full: FloatingPanelIntrinsicLayoutAnchor(absoluteOffset: 16.0, referenceGuide: .safeArea),
.half: FloatingPanelIntrinsicLayoutAnchor(fractionalOffset: 0.5, referenceGuide: .safeArea),
.tip: FloatingPanelLayoutAnchor(absoluteInset: 44.0, edge: .bottom, referenceGuide: .safeArea)
]
}
}
.topInteractionBuffer and .bottomInteractionBuffer.
floatingPanelDidMove(_:) delegate method as below.func floatingPanelDidMove(_ fpc: FloatingPanelController) {
if fpc.isAttracting == false {
let loc = fpc.surfaceLocation
let minY = fpc.surfaceLocation(for: .full).y - 6.0
let maxY = fpc.surfaceLocation(for: .tip).y + 6.0
fpc.surfaceLocation = CGPoint(x: loc.x, y: min(max(loc.y, minY), maxY))
}
}
FloatingPanelBehavior.springDecelerationRate and .springResponseTime properties are added to control the new spring effect of Numeric springing.addAnimator(_:to:), removeAnimator(_:from:)
floatingPanel(_:animatorForPresentingTo:) and floatingPanel(_:animatorForDismissingWith:) of FloatingPanelControllerDelegate because they are used for view transitions.interactionAnimator(_:to:with:), moveAnimator(_:from:to:)
removalVelocity, removalProgress
floatingPanel(_:shouldRemoveAt:with:) of FloatingPanelControllerDelegateremovalInteractionAnimator(_:with:)
floatingPanel(_:animatorForDismissingWith:) of FloatingPanelControllerDelegate.SurfaceViewSurfaceAppearance class and SurfaceView.appearance property are added to specify the rounding corners, shadows and background color.
SurfaceView.appearance property avoids Ambiguous use of 'cornerRadius' error, for instance.SurfaceAppearance enables to apply layered box shadows into a surface to materialize it.// Before:
fpc.surfaceView.cornerRadius = 6.0
fpc.surfaceView.backgroundColor = .clear
fpc.surfaceView.shadowHidden = false
fpc.surfaceView.shadowColor = .black
fpc.surfaceView.shadowOffset = CGSize(width: 0, height: 16)
fpc.surfaceView.shadowRadius = 16.0
// After:
let appearance = SurfaceAppearance()
appearance.cornerRadius = 8.0
appearance.backgroundColor = .clear
let shadow = SurfaceAppearance.Shadow()
shadow.color = .black
shadow.offset = CGSize(width: 0, height: 16)
shadow.radius = 16
shadow.spread = 8
appearance.shadows = [shadow]
fpc.surfaceView.appearance = appearance
grabberTopPadding is now grabberHandlePadding.topGrabberBarHeight is now grabberAreaOffset.grabberHandleWidth and grabberHandleHeight are replaced with grabberHandleSize.BackdropViewBackdropView.dismissalTapGestureRecognizer.isEnabled to true.FloatingPanelPanGestureRecognizerdelegateProxy property is added to intercept the gesture recognizer delegate.func layoutPanelForPad() {
fpc.behavior = SearchPaneliPadBehavior()
fpc.panGestureRecognizer.delegateProxy = self
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
UISpringTimingParameters(decelerationRate:frequencyResponse:initialVelocity:) initializer is added.