Merge branch 'master' into fix-hidden-position

This commit is contained in:
Shin Yamamoto
2019-07-09 22:03:18 +09:00
+49 -28
View File
@@ -10,7 +10,7 @@ import UIKit.UIGestureRecognizerSubclass // For Xcode 9.4.1
///
class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
// MUST be a weak reference to prevent UI freeze on the presentation modally
weak var viewcontroller: FloatingPanelController!
weak var viewcontroller: FloatingPanelController?
let surfaceView: FloatingPanelSurfaceView
let backdropView: FloatingPanelBackdropView
@@ -25,7 +25,11 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
private(set) var state: FloatingPanelPosition = .hidden {
didSet { viewcontroller.delegate?.floatingPanelDidChangePosition(viewcontroller) }
didSet {
if let vc = viewcontroller {
vc.delegate?.floatingPanelDidChangePosition(vc)
}
}
}
private var isBottomState: Bool {
@@ -90,6 +94,10 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
private func move(from: FloatingPanelPosition, to: FloatingPanelPosition, animated: Bool, completion: (() -> Void)? = nil) {
assert(layoutAdapter.isValid(to), "Can't move to '\(to)' position because it's not valid in the layout")
guard let vc = viewcontroller else {
completion?()
return
}
if state != layoutAdapter.topMostState {
lockScrollView()
}
@@ -99,11 +107,11 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let animator: UIViewPropertyAnimator
switch (from, to) {
case (.hidden, let to):
animator = behavior.addAnimator(self.viewcontroller, to: to)
animator = behavior.addAnimator(vc, to: to)
case (let from, .hidden):
animator = behavior.removeAnimator(self.viewcontroller, from: from)
animator = behavior.removeAnimator(vc, from: from)
case (let from, let to):
animator = behavior.moveAnimator(self.viewcontroller, from: from, to: to)
animator = behavior.moveAnimator(vc, from: from, to: to)
}
animator.addAnimations { [weak self] in
@@ -172,7 +180,8 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
/* log.debug("shouldRecognizeSimultaneouslyWith", otherGestureRecognizer) */
if viewcontroller.delegate?.floatingPanel(viewcontroller, shouldRecognizeSimultaneouslyWith: otherGestureRecognizer) ?? false {
if let vc = viewcontroller,
vc.delegate?.floatingPanel(vc, shouldRecognizeSimultaneouslyWith: otherGestureRecognizer) ?? false {
return true
}
@@ -219,11 +228,11 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
}
if viewcontroller.delegate?.floatingPanel(viewcontroller, shouldRecognizeSimultaneouslyWith: otherGestureRecognizer) ?? false {
if let vc = viewcontroller,
vc.delegate?.floatingPanel(vc, shouldRecognizeSimultaneouslyWith: otherGestureRecognizer) ?? false {
return false
}
switch otherGestureRecognizer {
case is UIPanGestureRecognizer,
is UISwipeGestureRecognizer,
@@ -321,7 +330,8 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
if interactionInProgress == false,
viewcontroller.delegate?.floatingPanelShouldBeginDragging(viewcontroller) == false {
let vc = viewcontroller,
vc.delegate?.floatingPanelShouldBeginDragging(vc) == false {
return
}
@@ -435,7 +445,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let didMove = (preY != currentY)
guard didMove else { return }
viewcontroller.delegate?.floatingPanelDidMove(viewcontroller)
if let vc = viewcontroller {
vc.delegate?.floatingPanelDidMove(vc)
}
}
private func allowsTopBuffer(for translationY: CGFloat) -> Bool {
@@ -453,11 +465,12 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
// Prevent stretching a view having a constraint to SafeArea.bottom in an overflow
// from the full position because SafeArea is global in a screen.
private func preserveContentVCLayoutIfNeeded() {
guard let vc = viewcontroller else { return }
// Must include topY
if (surfaceView.frame.minY <= layoutAdapter.topY) {
if !disabledBottomAutoLayout {
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
vc.contentViewController?.view?.constraints.forEach({ (const) in
switch vc.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.disableAutoLayout()
const.isActive = false
@@ -472,8 +485,8 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
disabledBottomAutoLayout = true
} else {
if disabledBottomAutoLayout {
viewcontroller.contentViewController?.view?.constraints.forEach({ (const) in
switch viewcontroller.contentViewController?.layoutGuide.bottomAnchor {
vc.contentViewController?.view?.constraints.forEach({ (const) in
switch vc.contentViewController?.layoutGuide.bottomAnchor {
case const.firstAnchor:
(const.secondItem as? UIView)?.enableAutoLayout()
const.isActive = true
@@ -515,17 +528,18 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let velocityVector = (distance != 0) ? CGVector(dx: 0,
dy: min(abs(velocity.y)/distance, behavior.removalVelocity)) : .zero
if shouldStartRemovalAnimation(with: velocityVector) {
viewcontroller.delegate?.floatingPanelDidEndDraggingToRemove(viewcontroller, withVelocity: velocity)
self.startRemovalAnimation(with: velocityVector) { [weak self] in
if shouldStartRemovalAnimation(with: velocityVector), let vc = viewcontroller {
vc.delegate?.floatingPanelDidEndDraggingToRemove(vc, withVelocity: velocity)
startRemovalAnimation(vc, with: velocityVector) { [weak self] in
self?.finishRemovalAnimation()
}
return
}
}
viewcontroller.delegate?.floatingPanelDidEndDragging(viewcontroller, withVelocity: velocity, targetPosition: targetPosition)
if let vc = viewcontroller {
vc.delegate?.floatingPanelDidEndDragging(vc, withVelocity: velocity, targetPosition: targetPosition)
}
// Workaround: Disable a tracking scroll to prevent bouncing a scroll content in a panel animating
let isScrollEnabled = scrollView?.isScrollEnabled
@@ -558,8 +572,8 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
return true
}
private func startRemovalAnimation(with velocityVector: CGVector, completion: (() -> Void)?) {
let animator = self.behavior.removalInteractionAnimator(self.viewcontroller, with: velocityVector)
private func startRemovalAnimation(_ vc: FloatingPanelController, with velocityVector: CGVector, completion: (() -> Void)?) {
let animator = behavior.removalInteractionAnimator(vc, with: velocityVector)
animator.addAnimations { [weak self] in
self?.updateLayout(to: .hidden)
@@ -598,7 +612,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
initialTranslationY = translation.y
viewcontroller.delegate?.floatingPanelWillBeginDragging(viewcontroller)
if let vc = viewcontroller {
vc.delegate?.floatingPanelWillBeginDragging(vc)
}
layoutAdapter.startInteraction(at: state)
@@ -630,12 +646,14 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
private func startAnimation(to targetPosition: FloatingPanelPosition, at distance: CGFloat, with velocity: CGPoint) {
log.debug("startAnimation to \(targetPosition) -- distance = \(distance), velocity = \(velocity.y)")
guard let vc = viewcontroller else { return }
isDecelerating = true
viewcontroller.delegate?.floatingPanelWillBeginDecelerating(viewcontroller)
vc.delegate?.floatingPanelWillBeginDecelerating(vc)
let velocityVector = (distance != 0) ? CGVector(dx: 0, dy: min(abs(velocity.y)/distance, 30.0)) : .zero
let animator = behavior.interactionAnimator(self.viewcontroller, to: targetPosition, with: velocityVector)
let animator = behavior.interactionAnimator(vc, to: targetPosition, with: velocityVector)
animator.addAnimations { [weak self] in
guard let `self` = self else { return }
self.state = targetPosition
@@ -655,7 +673,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
self.isDecelerating = false
self.animator = nil
self.viewcontroller.delegate?.floatingPanelDidEndDecelerating(self.viewcontroller)
if let vc = viewcontroller {
vc.delegate?.floatingPanelDidEndDecelerating(vc)
}
if let scrollView = scrollView {
log.debug("finishAnimation -- scroll offset = \(scrollView.contentOffset)")
@@ -681,6 +701,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
func targetPosition(from currentY: CGFloat, with velocity: CGPoint) -> (FloatingPanelPosition) {
guard let vc = viewcontroller else { return state }
let supportedPositions = layoutAdapter.supportedPositions
guard supportedPositions.count > 1 else {
@@ -690,7 +711,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let sortedPositions = Array(supportedPositions).sorted(by: { $0.rawValue < $1.rawValue })
// Projection
let decelerationRate = behavior.momentumProjectionRate(viewcontroller)
let decelerationRate = behavior.momentumProjectionRate(vc)
let baseY = abs(layoutAdapter.positionY(for: layoutAdapter.bottomMostState) - layoutAdapter.positionY(for: layoutAdapter.topMostState))
let vecY = velocity.y / baseY
var pY = project(initialVelocity: vecY, decelerationRate: decelerationRate) * baseY + currentY
@@ -705,7 +726,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
let (lowerPos, upperPos) = (segment.lower ?? sortedPositions.first!, segment.upper ?? sortedPositions.last!)
(fromPos, toPos) = forwardY ? (lowerPos, upperPos) : (upperPos, lowerPos)
if behavior.shouldProjectMomentum(viewcontroller, for: toPos) == false {
if behavior.shouldProjectMomentum(vc, for: toPos) == false {
let segment = layoutAdapter.segument(at: currentY, forward: forwardY)
var (lowerPos, upperPos) = (segment.lower ?? sortedPositions.first!, segment.upper ?? sortedPositions.last!)
// Equate the segment out of {top,bottom} most state to the {top,bottom} most segment
@@ -727,7 +748,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
}
// Redirection
let redirectionalProgress = max(min(behavior.redirectionalProgress(viewcontroller, from: fromPos, to: toPos), 1.0), 0.0)
let redirectionalProgress = max(min(behavior.redirectionalProgress(vc, from: fromPos, to: toPos), 1.0), 0.0)
let progress = abs(pY - layoutAdapter.positionY(for: fromPos)) / abs(layoutAdapter.positionY(for: fromPos) - layoutAdapter.positionY(for: toPos))
return progress > redirectionalProgress ? toPos : fromPos
}