Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2f5bd7d16 | |||
| b012aecb6b | |||
| 22b4ddd47e | |||
| 17a8231f20 | |||
| 1819113e97 | |||
| 208640e03e | |||
| f02439dc0c | |||
| 047415090a | |||
| 1e7c0534fc | |||
| 7f07cdff27 | |||
| 45f8dfcf19 | |||
| 5d2b0977bd | |||
| a792f46e3d | |||
| d6b4ba3d05 | |||
| 5c1d8c49a7 | |||
| af264ebb0d | |||
| 9d73db3919 | |||
| 6b1edd1dfb | |||
| 9134d20032 | |||
| 0c6f9ff040 | |||
| 12ac36646f | |||
| f9fedcb597 |
@@ -0,0 +1,22 @@
|
||||
// swift-tools-version:5.1
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "PanModal",
|
||||
platforms: [.iOS(.v10)],
|
||||
products: [
|
||||
.library(
|
||||
name: "PanModal",
|
||||
targets: ["PanModal"]),
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "PanModal",
|
||||
dependencies: [],
|
||||
path: "PanModal")
|
||||
],
|
||||
swiftLanguageVersions: [.version("5.0")]
|
||||
)
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'PanModal'
|
||||
s.version = '1.2.3'
|
||||
s.version = '1.2.7'
|
||||
s.summary = 'PanModal is an elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
@@ -24,6 +24,6 @@ Pod::Spec.new do |s|
|
||||
s.source = { :git => 'https://github.com/slackhq/PanModal.git', :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/slackhq'
|
||||
s.ios.deployment_target = '10.0'
|
||||
s.swift_version = '4.2'
|
||||
s.swift_version = '5.0'
|
||||
s.source_files = 'PanModal/**/*.{swift,h,m}'
|
||||
end
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -36,3 +37,4 @@ struct PanModalAnimator {
|
||||
completion: completion)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -72,7 +73,6 @@ public class PanModalPresentationAnimator: NSObject {
|
||||
|
||||
// Calls viewWillAppear and viewWillDisappear
|
||||
fromVC.beginAppearanceTransition(false, animated: true)
|
||||
toVC.beginAppearanceTransition(true, animated: true)
|
||||
|
||||
// Presents the view in shortForm position, initially
|
||||
let yPos: CGFloat = presentable?.shortFormYPos ?? 0.0
|
||||
@@ -94,7 +94,6 @@ public class PanModalPresentationAnimator: NSObject {
|
||||
}, config: presentable) { [weak self] didComplete in
|
||||
// Calls viewDidAppear and viewDidDisappear
|
||||
fromVC.endAppearanceTransition()
|
||||
toVC.endAppearanceTransition()
|
||||
transitionContext.completeTransition(didComplete)
|
||||
self?.feedbackGenerator = nil
|
||||
}
|
||||
@@ -111,7 +110,6 @@ public class PanModalPresentationAnimator: NSObject {
|
||||
else { return }
|
||||
|
||||
// Calls viewWillAppear and viewWillDisappear
|
||||
fromVC.beginAppearanceTransition(false, animated: true)
|
||||
toVC.beginAppearanceTransition(true, animated: true)
|
||||
|
||||
let presentable = panModalLayoutType(from: transitionContext)
|
||||
@@ -122,7 +120,6 @@ public class PanModalPresentationAnimator: NSObject {
|
||||
}, config: presentable) { didComplete in
|
||||
fromVC.view.removeFromSuperview()
|
||||
// Calls viewDidAppear and viewDidDisappear
|
||||
fromVC.endAppearanceTransition()
|
||||
toVC.endAppearanceTransition()
|
||||
transitionContext.completeTransition(didComplete)
|
||||
}
|
||||
@@ -172,3 +169,4 @@ extension PanModalPresentationAnimator: UIViewControllerAnimatedTransitioning {
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,7 @@ import UIKit
|
||||
By conforming to the PanModalPresentable protocol & overriding values
|
||||
the presented view can define its layout configuration & presentation.
|
||||
*/
|
||||
public class PanModalPresentationController: UIPresentationController {
|
||||
open class PanModalPresentationController: UIPresentationController {
|
||||
|
||||
/**
|
||||
Enum representing the possible presentation states
|
||||
@@ -105,13 +106,15 @@ public class PanModalPresentationController: UIPresentationController {
|
||||
*/
|
||||
private lazy var backgroundView: DimmedView = {
|
||||
let view: DimmedView
|
||||
if let alpha = presentable?.backgroundAlpha {
|
||||
view = DimmedView(dimAlpha: alpha)
|
||||
if let color = presentable?.panModalBackgroundColor {
|
||||
view = DimmedView(dimColor: color)
|
||||
} else {
|
||||
view = DimmedView()
|
||||
}
|
||||
view.didTap = { [weak self] _ in
|
||||
self?.dismissPresentedViewController()
|
||||
if self?.presentable?.allowsTapToDismiss == true {
|
||||
self?.presentedViewController.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
return view
|
||||
}()
|
||||
@@ -131,7 +134,7 @@ public class PanModalPresentationController: UIPresentationController {
|
||||
*/
|
||||
private lazy var dragIndicatorView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .lightGray
|
||||
view.backgroundColor = presentable?.dragIndicatorBackgroundColor
|
||||
view.layer.cornerRadius = Constants.dragIndicatorSize.height / 2.0
|
||||
return view
|
||||
}()
|
||||
@@ -189,7 +192,14 @@ public class PanModalPresentationController: UIPresentationController {
|
||||
})
|
||||
}
|
||||
|
||||
override public func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
if completed { return }
|
||||
|
||||
backgroundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
override public func dismissalTransitionWillBegin() {
|
||||
presentable?.panModalWillDismiss()
|
||||
|
||||
guard let coordinator = presentedViewController.transitionCoordinator else {
|
||||
backgroundView.dimState = .off
|
||||
@@ -207,10 +217,10 @@ public class PanModalPresentationController: UIPresentationController {
|
||||
})
|
||||
}
|
||||
|
||||
override public func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
if completed { return }
|
||||
|
||||
backgroundView.removeFromSuperview()
|
||||
override public func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
if !completed { return }
|
||||
|
||||
presentable?.panModalDidDismiss()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +236,6 @@ public class PanModalPresentationController: UIPresentationController {
|
||||
else { return }
|
||||
|
||||
self.adjustPresentedViewFrame()
|
||||
|
||||
if presentable.shouldRoundTopCorners {
|
||||
self.addRoundedCorners(to: self.presentedView)
|
||||
}
|
||||
@@ -259,35 +268,27 @@ public extension PanModalPresentationController {
|
||||
}
|
||||
|
||||
/**
|
||||
Set the content offset of the scroll view
|
||||
Operations on the scroll view, such as content height changes,
|
||||
or when inserting/deleting rows can cause the pan modal to jump,
|
||||
caused by the pan modal responding to content offset changes.
|
||||
|
||||
Due to content offset observation, its not possible to programmatically
|
||||
set the content offset directly on the scroll view while in the short form.
|
||||
|
||||
This method pauses the content offset KVO, performs the content offset change
|
||||
and then resumes content offset observation.
|
||||
To avoid this, you can call this method to perform scroll view updates,
|
||||
with scroll observation temporarily disabled.
|
||||
*/
|
||||
func setContentOffset(offset: CGPoint) {
|
||||
func performUpdates(_ updates: () -> Void) {
|
||||
|
||||
guard let scrollView = presentable?.panScrollable
|
||||
else { return }
|
||||
|
||||
/**
|
||||
Invalidate scroll view observer
|
||||
to prevent its overriding the content offset change
|
||||
*/
|
||||
// Pause scroll observer
|
||||
scrollObserver?.invalidate()
|
||||
scrollObserver = nil
|
||||
|
||||
/**
|
||||
Set scroll view offset & track scrolling
|
||||
*/
|
||||
scrollView.setContentOffset(offset, animated:false)
|
||||
trackScrolling(scrollView)
|
||||
// Perform updates
|
||||
updates()
|
||||
|
||||
/**
|
||||
Add the scroll view observer
|
||||
*/
|
||||
// Resume scroll observer
|
||||
trackScrolling(scrollView)
|
||||
observe(scrollView: scrollView)
|
||||
}
|
||||
|
||||
@@ -317,7 +318,7 @@ private extension PanModalPresentationController {
|
||||
var isPresentedViewAnchored: Bool {
|
||||
if !isPresentedViewAnimating
|
||||
&& extendsPanScrolling
|
||||
&& presentedView.frame.minY <= anchoredYPosition {
|
||||
&& presentedView.frame.minY.rounded() <= anchoredYPosition.rounded() {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -367,7 +368,16 @@ private extension PanModalPresentationController {
|
||||
else { return }
|
||||
|
||||
let adjustedSize = CGSize(width: frame.size.width, height: frame.size.height - anchoredYPosition)
|
||||
let panFrame = panContainerView.frame
|
||||
panContainerView.frame.size = frame.size
|
||||
|
||||
if ![shortFormYPosition, longFormYPosition].contains(panFrame.origin.y) {
|
||||
// if the container is already in the correct position, no need to adjust positioning
|
||||
// (rotations & size changes cause positioning to be out of sync)
|
||||
let yPosition = panFrame.origin.y - panFrame.height + frame.height
|
||||
presentedView.frame.origin.y = max(yPosition, anchoredYPosition)
|
||||
}
|
||||
panContainerView.frame.origin.x = frame.origin.x
|
||||
presentedViewController.view.frame = CGRect(origin: .zero, size: adjustedSize)
|
||||
}
|
||||
|
||||
@@ -513,7 +523,7 @@ private extension PanModalPresentationController {
|
||||
transition(to: .shortForm)
|
||||
|
||||
} else {
|
||||
dismissPresentedViewController()
|
||||
presentedViewController.dismiss(animated: true)
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -531,7 +541,7 @@ private extension PanModalPresentationController {
|
||||
transition(to: .shortForm)
|
||||
|
||||
} else {
|
||||
dismissPresentedViewController()
|
||||
presentedViewController.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,14 +680,6 @@ private extension PanModalPresentationController {
|
||||
else { return number }
|
||||
return nearestVal
|
||||
}
|
||||
|
||||
/**
|
||||
Dismiss presented view
|
||||
*/
|
||||
func dismissPresentedViewController() {
|
||||
presentable?.panModalWillDismiss()
|
||||
presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollView Observer
|
||||
@@ -782,7 +784,7 @@ private extension PanModalPresentationController {
|
||||
*/
|
||||
func handleScrollViewTopBounce(scrollView: UIScrollView, change: NSKeyValueObservedChange<CGPoint>) {
|
||||
|
||||
guard let oldYValue = change.oldValue?.y
|
||||
guard let oldYValue = change.oldValue?.y, scrollView.isDecelerating
|
||||
else { return }
|
||||
|
||||
let yOffset = scrollView.contentOffset.y
|
||||
@@ -822,11 +824,11 @@ extension PanModalPresentationController: UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
/**
|
||||
Allow simultaneous gesture recognizers only when the other gesture recognizer
|
||||
is a pan gesture recognizer
|
||||
Allow simultaneous gesture recognizers only when the other gesture recognizer's view
|
||||
is the pan scrollable view
|
||||
*/
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return otherGestureRecognizer == panGestureRecognizer
|
||||
return otherGestureRecognizer.view == presentable?.panScrollable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,3 +889,4 @@ private extension UIScrollView {
|
||||
return isDragging && !isDecelerating || isTracking
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -77,3 +78,4 @@ extension PanModalPresentationDelegate: UIAdaptivePresentationControllerDelegate
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -40,3 +41,4 @@ public enum PanModalHeight: Equatable {
|
||||
*/
|
||||
case intrinsicHeight
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,12 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
return [.curveEaseInOut, .allowUserInteraction, .beginFromCurrentState]
|
||||
}
|
||||
|
||||
var backgroundAlpha: CGFloat {
|
||||
return 0.7
|
||||
var panModalBackgroundColor: UIColor {
|
||||
return UIColor.black.withAlphaComponent(0.7)
|
||||
}
|
||||
|
||||
var dragIndicatorBackgroundColor: UIColor {
|
||||
return UIColor.lightGray
|
||||
}
|
||||
|
||||
var scrollIndicatorInsets: UIEdgeInsets {
|
||||
@@ -72,6 +77,10 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
return true
|
||||
}
|
||||
|
||||
var allowsTapToDismiss: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var isUserInteractionEnabled: Bool {
|
||||
return true
|
||||
}
|
||||
@@ -112,4 +121,8 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
|
||||
}
|
||||
|
||||
func panModalDidDismiss() {
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,11 @@ extension PanModalPresentable where Self: UIViewController {
|
||||
Gives us the safe area inset from the top.
|
||||
*/
|
||||
var topLayoutOffset: CGFloat {
|
||||
return UIApplication.shared.keyWindow?.rootViewController?.topLayoutGuide.length ?? 0
|
||||
|
||||
guard let rootVC = rootViewController
|
||||
else { return 0}
|
||||
|
||||
if #available(iOS 11.0, *) { return rootVC.view.safeAreaInsets.top } else { return rootVC.topLayoutGuide.length }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +39,11 @@ extension PanModalPresentable where Self: UIViewController {
|
||||
Gives us the safe area inset from the bottom.
|
||||
*/
|
||||
var bottomLayoutOffset: CGFloat {
|
||||
return UIApplication.shared.keyWindow?.rootViewController?.bottomLayoutGuide.length ?? 0
|
||||
|
||||
guard let rootVC = rootViewController
|
||||
else { return 0}
|
||||
|
||||
if #available(iOS 11.0, *) { return rootVC.view.safeAreaInsets.bottom } else { return rootVC.bottomLayoutGuide.length }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,4 +108,13 @@ extension PanModalPresentable where Self: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private var rootViewController: UIViewController? {
|
||||
|
||||
guard let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as? UIApplication
|
||||
else { return nil }
|
||||
|
||||
return application.keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -29,16 +30,6 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
presentedVC?.transition(to: state)
|
||||
}
|
||||
|
||||
/**
|
||||
Programmatically set the content offset of the pan scrollable.
|
||||
|
||||
This is required to use while in the short form presentation state,
|
||||
as due to content offset observation, setting the content offset directly would fail
|
||||
*/
|
||||
func panModalSetContentOffset(offset: CGPoint) {
|
||||
presentedVC?.setContentOffset(offset: offset)
|
||||
}
|
||||
|
||||
/**
|
||||
A function wrapper over the `setNeedsLayoutUpdate()`
|
||||
function in the PanModalPresentationController.
|
||||
@@ -49,6 +40,16 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
presentedVC?.setNeedsLayoutUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
Operations on the scroll view, such as content height changes, or when inserting/deleting rows can cause the pan modal to jump,
|
||||
caused by the pan modal responding to content offset changes.
|
||||
|
||||
To avoid this, you can call this method to perform scroll view updates, with scroll observation temporarily disabled.
|
||||
*/
|
||||
func panModalPerformUpdates(_ updates: () -> Void) {
|
||||
presentedVC?.performUpdates(updates)
|
||||
}
|
||||
|
||||
/**
|
||||
A function wrapper over the animate function in PanModalAnimator.
|
||||
|
||||
@@ -59,3 +60,4 @@ public extension PanModalPresentable where Self: UIViewController {
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2017 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -86,13 +87,20 @@ public protocol PanModalPresentable: AnyObject {
|
||||
var transitionAnimationOptions: UIView.AnimationOptions { get }
|
||||
|
||||
/**
|
||||
The background view alpha.
|
||||
The background view color.
|
||||
|
||||
- Note: This is only utilized at the very start of the transition.
|
||||
|
||||
Default Value is 0.7.
|
||||
*/
|
||||
var backgroundAlpha: CGFloat { get }
|
||||
Default Value is black with alpha component 0.7.
|
||||
*/
|
||||
var panModalBackgroundColor: UIColor { get }
|
||||
|
||||
/**
|
||||
The drag indicator view color.
|
||||
|
||||
Default value is light gray.
|
||||
*/
|
||||
var dragIndicatorBackgroundColor: UIColor { get }
|
||||
|
||||
/**
|
||||
We configure the panScrollable's scrollIndicatorInsets interally so override this value
|
||||
@@ -127,6 +135,13 @@ public protocol PanModalPresentable: AnyObject {
|
||||
*/
|
||||
var allowsDragToDismiss: Bool { get }
|
||||
|
||||
/**
|
||||
A flag to determine if dismissal should be initiated when tapping on the dimmed background view.
|
||||
|
||||
Default value is true.
|
||||
*/
|
||||
var allowsTapToDismiss: Bool { get }
|
||||
|
||||
/**
|
||||
A flag to toggle user interactions on the container view.
|
||||
|
||||
@@ -212,4 +227,11 @@ public protocol PanModalPresentable: AnyObject {
|
||||
*/
|
||||
func panModalWillDismiss()
|
||||
|
||||
/**
|
||||
Notifies the delegate after the pan modal is dismissed.
|
||||
|
||||
Default value is an empty implementation.
|
||||
*/
|
||||
func panModalDidDismiss()
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,10 @@ protocol PanModalPresenter: AnyObject {
|
||||
/**
|
||||
Presents a view controller that conforms to the PanModalPresentable protocol
|
||||
*/
|
||||
func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView?, sourceRect: CGRect)
|
||||
func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType,
|
||||
sourceView: UIView?,
|
||||
sourceRect: CGRect,
|
||||
completion: (() -> Void)?)
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -34,10 +35,14 @@ extension UIViewController: PanModalPresenter {
|
||||
- viewControllerToPresent: The view controller to be presented
|
||||
- sourceView: The view containing the anchor rectangle for the popover.
|
||||
- sourceRect: The rectangle in the specified view in which to anchor the popover.
|
||||
- completion: The block to execute after the presentation finishes. You may specify nil for this parameter.
|
||||
|
||||
- Note: sourceView & sourceRect are only required for presentation on an iPad.
|
||||
*/
|
||||
public func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView? = nil, sourceRect: CGRect = .zero) {
|
||||
public func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType,
|
||||
sourceView: UIView? = nil,
|
||||
sourceRect: CGRect = .zero,
|
||||
completion: (() -> Void)? = nil) {
|
||||
|
||||
/**
|
||||
Here, we deliberately do not check for size classes. More info in `PanModalPresentationDelegate`
|
||||
@@ -54,7 +59,8 @@ extension UIViewController: PanModalPresenter {
|
||||
viewControllerToPresent.transitioningDelegate = PanModalPresentationDelegate.default
|
||||
}
|
||||
|
||||
present(viewControllerToPresent, animated: true, completion: nil)
|
||||
present(viewControllerToPresent, animated: true, completion: completion)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2017 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -31,12 +32,11 @@ public class DimmedView: UIView {
|
||||
didSet {
|
||||
switch dimState {
|
||||
case .max:
|
||||
alpha = dimAlpha
|
||||
alpha = 1.0
|
||||
case .off:
|
||||
alpha = 0.0
|
||||
case .percent(let percentage):
|
||||
let val = max(0.0, min(1.0, percentage))
|
||||
alpha = dimAlpha * val
|
||||
alpha = max(0.0, min(1.0, percentage))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,15 +53,12 @@ public class DimmedView: UIView {
|
||||
return UITapGestureRecognizer(target: self, action: #selector(didTapView))
|
||||
}()
|
||||
|
||||
private let dimAlpha: CGFloat
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init(dimAlpha: CGFloat = 0.7) {
|
||||
self.dimAlpha = dimAlpha
|
||||
init(dimColor: UIColor = UIColor.black.withAlphaComponent(0.7)) {
|
||||
super.init(frame: .zero)
|
||||
alpha = 0.0
|
||||
backgroundColor = .black
|
||||
backgroundColor = dimColor
|
||||
addGestureRecognizer(tapGesture)
|
||||
}
|
||||
|
||||
@@ -76,3 +73,4 @@ public class DimmedView: UIView {
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
@@ -40,3 +41,4 @@ extension UIView {
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -629,7 +629,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.slack.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -658,7 +658,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.slack.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -679,7 +679,7 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = slack.PanModalTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PanModalDemo.app/PanModalDemo";
|
||||
};
|
||||
@@ -699,7 +699,7 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = slack.PanModalTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PanModalDemo.app/PanModalDemo";
|
||||
};
|
||||
@@ -763,6 +763,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -817,6 +818,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
@@ -837,7 +839,7 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -858,7 +860,7 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
|
||||
### PanModal is an elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/slackhq/PanModal/raw/master/Screenshots/panModal.gif" width="30%" height="30%" alt="Screenshot Preview" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/Platform-iOS_10+-green.svg" alt="Platform: iOS 10.0+" />
|
||||
<a href="https://developer.apple.com/swift" target="_blank"><img src="https://img.shields.io/badge/Language-Swift_4-blueviolet.svg" alt="Language: Swift 4" /></a>
|
||||
<a href="https://developer.apple.com/swift" target="_blank"><img src="https://img.shields.io/badge/Language-Swift_5-blueviolet.svg" alt="Language: Swift 5" /></a>
|
||||
<a href="https://cocoapods.org/pods/PanModal" target="_blank"><img src="https://img.shields.io/badge/CocoaPods-v1.0-red.svg" alt="CocoaPods compatible" /></a>
|
||||
<a href="https://github.com/Carthage/Carthage" target="_blank"><img src="https://img.shields.io/badge/Carthage-compatible-blue.svg" alt="Carthage compatible" /></a>
|
||||
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT" />
|
||||
@@ -21,6 +24,12 @@
|
||||
• <a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Read our <a href="https://slack.engineering/panmodal-better-support-for-thumb-accessibility-on-slack-mobile-52b2a7596031" target="_blank">blog</a> on how Slack is getting more :thumbsup: with PanModal
|
||||
|
||||
Swift 4.2 support can be found on the `Swift4.2` branch.
|
||||
</p>
|
||||
|
||||
## Features
|
||||
|
||||
* Supports any type of `UIViewController`
|
||||
@@ -45,6 +54,14 @@ pod 'PanModal'
|
||||
github "slackhq/PanModal"
|
||||
```
|
||||
|
||||
* <a href="https://swift.org/package-manager/" target="_blank">Swift Package Manager</a>:
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/slackhq/PanModal.git", .exact("1.2.6")),
|
||||
],
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
PanModal was designed to be used effortlessly. Simply call `presentPanModal` in the same way you would expect to present a `UIViewController`
|
||||
@@ -130,7 +147,7 @@ We will only be fixing critical bugs, thus, for any non-critical issues or featu
|
||||
|
||||
## Authors
|
||||
|
||||
[Stephen Sowole](https://github.com/tun57) • [Tosin Afolabi](https://github.com/tosinaf)
|
||||
[Stephen Sowole](https://github.com/ste57) • [Tosin Afolabi](https://github.com/tosinaf)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ class TransientAlertViewController: AlertViewController {
|
||||
return true
|
||||
}
|
||||
|
||||
override var backgroundAlpha: CGFloat {
|
||||
return 0.0
|
||||
override var panModalBackgroundColor: UIColor {
|
||||
return .clear
|
||||
}
|
||||
|
||||
override var isUserInteractionEnabled: Bool {
|
||||
|
||||
@@ -46,8 +46,8 @@ class AlertViewController: UIViewController, PanModalPresentable {
|
||||
return shortFormHeight
|
||||
}
|
||||
|
||||
var backgroundAlpha: CGFloat {
|
||||
return 0.1
|
||||
var panModalBackgroundColor: UIColor {
|
||||
return UIColor.black.withAlphaComponent(0.1)
|
||||
}
|
||||
|
||||
var shouldRoundTopCorners: Bool {
|
||||
|
||||
+9
-5
@@ -12,13 +12,17 @@ class NavigationController: UINavigationController, PanModalPresentable {
|
||||
|
||||
private let navGroups = NavUserGroups()
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .lightContent
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
viewControllers = [navGroups]
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
pushViewController(navGroups, animated: false)
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .lightContent
|
||||
}
|
||||
|
||||
override func popViewController(animated: Bool) -> UIViewController? {
|
||||
|
||||
@@ -48,11 +48,13 @@ class PanModalTests: XCTestCase {
|
||||
XCTAssertEqual(vc.shortFormHeight, PanModalHeight.maxHeight)
|
||||
XCTAssertEqual(vc.longFormHeight, PanModalHeight.maxHeight)
|
||||
XCTAssertEqual(vc.springDamping, 0.8)
|
||||
XCTAssertEqual(vc.backgroundAlpha, 0.7)
|
||||
XCTAssertEqual(vc.panModalBackgroundColor, UIColor.black.withAlphaComponent(0.7))
|
||||
XCTAssertEqual(vc.dragIndicatorBackgroundColor, UIColor.lightGray)
|
||||
XCTAssertEqual(vc.scrollIndicatorInsets, .zero)
|
||||
XCTAssertEqual(vc.anchorModalToLongForm, true)
|
||||
XCTAssertEqual(vc.allowsExtendedPanScrolling, false)
|
||||
XCTAssertEqual(vc.allowsDragToDismiss, true)
|
||||
XCTAssertEqual(vc.allowsTapToDismiss, true)
|
||||
XCTAssertEqual(vc.isUserInteractionEnabled, true)
|
||||
XCTAssertEqual(vc.isHapticFeedbackEnabled, true)
|
||||
XCTAssertEqual(vc.shouldRoundTopCorners, false)
|
||||
|
||||
Reference in New Issue
Block a user