Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8fcb50874 | |||
| 9c45c31190 | |||
| 895790f697 | |||
| 43c7e8c2a0 | |||
| 904a66115c | |||
| d0932e8e37 | |||
| 22009013eb |
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FloatingPanel"
|
||||
s.version = "2.3.1"
|
||||
s.version = "2.4.0"
|
||||
s.summary = "FloatingPanel is a clean and easy-to-use UI component of a floating panel interface."
|
||||
s.description = <<-DESC
|
||||
FloatingPanel is a clean and easy-to-use UI component for a new interface introduced in Apple Maps, Shortcuts and Stocks app.
|
||||
|
||||
@@ -55,6 +55,7 @@ The new interface displays the related contents and utilities in parallel as a u
|
||||
- [Create an additional floating panel for a detail](#create-an-additional-floating-panel-for-a-detail)
|
||||
- [Move a position with an animation](#move-a-position-with-an-animation)
|
||||
- [Work your contents together with a floating panel behavior](#work-your-contents-together-with-a-floating-panel-behavior)
|
||||
- [Enabling the tap-to-dismiss action of the backdrop view](#enabling-the-tap-to-dismiss-action-of-the-backdrop-view)
|
||||
- [Notes](#notes)
|
||||
- ['Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller](#show-or-show-detail-segues-from-floatingpanelcontrollers-content-view-controller)
|
||||
- [UISearchController issue](#uisearchcontroller-issue)
|
||||
@@ -564,8 +565,8 @@ override func viewDidLoad() {
|
||||
surfaceTapGesture.isEnabled = (fpc.position == .tip)
|
||||
}
|
||||
|
||||
// Enable `surfaceTapGesture` only at `tip` position
|
||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
||||
// Enable `surfaceTapGesture` only at `tip` state
|
||||
func floatingPanelDidChangeState(_ vc: FloatingPanelController) {
|
||||
surfaceTapGesture.isEnabled = (vc.position == .tip)
|
||||
}
|
||||
```
|
||||
@@ -658,6 +659,14 @@ class ViewController: UIViewController, FloatingPanelControllerDelegate {
|
||||
}
|
||||
```
|
||||
|
||||
### Enabling the tap-to-dismiss action of the backdrop view
|
||||
|
||||
The tap-to-dismiss action is disabled by default. So it needs to be enabled as below.
|
||||
|
||||
```swift
|
||||
fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = true
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### 'Show' or 'Show Detail' Segues from `FloatingPanelController`'s content view controller
|
||||
|
||||
+15
-19
@@ -26,10 +26,12 @@ import UIKit
|
||||
@objc(floatingPanel:animatorForDismissingWithVelocity:) optional
|
||||
func floatingPanel(_ fpc: FloatingPanelController, animatorForDismissingWith velocity: CGVector) -> UIViewPropertyAnimator
|
||||
|
||||
/// Called when a panel has changed to a new position. Can be called inside an animation block, so any
|
||||
/// view properties set inside this function will be automatically animated alongside a panel.
|
||||
/// Called when a panel has changed to a new state.
|
||||
///
|
||||
/// This can be called inside an animation block for presenting, dismissing a panel or moving a panel with your
|
||||
/// animation. So any view properties set inside this function will be automatically animated alongside a panel.
|
||||
@objc optional
|
||||
func floatingPanelDidChangePosition(_ fpc: FloatingPanelController)
|
||||
func floatingPanelDidChangeState(_ fpc: FloatingPanelController)
|
||||
|
||||
/// Asks the delegate if dragging should begin by the pan gesture recognizer.
|
||||
@objc optional
|
||||
@@ -396,20 +398,8 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
private func activateLayout(forceLayout: Bool = false) {
|
||||
floatingPanel.layoutAdapter.prepareLayout()
|
||||
|
||||
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
|
||||
var contentOffset: CGPoint?
|
||||
if contentInsetAdjustmentBehavior == .always {
|
||||
contentOffset = trackingScrollView?.contentOffset
|
||||
}
|
||||
|
||||
floatingPanel.layoutAdapter.updateStaticConstraint()
|
||||
floatingPanel.layoutAdapter.activateLayout(for: floatingPanel.state, forceLayout: forceLayout)
|
||||
|
||||
if let contentOffset = contentOffset {
|
||||
trackingScrollView?.contentOffset = contentOffset
|
||||
}
|
||||
floatingPanel.activateLayout(forceLayout: forceLayout,
|
||||
contentInsetAdjustmentBehavior: contentInsetAdjustmentBehavior)
|
||||
}
|
||||
|
||||
func remove() {
|
||||
@@ -427,6 +417,9 @@ open class FloatingPanelController: UIViewController {
|
||||
// MARK: - Container view controller interface
|
||||
|
||||
/// Shows the surface view at the initial position defined by the current layout
|
||||
/// - Parameters:
|
||||
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||
/// - completion: The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
|
||||
@objc(show:completion:)
|
||||
public func show(animated: Bool = false, completion: (() -> Void)? = nil) {
|
||||
// Must apply the current layout here
|
||||
@@ -468,8 +461,9 @@ open class FloatingPanelController: UIViewController {
|
||||
/// - parent: A parent view controller object that displays FloatingPanelController's view. A container view controller object isn't applicable.
|
||||
/// - viewIndex: Insert the surface view managed by the controller below the specified view index. By default, the surface view will be added to the end of the parent list of subviews.
|
||||
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||
@objc(addPanelToParent:at:animated:)
|
||||
public func addPanel(toParent parent: UIViewController, at viewIndex: Int = -1, animated: Bool = false) {
|
||||
/// - completion: The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
|
||||
@objc(addPanelToParent:at:animated:completion:)
|
||||
public func addPanel(toParent parent: UIViewController, at viewIndex: Int = -1, animated: Bool = false, completion: (() -> Void)? = nil) {
|
||||
guard self.parent == nil else {
|
||||
log.warning("Already added to a parent(\(parent))")
|
||||
return
|
||||
@@ -500,6 +494,7 @@ open class FloatingPanelController: UIViewController {
|
||||
show(animated: animated) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.didMove(toParent: parent)
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,6 +526,7 @@ open class FloatingPanelController: UIViewController {
|
||||
}
|
||||
|
||||
/// Moves the position to the specified position.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - to: Pass a FloatingPanelPosition value to move the surface view to the position.
|
||||
/// - animated: Pass true to animate the presentation; otherwise, pass false.
|
||||
|
||||
+51
-27
@@ -24,7 +24,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
didSet {
|
||||
log.debug("state changed: \(oldValue) -> \(state)")
|
||||
if let vc = ownerVC {
|
||||
vc.delegate?.floatingPanelDidChangePosition?(vc)
|
||||
vc.delegate?.floatingPanelDidChangeState?(vc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
if state != layoutAdapter.edgeMostState {
|
||||
if state != layoutAdapter.mostExpandedState {
|
||||
lockScrollView()
|
||||
}
|
||||
tearDownActiveInteraction()
|
||||
@@ -117,7 +117,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
if animated {
|
||||
let updateScrollView: () -> Void = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if self.state == self.layoutAdapter.edgeMostState, abs(self.layoutAdapter.offsetFromEdgeMost) <= 1.0 {
|
||||
if self.state == self.layoutAdapter.mostExpandedState, abs(self.layoutAdapter.offsetFromMostExpandedAnchor) <= 1.0 {
|
||||
self.unlockScrollView()
|
||||
} else {
|
||||
self.lockScrollView()
|
||||
@@ -173,7 +173,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
} else {
|
||||
self.state = to
|
||||
self.updateLayout(to: to)
|
||||
if self.state == self.layoutAdapter.edgeMostState {
|
||||
if self.state == self.layoutAdapter.mostExpandedState {
|
||||
self.unlockScrollView()
|
||||
} else {
|
||||
self.lockScrollView()
|
||||
@@ -186,6 +186,30 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
// MARK: - Layout update
|
||||
|
||||
func activateLayout(forceLayout: Bool = false,
|
||||
contentInsetAdjustmentBehavior: FloatingPanelController.ContentInsetAdjustmentBehavior) {
|
||||
layoutAdapter.prepareLayout()
|
||||
|
||||
// preserve the current content offset if contentInsetAdjustmentBehavior is `.always`
|
||||
var contentOffset: CGPoint?
|
||||
if contentInsetAdjustmentBehavior == .always {
|
||||
contentOffset = scrollView?.contentOffset
|
||||
}
|
||||
|
||||
layoutAdapter.updateStaticConstraint()
|
||||
layoutAdapter.activateLayout(for: state, forceLayout: true)
|
||||
|
||||
// Update the backdrop alpha only when called in `Controller.show(animated:completion:)`
|
||||
// Because that prevents a backdrop flicking just before presenting a panel(#466).
|
||||
if forceLayout {
|
||||
backdropView.alpha = getBackdropAlpha(for: state)
|
||||
}
|
||||
|
||||
if let contentOffset = contentOffset {
|
||||
scrollView?.contentOffset = contentOffset
|
||||
}
|
||||
}
|
||||
|
||||
private func updateLayout(to target: FloatingPanelState) {
|
||||
self.layoutAdapter.activateLayout(for: target, forceLayout: true)
|
||||
self.backdropView.alpha = self.getBackdropAlpha(for: target)
|
||||
@@ -201,8 +225,8 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
let segment = layoutAdapter.segment(at: cur, forward: forwardY)
|
||||
|
||||
let lowerState = segment.lower ?? layoutAdapter.edgeMostState
|
||||
let upperState = segment.upper ?? layoutAdapter.edgeLeastState
|
||||
let lowerState = segment.lower ?? layoutAdapter.mostExpandedState
|
||||
let upperState = segment.upper ?? layoutAdapter.leastExpandedState
|
||||
|
||||
let preState = forwardY ? lowerState : upperState
|
||||
let nextState = forwardY ? upperState : lowerState
|
||||
@@ -353,7 +377,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
let velocity = value(of: panGesture.velocity(in: panGesture.view))
|
||||
let location = panGesture.location(in: surfaceView)
|
||||
|
||||
let belowEdgeMost = 0 > layoutAdapter.offsetFromEdgeMost + (1.0 / surfaceView.fp_displayScale)
|
||||
let belowEdgeMost = 0 > layoutAdapter.offsetFromMostExpandedAnchor + (1.0 / surfaceView.fp_displayScale)
|
||||
|
||||
log.debug("""
|
||||
scroll gesture(\(state):\(panGesture.state)) -- \
|
||||
@@ -367,7 +391,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
if belowEdgeMost {
|
||||
// Scroll offset pinning
|
||||
if state == layoutAdapter.edgeMostState {
|
||||
if state == layoutAdapter.mostExpandedState {
|
||||
if interactionInProgress {
|
||||
log.debug("settle offset --", value(of: initialScrollOffset))
|
||||
stopScrolling(at: initialScrollOffset)
|
||||
@@ -385,7 +409,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
if interactionInProgress {
|
||||
lockScrollView()
|
||||
} else {
|
||||
if state == layoutAdapter.edgeMostState, self.transitionAnimator == nil {
|
||||
if state == layoutAdapter.mostExpandedState, self.transitionAnimator == nil {
|
||||
switch layoutAdapter.position {
|
||||
case .top, .left:
|
||||
if offsetDiff < 0 && velocity > 0 {
|
||||
@@ -413,14 +437,14 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
}
|
||||
if state == layoutAdapter.edgeMostState {
|
||||
if state == layoutAdapter.mostExpandedState {
|
||||
// Adjust a small gap of the scroll offset just after swiping down starts in the grabber area.
|
||||
if grabberAreaFrame.contains(location), grabberAreaFrame.contains(initialLocation) {
|
||||
stopScrolling(at: initialScrollOffset)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if state == layoutAdapter.edgeMostState {
|
||||
if state == layoutAdapter.mostExpandedState {
|
||||
switch layoutAdapter.position {
|
||||
case .top, .left:
|
||||
if velocity < 0, !allowScrollPanGesture(for: scrollView) {
|
||||
@@ -506,15 +530,15 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
endAttraction(false)
|
||||
}
|
||||
if let animator = self.transitionAnimator {
|
||||
guard 0 >= layoutAdapter.offsetFromEdgeMost else { return }
|
||||
guard 0 >= layoutAdapter.offsetFromMostExpandedAnchor else { return }
|
||||
log.debug("a panel animation(interruptible: \(animator.isInterruptible)) interrupted!!!")
|
||||
if animator.isInterruptible {
|
||||
animator.stopAnimation(false)
|
||||
// A user can stop a panel at the nearest Y of a target position so this fine-tunes
|
||||
// the a small gap between the presentation layer frame and model layer frame
|
||||
// to unlock scroll view properly at finishAnimation(at:)
|
||||
if abs(layoutAdapter.offsetFromEdgeMost) <= 1.0 {
|
||||
layoutAdapter.surfaceLocation = layoutAdapter.surfaceLocation(for: layoutAdapter.edgeMostState)
|
||||
if abs(layoutAdapter.offsetFromMostExpandedAnchor) <= 1.0 {
|
||||
layoutAdapter.surfaceLocation = layoutAdapter.surfaceLocation(for: layoutAdapter.mostExpandedState)
|
||||
}
|
||||
animator.finishAnimation(at: .current)
|
||||
} else {
|
||||
@@ -540,9 +564,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
guard
|
||||
state == layoutAdapter.edgeMostState, // When not top most(i.e. .full), don't scroll.
|
||||
state == layoutAdapter.mostExpandedState, // When not top most(i.e. .full), don't scroll.
|
||||
interactionInProgress == false, // When interaction already in progress, don't scroll.
|
||||
0 == layoutAdapter.offsetFromEdgeMost
|
||||
0 == layoutAdapter.offsetFromMostExpandedAnchor
|
||||
else {
|
||||
return false
|
||||
}
|
||||
@@ -602,7 +626,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
log.debug("panningBegan -- location = \(value(of: location))")
|
||||
|
||||
guard let scrollView = scrollView else { return }
|
||||
if state == layoutAdapter.edgeMostState {
|
||||
if state == layoutAdapter.mostExpandedState {
|
||||
if grabberAreaFrame.contains(location) {
|
||||
initialScrollOffset = scrollView.contentOffset
|
||||
}
|
||||
@@ -669,7 +693,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
stopScrollDeceleration = (0 > layoutAdapter.offsetFromEdgeMost + (1.0 / surfaceView.fp_displayScale)) // Projecting the dragging to the scroll dragging or not
|
||||
stopScrollDeceleration = (0 > layoutAdapter.offsetFromMostExpandedAnchor + (1.0 / surfaceView.fp_displayScale)) // Projecting the dragging to the scroll dragging or not
|
||||
if stopScrollDeceleration {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@@ -719,14 +743,14 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
// Workaround: Disable a tracking scroll to prevent bouncing a scroll content in a panel animating
|
||||
let isScrollEnabled = scrollView?.isScrollEnabled
|
||||
if let scrollView = scrollView, targetPosition != layoutAdapter.edgeMostState {
|
||||
if let scrollView = scrollView, targetPosition != layoutAdapter.mostExpandedState {
|
||||
scrollView.isScrollEnabled = false
|
||||
}
|
||||
|
||||
startAttraction(to: targetPosition, with: velocity)
|
||||
|
||||
// Workaround: Reset `self.scrollView.isScrollEnabled`
|
||||
if let scrollView = scrollView, targetPosition != layoutAdapter.edgeMostState,
|
||||
if let scrollView = scrollView, targetPosition != layoutAdapter.mostExpandedState,
|
||||
let isScrollEnabled = isScrollEnabled {
|
||||
scrollView.isScrollEnabled = isScrollEnabled
|
||||
}
|
||||
@@ -760,7 +784,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
var offset: CGPoint = .zero
|
||||
|
||||
initialSurfaceLocation = layoutAdapter.surfaceLocation
|
||||
if state == layoutAdapter.edgeMostState, let scrollView = scrollView {
|
||||
if state == layoutAdapter.mostExpandedState, let scrollView = scrollView {
|
||||
if grabberAreaFrame.contains(location) {
|
||||
initialScrollOffset = scrollView.contentOffset
|
||||
} else {
|
||||
@@ -805,7 +829,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
interactionInProgress = false
|
||||
|
||||
// Prevent to keep a scroll view indicator visible at the half/tip position
|
||||
if targetPosition != layoutAdapter.edgeMostState {
|
||||
if targetPosition != layoutAdapter.mostExpandedState {
|
||||
lockScrollView()
|
||||
}
|
||||
|
||||
@@ -882,9 +906,9 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
log.debug("""
|
||||
finishAnimation -- state = \(state) \
|
||||
surface location = \(layoutAdapter.surfaceLocation) \
|
||||
edge most position = \(layoutAdapter.surfaceLocation(for: layoutAdapter.edgeMostState))
|
||||
edge most position = \(layoutAdapter.surfaceLocation(for: layoutAdapter.mostExpandedState))
|
||||
""")
|
||||
if finished, state == layoutAdapter.edgeMostState, abs(layoutAdapter.offsetFromEdgeMost) <= 1.0 {
|
||||
if finished, state == layoutAdapter.mostExpandedState, abs(layoutAdapter.offsetFromMostExpandedAnchor) <= 1.0 {
|
||||
unlockScrollView()
|
||||
}
|
||||
}
|
||||
@@ -911,7 +935,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
func targetPosition(from currentY: CGFloat, with velocity: CGFloat) -> (FloatingPanelState) {
|
||||
log.debug("targetPosition -- currentY = \(currentY), velocity = \(velocity)")
|
||||
|
||||
let sortedPositions = layoutAdapter.sortedDirectionalStates
|
||||
let sortedPositions = layoutAdapter.sortedAnchorStatesByCoordinate
|
||||
|
||||
guard sortedPositions.count > 1 else {
|
||||
return state
|
||||
@@ -919,7 +943,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
// Projection
|
||||
let decelerationRate = behaviorAdapter.momentumProjectionRate
|
||||
let baseY = abs(layoutAdapter.position(for: layoutAdapter.edgeLeastState) - layoutAdapter.position(for: layoutAdapter.edgeMostState))
|
||||
let baseY = abs(layoutAdapter.position(for: layoutAdapter.leastExpandedState) - layoutAdapter.position(for: layoutAdapter.mostExpandedState))
|
||||
let vecY = velocity / baseY
|
||||
var pY = project(initialVelocity: vecY, decelerationRate: decelerationRate) * baseY + currentY
|
||||
|
||||
@@ -1015,7 +1039,7 @@ class Core: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
private func allowScrollPanGesture(for scrollView: UIScrollView) -> Bool {
|
||||
guard state == layoutAdapter.edgeMostState else { return false }
|
||||
guard state == layoutAdapter.mostExpandedState else { return false }
|
||||
var offsetY: CGFloat = 0
|
||||
switch layoutAdapter.position {
|
||||
case .top, .left:
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.3.1</string>
|
||||
<string>2.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
|
||||
+31
-35
@@ -92,10 +92,16 @@ class LayoutAdapter {
|
||||
|
||||
private var staticConstraint: NSLayoutConstraint?
|
||||
|
||||
private var activeStates: Set<FloatingPanelState> {
|
||||
private var anchorStates: Set<FloatingPanelState> {
|
||||
return Set(layout.anchors.keys)
|
||||
}
|
||||
|
||||
private var sortedAnchorStates: [FloatingPanelState] {
|
||||
return anchorStates.sorted(by: {
|
||||
return $0.order < $1.order
|
||||
})
|
||||
}
|
||||
|
||||
var initialState: FloatingPanelState {
|
||||
layout.initialState
|
||||
}
|
||||
@@ -104,18 +110,12 @@ class LayoutAdapter {
|
||||
layout.position
|
||||
}
|
||||
|
||||
var orderedStates: [FloatingPanelState] {
|
||||
return activeStates.sorted(by: {
|
||||
return $0.order < $1.order
|
||||
})
|
||||
}
|
||||
|
||||
var validStates: Set<FloatingPanelState> {
|
||||
return activeStates.union([.hidden])
|
||||
return anchorStates.union([.hidden])
|
||||
}
|
||||
|
||||
var sortedDirectionalStates: [FloatingPanelState] {
|
||||
return activeStates.sorted(by: {
|
||||
var sortedAnchorStatesByCoordinate: [FloatingPanelState] {
|
||||
return anchorStates.sorted(by: {
|
||||
switch position {
|
||||
case .top, .left:
|
||||
return $0.order < $1.order
|
||||
@@ -125,30 +125,26 @@ class LayoutAdapter {
|
||||
})
|
||||
}
|
||||
|
||||
private var directionalLeastState: FloatingPanelState {
|
||||
return sortedDirectionalStates.first ?? .hidden
|
||||
private var leastCoordinateState: FloatingPanelState {
|
||||
return sortedAnchorStatesByCoordinate.first ?? .hidden
|
||||
}
|
||||
|
||||
private var directionalMostState: FloatingPanelState {
|
||||
return sortedDirectionalStates.last ?? .hidden
|
||||
private var mostCoordinateState: FloatingPanelState {
|
||||
return sortedAnchorStatesByCoordinate.last ?? .hidden
|
||||
}
|
||||
|
||||
var edgeLeastState: FloatingPanelState {
|
||||
if orderedStates.count == 1 {
|
||||
var leastExpandedState: FloatingPanelState {
|
||||
if sortedAnchorStates.count == 1 {
|
||||
return .hidden
|
||||
}
|
||||
return orderedStates.first ?? .hidden
|
||||
return sortedAnchorStates.first ?? .hidden
|
||||
}
|
||||
|
||||
var edgeMostState: FloatingPanelState {
|
||||
if orderedStates.count == 1 {
|
||||
return orderedStates[0]
|
||||
var mostExpandedState: FloatingPanelState {
|
||||
if sortedAnchorStates.count == 1 {
|
||||
return sortedAnchorStates[0]
|
||||
}
|
||||
return orderedStates.last ?? .hidden
|
||||
}
|
||||
|
||||
var edgeMostY: CGFloat {
|
||||
return position(for: edgeMostState)
|
||||
return sortedAnchorStates.last ?? .hidden
|
||||
}
|
||||
|
||||
var adjustedContentInsets: UIEdgeInsets {
|
||||
@@ -265,12 +261,12 @@ class LayoutAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
var offsetFromEdgeMost: CGFloat {
|
||||
var offsetFromMostExpandedAnchor: CGFloat {
|
||||
switch position {
|
||||
case .top, .left:
|
||||
return edgePosition(surfaceView.presentationFrame) - position(for: directionalMostState)
|
||||
return edgePosition(surfaceView.presentationFrame) - position(for: mostExpandedState)
|
||||
case .bottom, .right:
|
||||
return position(for: directionalLeastState) - edgePosition(surfaceView.presentationFrame)
|
||||
return position(for: mostExpandedState) - edgePosition(surfaceView.presentationFrame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,7 +637,7 @@ class LayoutAdapter {
|
||||
return
|
||||
}
|
||||
|
||||
let anchor = layout.anchors[self.edgeMostState]!
|
||||
let anchor = layout.anchors[self.mostExpandedState]!
|
||||
let surfaceAnchor = position.mainDimensionAnchor(surfaceView)
|
||||
switch anchor {
|
||||
case let anchor as FloatingPanelIntrinsicLayoutAnchor:
|
||||
@@ -662,11 +658,11 @@ class LayoutAdapter {
|
||||
default:
|
||||
switch position {
|
||||
case .top, .left:
|
||||
staticConstraint = surfaceAnchor.constraint(equalToConstant: position(for: self.directionalMostState))
|
||||
staticConstraint = surfaceAnchor.constraint(equalToConstant: position(for: self.mostCoordinateState))
|
||||
case .bottom, .right:
|
||||
let rootViewAnchor = position.mainDimensionAnchor(vc.view)
|
||||
staticConstraint = rootViewAnchor.constraint(equalTo: surfaceAnchor,
|
||||
constant: position(for: self.directionalLeastState))
|
||||
constant: position(for: self.leastCoordinateState))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,8 +683,8 @@ class LayoutAdapter {
|
||||
log.debug("update surface location = \(surfaceLocation)")
|
||||
}
|
||||
|
||||
let minConst: CGFloat = position(for: directionalLeastState)
|
||||
let maxConst: CGFloat = position(for: directionalMostState)
|
||||
let minConst: CGFloat = position(for: leastCoordinateState)
|
||||
let maxConst: CGFloat = position(for: mostCoordinateState)
|
||||
|
||||
var const = initialConst + diff
|
||||
|
||||
@@ -779,7 +775,7 @@ class LayoutAdapter {
|
||||
|
||||
fileprivate func checkLayout() {
|
||||
// Verify layout configurations
|
||||
assert(activeStates.count > 0)
|
||||
assert(anchorStates.count > 0)
|
||||
assert(validStates.contains(layout.initialState),
|
||||
"Does not include an initial state (\(layout.initialState)) in (\(validStates))")
|
||||
/* This assertion does not work in a device rotating
|
||||
@@ -799,7 +795,7 @@ extension LayoutAdapter {
|
||||
/// |-------|-------|===o===| |-------|===o===|-------|
|
||||
/// pos: o/x, segment: =
|
||||
|
||||
let sortedStates = sortedDirectionalStates
|
||||
let sortedStates = sortedAnchorStatesByCoordinate
|
||||
|
||||
let upperIndex: Int?
|
||||
if forward {
|
||||
|
||||
@@ -13,8 +13,8 @@ class LayoutTests: XCTestCase {
|
||||
override func tearDown() {}
|
||||
|
||||
func test_layoutAdapter_topAndBottomMostState() {
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .full)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .tip)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .full)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
|
||||
|
||||
class FloatingPanelLayoutWithHidden: FloatingPanelLayout {
|
||||
var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] {
|
||||
@@ -38,12 +38,12 @@ class LayoutTests: XCTestCase {
|
||||
let position: FloatingPanelPosition = .bottom
|
||||
}
|
||||
fpc.layout = FloatingPanelLayoutWithHidden()
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .full)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .hidden)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .full)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .hidden)
|
||||
|
||||
fpc.layout = FloatingPanelLayout2Positions()
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeMostState, .half)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.edgeLeastState, .tip)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.mostExpandedState, .half)
|
||||
XCTAssertEqual(fpc.floatingPanel.layoutAdapter.leastExpandedState, .tip)
|
||||
}
|
||||
|
||||
func test_layoutSegment_3position() {
|
||||
|
||||
@@ -18,7 +18,7 @@ extension FloatingPanelController {
|
||||
class FloatingPanelTestDelegate: FloatingPanelControllerDelegate {
|
||||
var position: FloatingPanelState = .hidden
|
||||
var didMoveCallback: ((FloatingPanelController) -> Void)?
|
||||
func floatingPanelDidChangePosition(_ vc: FloatingPanelController) {
|
||||
func floatingPanelDidChangeState(_ vc: FloatingPanelController) {
|
||||
position = vc.state
|
||||
}
|
||||
func floatingPanelDidMove(_ vc: FloatingPanelController) {
|
||||
|
||||
Reference in New Issue
Block a user