* Core
* Support bottom/left/right positioned panel.
* Replace FloatingPanelPosition with FloatingPanelState
* Redefine FloatingPanelPosition
* Fix pan interaction of the intrinsic layout
* Allow to modify a target position
* Fix floating point problem
* Use UIScrollView.adjustedContentInset by default
* Add shouldDecelerate(to:)
* Rename contentOrigin to contentOffsetForPinning
* Improve initialLocation
* Introduce numeric springing
* Support moving a panel with numeric springing
* Update FloatingPanelBehavior
* Remove {top,Bottom}InteractionBuffer from FloatingPanelLayout
* Remove the move animator to be same as UIScrollView behavior
* Add floatingPanelDidMove(_:) tests in calling move(to:animated:)
* Update FloatingPanelControllerDelegate
* Move {add,remove}PanelAnimator delegation
* Add FloatingPanelBehaviorAdapter
* Modify access to `behavior` prop
* Update FloatingPanelLayout
* Support bottom/left/right positioned panel.
* Add anchoredPosition property
* Introduce FloatingLayoutAnchor & FloatingPanelIntrinsicLayoutAnchor
* Add FloatingPanelReferenceEdge
* Rename FloatingPanelDefaultLayout to FloatingPanelBottomLayout
* Update FloatingPanelLayoutAdapter
* Add LayoutAdapter.sortedDirectionalPositions
* Reimplement positionY(for:)
* Add edge{most,least}state
* Add orderedStates
* Add edge{Most,Least} properties in LayoutAdapter
* Modify `bottomOverflow` value and rename it to `containerOverflow`
* Modify access to `layout` prop
* Add FloatingPanelLayoutAnchoring/LayoutAnchor/IntrinsicLayoutAnchor
* Add FloatingPanel{Intrinsic}LayoutAnchor tests
* Update FloatingPanelController
* Add surfaceLocation
* update addPanelToParent API
* Update layout reloading logic
* Rename updateLayout() to invalidateLayout()
* Fix contentViewController access level
* Update FloatingPanelControllerDelegate
* Add floatingPanelDidEndDragging(_ vc:willDecelerate:)
* Update floatingPanelWillBeginDecelerating delegate method
* Remove behavior delegate method
* Reimplement the removal action
* Use floatingPanel(_:shouldRemoveAt:with:) delegate
* Update SurfaceView
* Support bottom/left/right positioned panel.
* Add FloatingPanelSurfaceAppearance
* Add SurfaceView.intrinsicContentSize
* Rename FloatingPanelSurfaceView.add(contentView:)
* GrabberHandle for bottom edge grabber
* Rename GrabberHandleView to FloatingPanelGrabberView
* Rename containerMargin & contentInsets to content{Margins,Padding}
* Rename bottomOverflow to containerOverflow
* ObjC Support
* Add @objc to FloatingPanelControllerDelegate
* Add @objc to FloatingPanelController
* Use @objc protocol FloatingPanelBehavior
* Redefine FloatingPanelState for objc.
* Update Utils
* Modify logger prefix
* Update extensions for left/right positions
* Update unit tests
* Update layout tests
* Add test_surfaceView_{contentView,containerView}
* Add test_positionY_top2bottom()
* Add test_moveTo_bottomEdge()
* Update test_warningRetainCycle()
* Fix FloatingPanelControllerTests
* Fix LayoutTests
* Fix FloatingPanelStateTests
* Fix FloatingPanelTest
123 lines
4.9 KiB
Swift
123 lines
4.9 KiB
Swift
//
|
|
// Created by Shin Yamamoto on 2018/11/21.
|
|
// Copyright © 2018 Shin Yamamoto. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class FloatingPanelModalTransition: NSObject, UIViewControllerTransitioningDelegate {
|
|
func animationController(forPresented presented: UIViewController,
|
|
presenting: UIViewController,
|
|
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
return FloatingPanelModalPresentTransition()
|
|
}
|
|
|
|
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
return FloatingPanelModalDismissTransition()
|
|
}
|
|
|
|
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
|
return FloatingPanelPresentationController(presentedViewController: presented, presenting: presenting)
|
|
}
|
|
}
|
|
|
|
class FloatingPanelPresentationController: UIPresentationController {
|
|
override func presentationTransitionWillBegin() {
|
|
// Must call here even if duplicating on in containerViewWillLayoutSubviews()
|
|
// Because it let the floating panel present correctly with the presentation animation
|
|
addFloatingPanel()
|
|
}
|
|
|
|
override func presentationTransitionDidEnd(_ completed: Bool) {
|
|
// For non-animated presentation
|
|
if let fpc = presentedViewController as? FloatingPanelController, fpc.state == .hidden {
|
|
fpc.show(animated: false, completion: nil)
|
|
}
|
|
}
|
|
|
|
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
|
if let fpc = presentedViewController as? FloatingPanelController {
|
|
// For non-animated dismissal
|
|
if fpc.state != .hidden {
|
|
fpc.hide(animated: false, completion: nil)
|
|
}
|
|
fpc.view.removeFromSuperview()
|
|
}
|
|
}
|
|
|
|
override func containerViewWillLayoutSubviews() {
|
|
guard
|
|
let fpc = presentedViewController as? FloatingPanelController
|
|
else { fatalError() }
|
|
|
|
/*
|
|
* Layout the views managed by `FloatingPanelController` here for the
|
|
* sake of the presentation and dismissal modally from the controller.
|
|
*/
|
|
addFloatingPanel()
|
|
|
|
// Forward touch events to the presenting view controller
|
|
(fpc.view as? FloatingPanelPassThroughView)?.eventForwardingView = presentingViewController.view
|
|
}
|
|
|
|
@objc func handleBackdrop(tapGesture: UITapGestureRecognizer) {
|
|
presentedViewController.dismiss(animated: true, completion: nil)
|
|
}
|
|
|
|
private func addFloatingPanel() {
|
|
guard
|
|
let containerView = self.containerView,
|
|
let fpc = presentedViewController as? FloatingPanelController
|
|
else { fatalError() }
|
|
|
|
containerView.addSubview(fpc.view)
|
|
fpc.view.frame = containerView.bounds
|
|
fpc.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
}
|
|
}
|
|
|
|
class FloatingPanelModalPresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
|
guard
|
|
let fpc = transitionContext?.viewController(forKey: .to) as? FloatingPanelController
|
|
else { fatalError()}
|
|
|
|
let animator = fpc.delegate?.floatingPanel?(fpc, animatorForPresentingTo: fpc.layout.initialState)
|
|
?? FloatingPanelDefaultBehavior().addPanelAnimator(fpc, to: fpc.layout.initialState)
|
|
return TimeInterval(animator.duration)
|
|
}
|
|
|
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
|
guard
|
|
let fpc = transitionContext.viewController(forKey: .to) as? FloatingPanelController
|
|
else { fatalError() }
|
|
|
|
fpc.show(animated: true) {
|
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
|
}
|
|
}
|
|
}
|
|
|
|
class FloatingPanelModalDismissTransition: NSObject, UIViewControllerAnimatedTransitioning {
|
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
|
guard
|
|
let fpc = transitionContext?.viewController(forKey: .from) as? FloatingPanelController
|
|
else { fatalError()}
|
|
|
|
let animator = fpc.delegate?.floatingPanel?(fpc, animatorForDismissingWith: .zero)
|
|
?? FloatingPanelDefaultBehavior().removePanelAnimator(fpc, from: fpc.state, with: .zero)
|
|
return TimeInterval(animator.duration)
|
|
}
|
|
|
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
|
guard
|
|
let fpc = transitionContext.viewController(forKey: .from) as? FloatingPanelController
|
|
else { fatalError() }
|
|
|
|
fpc.hide(animated: true) {
|
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
|
}
|
|
}
|
|
}
|
|
|