Skip to content

Commit

Permalink
allow dynamic full expansion behaviour (#103)
Browse files Browse the repository at this point in the history
* allow dynamic full expansion behaviour

* Apply suggestions from code review

Co-Authored-By: ilyapuchka <[email protected]>
  • Loading branch information
ilyapuchka authored and andersio committed Jun 14, 2019
1 parent 84dddd5 commit 2cca1eb
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 55 deletions.
31 changes: 20 additions & 11 deletions DrawerKit/DrawerKit/Internal API/AnimationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ extension AnimationController: UIViewControllerAnimatedTransitioning {
presentingVC: presentingVC,
presentedVC: presentedVC)

let drawerFullY = (presentedVC as? DrawerPresentable)?.fullExpansionBehaviour?.drawerFullY
?? configuration.fullExpansionBehaviour.drawerFullY

let drawerPartialH = (presentedVC as? DrawerPresentable)?.heightOfPartiallyExpandedDrawer ?? 0
let partialH = GeometryEvaluator.drawerPartialH(drawerPartialHeight: drawerPartialH,
containerViewHeight: containerViewH)
Expand All @@ -55,17 +58,23 @@ extension AnimationController: UIViewControllerAnimatedTransitioning {
let collapsedH = GeometryEvaluator.drawerPartialH(drawerPartialHeight: drawerCollapsedH,
containerViewHeight: containerViewH)

let startDrawerState = GeometryEvaluator.drawerState(for: initialFrame.origin.y,
drawerCollapsedHeight: collapsedH,
drawerPartialHeight: partialH,
containerViewHeight: containerViewH,
configuration: configuration)

let targetDrawerState = GeometryEvaluator.drawerState(for: finalFrame.origin.y,
drawerCollapsedHeight: collapsedH,
drawerPartialHeight: partialH,
containerViewHeight: containerViewH,
configuration: configuration)
let startDrawerState = GeometryEvaluator.drawerState(
for: initialFrame.origin.y,
drawerFullY: drawerFullY,
drawerCollapsedHeight: collapsedH,
drawerPartialHeight: partialH,
containerViewHeight: containerViewH,
configuration: configuration
)

let targetDrawerState = GeometryEvaluator.drawerState(
for: finalFrame.origin.y,
drawerFullY: drawerFullY,
drawerCollapsedHeight: collapsedH,
drawerPartialHeight: partialH,
containerViewHeight: containerViewH,
configuration: configuration
)

let info = AnimationSupport.makeInfo(startDrawerState: startDrawerState,
targetDrawerState: targetDrawerState,
Expand Down
25 changes: 15 additions & 10 deletions DrawerKit/DrawerKit/Internal API/GeometryEvaluator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ struct GeometryEvaluator {
return containerViewHeight - collapsedH
}

static func upperMarkY(drawerPartialHeight: CGFloat,
containerViewHeight: CGFloat,
configuration: DrawerConfiguration) -> CGFloat {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
static func upperMarkY(
drawerFullY: CGFloat,
drawerPartialHeight: CGFloat,
containerViewHeight: CGFloat,
configuration: DrawerConfiguration
) -> CGFloat {
let partialY = drawerPartialY(drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight)
return max(partialY - configuration.upperMarkGap, drawerFullY)
Expand All @@ -43,11 +45,12 @@ struct GeometryEvaluator {
}

static func clamped(_ positionY: CGFloat,
drawerFullY: CGFloat,
drawerPartialHeight: CGFloat,
containerViewHeight: CGFloat,
configuration: DrawerConfiguration) -> CGFloat {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
let upperY = upperMarkY(drawerPartialHeight: drawerPartialHeight,
let upperY = upperMarkY(drawerFullY: drawerFullY,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)
if smallerThanOrEqual(positionY, upperY) {
Expand All @@ -73,12 +76,12 @@ struct GeometryEvaluator {

extension GeometryEvaluator {
static func drawerState(for positionY: CGFloat,
drawerFullY: CGFloat,
drawerCollapsedHeight: CGFloat,
drawerPartialHeight: CGFloat,
containerViewHeight: CGFloat,
configuration: DrawerConfiguration,
clampToNearest: Bool = false) -> DrawerState {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
if smallerThanOrEqual(positionY, drawerFullY) { return .fullyExpanded }
if greaterThanOrEqual(positionY, containerViewHeight) { return .dismissed }

Expand All @@ -92,10 +95,12 @@ extension GeometryEvaluator {

if clampToNearest {
let posY = clamped(positionY,
drawerFullY: drawerFullY,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)
return drawerState(for: posY,
drawerFullY: drawerFullY,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
Expand Down Expand Up @@ -128,6 +133,7 @@ extension GeometryEvaluator {

static func nextStateFrom(currentState: DrawerState,
speedY: CGFloat,
drawerFullY: CGFloat,
drawerCollapsedHeight: CGFloat,
drawerPartialHeight: CGFloat,
containerViewHeight: CGFloat,
Expand All @@ -141,15 +147,14 @@ extension GeometryEvaluator {
let isMovingUpQuickly = isMovingUp && isMovingQuickly
let isMovingDownQuickly = isMovingDown && isMovingQuickly

let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY

let positionY = drawerPositionY(for: currentState,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
drawerFullY: drawerFullY)

let upperY = upperMarkY(drawerPartialHeight: drawerPartialHeight,
let upperY = upperMarkY(drawerFullY: drawerFullY,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,14 @@ extension PresentationController {
}

if endingPosition != .end {
self.targetDrawerState = GeometryEvaluator.drawerState(for: self.currentDrawerY,
drawerCollapsedHeight: self.drawerCollapsedHeight,
drawerPartialHeight: self.drawerPartialY,
containerViewHeight: self.containerViewHeight,
configuration: self.configuration)
self.targetDrawerState = GeometryEvaluator.drawerState(
for: self.currentDrawerY,
drawerFullY: self.drawerFullY,
drawerCollapsedHeight: self.drawerCollapsedHeight,
drawerPartialHeight: self.drawerPartialY,
containerViewHeight: self.containerViewHeight,
configuration: self.configuration
)
}

AnimationSupport.clientCleanupViews(presentingDrawerAnimationActions: presentingAnimationActions,
Expand All @@ -110,7 +113,6 @@ extension PresentationController {
}

func addCornerRadiusAnimationEnding(at endingState: DrawerState) {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
guard maximumCornerRadius != 0
&& drawerPartialY != drawerFullY
&& endingState != currentDrawerState
Expand Down Expand Up @@ -162,7 +164,6 @@ extension PresentationController {

private func positionsY(startingState: DrawerState,
endingState: DrawerState) -> (starting: CGFloat, ending: CGFloat) {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
let startingPositionY =
GeometryEvaluator.drawerPositionY(for: startingState,
drawerCollapsedHeight: drawerCollapsedHeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ extension PresentationController {

case .ended:
let drawerSpeedY = panGesture.velocity(in: view).y / containerViewHeight
let endingState = GeometryEvaluator.nextStateFrom(currentState: currentDrawerState,
speedY: drawerSpeedY,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)
let endingState = GeometryEvaluator.nextStateFrom(
currentState: currentDrawerState,
speedY: drawerSpeedY,
drawerFullY: drawerFullY,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration
)
animateTransition(to: endingState)

case .cancelled:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ extension PresentationController {
return containerViewSize.height
}

var drawerFullY: CGFloat {
return (presentedViewController as? DrawerPresentable)?.fullExpansionBehaviour?.drawerFullY
?? configuration.fullExpansionBehaviour.drawerFullY
}

var drawerPartialHeight: CGFloat {
guard let presentedVC = presentedViewController as? DrawerPresentable else { return 0 }
let drawerPartialH = presentedVC.heightOfPartiallyExpandedDrawer
Expand All @@ -38,7 +43,8 @@ extension PresentationController {
}

var upperMarkY: CGFloat {
return GeometryEvaluator.upperMarkY(drawerPartialHeight: drawerPartialHeight,
return GeometryEvaluator.upperMarkY(drawerFullY: drawerFullY,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)
}
Expand All @@ -52,14 +58,14 @@ extension PresentationController {
var currentDrawerState: DrawerState {
get {
return GeometryEvaluator.drawerState(for: currentDrawerY,
drawerFullY: drawerFullY,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
configuration: configuration)
}

set {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
currentDrawerY =
GeometryEvaluator.drawerPositionY(for: newValue,
drawerCollapsedHeight: drawerCollapsedHeight,
Expand All @@ -71,13 +77,11 @@ extension PresentationController {

var currentDrawerY: CGFloat {
get {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
let posY = presentedView?.frame.origin.y ?? drawerFullY
return min(max(posY, drawerFullY), containerViewHeight)
}

set {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
let posY = min(max(newValue, drawerFullY), containerViewHeight)
presentedView?.frame.origin.y = posY
}
Expand Down Expand Up @@ -113,16 +117,15 @@ extension PresentationController {
case .maximumAtPartialY:
return maximumCornerRadius * triangularValue(at: state)
case .alwaysShowBelowStatusBar:
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
let positionY =
GeometryEvaluator.drawerPositionY(for: state,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
drawerFullY: drawerFullY)
let positionY = GeometryEvaluator.drawerPositionY(
for: state,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
containerViewHeight: containerViewHeight,
drawerFullY: drawerFullY
)

return maximumCornerRadius * min(positionY, DrawerGeometry.statusBarHeight) / DrawerGeometry.statusBarHeight

}
}

Expand All @@ -131,7 +134,6 @@ extension PresentationController {
}

private func triangularValue(at state: DrawerState) -> CGFloat {
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
guard drawerPartialY != drawerFullY
&& drawerPartialY != containerViewHeight
&& drawerFullY != containerViewHeight
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,15 @@ final class PullToDismissManager: NSObject, UIScrollViewDelegate {
}

let drawerSpeedY = -velocity.y / presentationController.containerViewHeight
let endingState = GeometryEvaluator.nextStateFrom(currentState: presentationController.currentDrawerState,
speedY: drawerSpeedY,
drawerCollapsedHeight: presentationController.drawerCollapsedHeight,
drawerPartialHeight: presentationController.drawerPartialHeight,
containerViewHeight: presentationController.containerViewHeight,
configuration: presentationController.configuration)
let endingState = GeometryEvaluator.nextStateFrom(
currentState: presentationController.currentDrawerState,
speedY: drawerSpeedY,
drawerFullY: presentationController.drawerFullY,
drawerCollapsedHeight: presentationController.drawerCollapsedHeight,
drawerPartialHeight: presentationController.drawerPartialHeight,
containerViewHeight: presentationController.containerViewHeight,
configuration: presentationController.configuration
)

presentationController.animateTransition(
to: endingState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ extension PresentationController {
var frame: CGRect = .zero
frame.size = size(forChildContentContainer: presentedViewController,
withParentContainerSize: containerViewSize)
let drawerFullY = configuration.fullExpansionBehaviour.drawerFullY
frame.origin.y = GeometryEvaluator.drawerPositionY(for: targetDrawerState,
drawerCollapsedHeight: drawerCollapsedHeight,
drawerPartialHeight: drawerPartialHeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ public protocol DrawerPresentable: class {
/// collapsed state. If negative, its value is clamped to zero.
/// Default implementation returns 0.
var heightOfCollapsedDrawer: CGFloat { get }

/// Whether the drawer expands to cover the entire screen, the entire screen minus
/// the status bar, or the entire screen minus a custom gap. If this property
/// returns `nil` then the value of `fullExpansionBehaviour` in the `DrawerConfiguration`
/// will be used instead. The default is `nil`.
var fullExpansionBehaviour: DrawerConfiguration.FullExpansionBehaviour? { get }
}

public extension DrawerPresentable {
var heightOfCollapsedDrawer: CGFloat { return 0 }
var fullExpansionBehaviour: DrawerConfiguration.FullExpansionBehaviour? { return nil }
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ public struct DrawerConfiguration {

/// Whether the drawer expands to cover the entire screen, the entire screen minus
/// the status bar, or the entire screen minus a custom gap. The default is to cover
/// the full screen.
/// the full screen. If presented view controller conforms to the DrawerPresentable
/// the value that it returns from `fullExpansionBehaviour` will be used instead,
/// unless it is `nil`.
public var fullExpansionBehaviour: FullExpansionBehaviour

/// When `true`, the drawer is presented first in its partially expanded state.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ extension PresentedNavigationController: DrawerPresentable {
var heightOfPartiallyExpandedDrawer: CGFloat {
return (topViewController as? DrawerPresentable)?.heightOfPartiallyExpandedDrawer ?? 0.0
}

var fullExpansionBehaviour: DrawerConfiguration.FullExpansionBehaviour? {
return (topViewController as? DrawerPresentable)?.fullExpansionBehaviour
}
}

extension PresentedNavigationController: UINavigationControllerDelegate {
Expand Down
4 changes: 4 additions & 0 deletions DrawerKitDemo/DrawerKitDemo/PresentedViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ extension PresentedViewController: DrawerPresentable {

// var heightOfCollapsedDrawer: CGFloat {
// return 100
// }

// var fullExpansionBehaviour: DrawerConfiguration.FullExpansionBehaviour? {
// return .leavesCustomGap(gap: 100)
// }

var heightOfPartiallyExpandedDrawer: CGFloat {
Expand Down

0 comments on commit 2cca1eb

Please sign in to comment.