Merge pull request #231 from SCENEE/prevent-found-nil-error
Prevent 'unexpectedly found nil' fatal error
This commit is contained in:
@@ -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 {
|
||||
@@ -89,6 +93,10 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
private func move(from: FloatingPanelPosition, to: FloatingPanelPosition, animated: Bool, completion: (() -> Void)? = nil) {
|
||||
guard let vc = viewcontroller else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
if state != layoutAdapter.topMostState {
|
||||
lockScrollView()
|
||||
}
|
||||
@@ -98,11 +106,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
|
||||
@@ -167,7 +175,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
|
||||
}
|
||||
|
||||
@@ -214,11 +223,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,
|
||||
@@ -316,7 +325,8 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
if interactionInProgress == false,
|
||||
viewcontroller.delegate?.floatingPanelShouldBeginDragging(viewcontroller) == false {
|
||||
let vc = viewcontroller,
|
||||
vc.delegate?.floatingPanelShouldBeginDragging(vc) == false {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -429,7 +439,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
let didMove = (pre != surfaceView.frame.minY)
|
||||
guard didMove else { return }
|
||||
|
||||
viewcontroller.delegate?.floatingPanelDidMove(viewcontroller)
|
||||
if let vc = viewcontroller {
|
||||
vc.delegate?.floatingPanelDidMove(vc)
|
||||
}
|
||||
}
|
||||
|
||||
private func allowsTopBuffer(for translationY: CGFloat) -> Bool {
|
||||
@@ -447,11 +459,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
|
||||
@@ -466,8 +479,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
|
||||
@@ -508,17 +521,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
|
||||
@@ -551,8 +565,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)
|
||||
@@ -591,7 +605,9 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
|
||||
initialTranslationY = translation.y
|
||||
|
||||
viewcontroller.delegate?.floatingPanelWillBeginDragging(viewcontroller)
|
||||
if let vc = viewcontroller {
|
||||
vc.delegate?.floatingPanelWillBeginDragging(vc)
|
||||
}
|
||||
|
||||
layoutAdapter.startInteraction(at: state)
|
||||
|
||||
@@ -623,12 +639,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
|
||||
@@ -648,7 +666,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)")
|
||||
@@ -705,6 +725,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
private func targetPosition(with velocity: CGPoint) -> (FloatingPanelPosition) {
|
||||
guard let vc = viewcontroller else { return state }
|
||||
let currentY = surfaceView.frame.minY
|
||||
let supportedPositions = layoutAdapter.supportedPositions
|
||||
|
||||
@@ -747,7 +768,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
fatalError("Now .hidden must not be used for a user interaction")
|
||||
}
|
||||
|
||||
let redirectionalProgress = max(min(behavior.redirectionalProgress(viewcontroller, from: state, to: nextState), 1.0), 0.0)
|
||||
let redirectionalProgress = max(min(behavior.redirectionalProgress(vc, from: state, to: nextState), 1.0), 0.0)
|
||||
|
||||
let th1: CGFloat
|
||||
let th2: CGFloat
|
||||
@@ -760,7 +781,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
th2 = bottomY - (bottomY - middleY) * redirectionalProgress
|
||||
}
|
||||
|
||||
let decelerationRate = behavior.momentumProjectionRate(viewcontroller)
|
||||
let decelerationRate = behavior.momentumProjectionRate(vc)
|
||||
|
||||
let baseY = abs(bottomY - topY)
|
||||
let vecY = velocity.y / baseY
|
||||
@@ -770,7 +791,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
case ..<th1:
|
||||
switch pY {
|
||||
case bottomY...:
|
||||
return behavior.shouldProjectMomentum(viewcontroller, for: .tip) ? .tip : .half
|
||||
return behavior.shouldProjectMomentum(vc, for: .tip) ? .tip : .half
|
||||
case middleY...:
|
||||
return .half
|
||||
case topY...:
|
||||
@@ -781,7 +802,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
case ...middleY:
|
||||
switch pY {
|
||||
case bottomY...:
|
||||
return behavior.shouldProjectMomentum(viewcontroller, for: .tip) ? .tip : .half
|
||||
return behavior.shouldProjectMomentum(vc, for: .tip) ? .tip : .half
|
||||
case middleY...:
|
||||
return .half
|
||||
case topY...:
|
||||
@@ -798,7 +819,7 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
case topY...:
|
||||
return .half
|
||||
default:
|
||||
return behavior.shouldProjectMomentum(viewcontroller, for: .full) ? .full : .half
|
||||
return behavior.shouldProjectMomentum(vc, for: .full) ? .full : .half
|
||||
}
|
||||
default:
|
||||
switch pY {
|
||||
@@ -809,13 +830,14 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
case topY...:
|
||||
return .half
|
||||
default:
|
||||
return behavior.shouldProjectMomentum(viewcontroller, for: .full) ? .full : .half
|
||||
return behavior.shouldProjectMomentum(vc, for: .full) ? .full : .half
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func targetPosition(from positions: [FloatingPanelPosition], at currentY: CGFloat, velocity: CGPoint) -> FloatingPanelPosition {
|
||||
guard let vc = viewcontroller else { return state }
|
||||
assert(positions.count == 2)
|
||||
|
||||
let top = positions[0]
|
||||
@@ -825,11 +847,11 @@ class FloatingPanel: NSObject, UIGestureRecognizerDelegate {
|
||||
let bottomY = layoutAdapter.positionY(for: bottom)
|
||||
|
||||
let target = top == state ? bottom : top
|
||||
let redirectionalProgress = max(min(behavior.redirectionalProgress(viewcontroller, from: state, to: target), 1.0), 0.0)
|
||||
let redirectionalProgress = max(min(behavior.redirectionalProgress(vc, from: state, to: target), 1.0), 0.0)
|
||||
|
||||
let th = topY + (bottomY - topY) * redirectionalProgress
|
||||
|
||||
let decelerationRate = behavior.momentumProjectionRate(viewcontroller)
|
||||
let decelerationRate = behavior.momentumProjectionRate(vc)
|
||||
let pY = project(initialVelocity: velocity.y, decelerationRate: decelerationRate) + currentY
|
||||
|
||||
switch currentY {
|
||||
|
||||
Reference in New Issue
Block a user