Compare commits
No commits in common. "master" and "swift4.2" have entirely different histories.
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// 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")]
|
||||
)
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'PanModal'
|
||||
s.version = '1.3.1'
|
||||
s.version = '1.2.4'
|
||||
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.
|
||||
|
|
@ -18,12 +18,12 @@ Pod::Spec.new do |s|
|
|||
# * Finally, don't worry about the indent, CocoaPods strips it!
|
||||
|
||||
s.description = 'PanModal is an elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/PanModal'
|
||||
s.homepage = 'https://github.com/slackhq/PanModal'
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'slack' => 'opensource@slack.com' }
|
||||
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/PanModal.git', :tag => s.version.to_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 = '5.0'
|
||||
s.swift_version = '4.2'
|
||||
s.source_files = 'PanModal/**/*.{swift,h,m}'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -37,4 +36,3 @@ struct PanModalAnimator {
|
|||
completion: completion)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -169,4 +168,3 @@ extension PanModalPresentationAnimator: UIViewControllerAnimatedTransitioning {
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -30,7 +29,6 @@ open class PanModalPresentationController: UIPresentationController {
|
|||
*/
|
||||
public enum PresentationState {
|
||||
case shortForm
|
||||
case mediumForm
|
||||
case longForm
|
||||
}
|
||||
|
||||
|
|
@ -80,17 +78,11 @@ open class PanModalPresentationController: UIPresentationController {
|
|||
*/
|
||||
private var shortFormYPosition: CGFloat = 0
|
||||
|
||||
private var mediumFormYPosition: CGFloat = 0
|
||||
|
||||
/**
|
||||
The y value for the long form presentation state
|
||||
*/
|
||||
private var longFormYPosition: CGFloat = 0
|
||||
|
||||
private var allowsDragToDismiss: Bool {
|
||||
presentable?.onDragToDismiss != nil
|
||||
}
|
||||
|
||||
/**
|
||||
Determine anchored Y postion based on the `anchorModalToLongForm` flag
|
||||
*/
|
||||
|
|
@ -112,13 +104,15 @@ open class PanModalPresentationController: UIPresentationController {
|
|||
Background view used as an overlay over the presenting view
|
||||
*/
|
||||
private lazy var backgroundView: DimmedView = {
|
||||
let view: DimmedView = presentable?.dimmedView ?? DimmedView()
|
||||
let view: DimmedView
|
||||
if let color = presentable?.panModalBackgroundColor {
|
||||
view.backgroundColor = color
|
||||
view = DimmedView(dimColor: color)
|
||||
} else {
|
||||
view = DimmedView()
|
||||
}
|
||||
view.didTap = { [weak self] _ in
|
||||
if self?.presentable?.allowsTapToDismiss == true {
|
||||
self?.presentable?.onTapToDismiss?()
|
||||
self?.dismissPresentedViewController()
|
||||
}
|
||||
}
|
||||
return view
|
||||
|
|
@ -192,21 +186,12 @@ open class PanModalPresentationController: UIPresentationController {
|
|||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: { [weak self] _ in
|
||||
if let yPos = self?.shortFormYPosition {
|
||||
self?.adjust(toYPosition: yPos)
|
||||
}
|
||||
self?.backgroundView.dimState = .max
|
||||
self?.presentedViewController.setNeedsStatusBarAppearanceUpdate()
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -224,10 +209,10 @@ open class PanModalPresentationController: UIPresentationController {
|
|||
})
|
||||
}
|
||||
|
||||
override public func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
if !completed { return }
|
||||
|
||||
presentable?.panModalDidDismiss()
|
||||
override public func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
if completed { return }
|
||||
|
||||
backgroundView.removeFromSuperview()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -269,10 +254,6 @@ public extension PanModalPresentationController {
|
|||
switch state {
|
||||
case .shortForm:
|
||||
snap(toYPosition: shortFormYPosition)
|
||||
|
||||
case .mediumForm:
|
||||
snap(toYPosition: mediumFormYPosition)
|
||||
|
||||
case .longForm:
|
||||
snap(toYPosition: longFormYPosition)
|
||||
}
|
||||
|
|
@ -329,7 +310,7 @@ private extension PanModalPresentationController {
|
|||
var isPresentedViewAnchored: Bool {
|
||||
if !isPresentedViewAnimating
|
||||
&& extendsPanScrolling
|
||||
&& presentedView.frame.minY.rounded() <= anchoredYPosition.rounded() {
|
||||
&& presentedView.frame.minY <= anchoredYPosition {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -381,14 +362,11 @@ private extension PanModalPresentationController {
|
|||
let adjustedSize = CGSize(width: frame.size.width, height: frame.size.height - anchoredYPosition)
|
||||
let panFrame = panContainerView.frame
|
||||
panContainerView.frame.size = frame.size
|
||||
|
||||
let positions = [shortFormYPosition, mediumFormYPosition, longFormYPosition]
|
||||
|
||||
if !positions.contains(panFrame.origin.y) {
|
||||
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)
|
||||
adjust(toYPosition: panFrame.origin.y - panFrame.height + frame.height)
|
||||
}
|
||||
panContainerView.frame.origin.x = frame.origin.x
|
||||
presentedViewController.view.frame = CGRect(origin: .zero, size: adjustedSize)
|
||||
|
|
@ -439,7 +417,6 @@ private extension PanModalPresentationController {
|
|||
else { return }
|
||||
|
||||
shortFormYPosition = layoutPresentable.shortFormYPos
|
||||
mediumFormYPosition = layoutPresentable.mediumFormYPos
|
||||
longFormYPosition = layoutPresentable.longFormYPos
|
||||
anchorModalToLongForm = layoutPresentable.anchorModalToLongForm
|
||||
extendsPanScrolling = layoutPresentable.allowsExtendedPanScrolling
|
||||
|
|
@ -468,15 +445,7 @@ private extension PanModalPresentationController {
|
|||
Set the appropriate contentInset as the configuration within this class
|
||||
offsets it
|
||||
*/
|
||||
let bottomInset: CGFloat
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomInset = presentingViewController.view.safeAreaInsets.bottom
|
||||
} else {
|
||||
bottomInset = presentingViewController.bottomLayoutGuide.length
|
||||
}
|
||||
|
||||
scrollView.contentInset.bottom = bottomInset
|
||||
scrollView.contentInset.bottom = presentingViewController.bottomLayoutGuide.length
|
||||
|
||||
/**
|
||||
As we adjust the bounds during `handleScrollViewTopBounce`
|
||||
|
|
@ -540,18 +509,12 @@ private extension PanModalPresentationController {
|
|||
if velocity.y < 0 {
|
||||
transition(to: .longForm)
|
||||
|
||||
} else if nearest(to: presentedView.frame.minY,
|
||||
inValues: [mediumFormYPosition, containerView.bounds.height]) == mediumFormYPosition
|
||||
&& presentedView.frame.minY < mediumFormYPosition {
|
||||
transition(to: .mediumForm)
|
||||
|
||||
} else if (nearest(to: presentedView.frame.minY,
|
||||
inValues: [longFormYPosition, containerView.bounds.height]) == longFormYPosition
|
||||
&& presentedView.frame.minY < shortFormYPosition) || allowsDragToDismiss == false {
|
||||
} else if (nearest(to: presentedView.frame.minY, inValues: [longFormYPosition, containerView.bounds.height]) == longFormYPosition
|
||||
&& presentedView.frame.minY < shortFormYPosition) || presentable?.allowsDragToDismiss == false {
|
||||
transition(to: .shortForm)
|
||||
|
||||
} else {
|
||||
presentable?.onDragToDismiss?()
|
||||
dismissPresentedViewController()
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -560,23 +523,16 @@ private extension PanModalPresentationController {
|
|||
The `containerView.bounds.height` is used to determine
|
||||
how close the presented view is to the bottom of the screen
|
||||
*/
|
||||
let position = nearest(to: presentedView.frame.minY,
|
||||
inValues: [containerView.bounds.height,
|
||||
shortFormYPosition,
|
||||
mediumFormYPosition,
|
||||
longFormYPosition])
|
||||
let position = nearest(to: presentedView.frame.minY, inValues: [containerView.bounds.height, shortFormYPosition, longFormYPosition])
|
||||
|
||||
if position == longFormYPosition {
|
||||
transition(to: .longForm)
|
||||
|
||||
} else if position == mediumFormYPosition {
|
||||
transition(to: .mediumForm)
|
||||
|
||||
} else if position == shortFormYPosition || allowsDragToDismiss == false {
|
||||
} else if position == shortFormYPosition || presentable?.allowsDragToDismiss == false {
|
||||
transition(to: .shortForm)
|
||||
|
||||
} else {
|
||||
presentable?.onDragToDismiss?()
|
||||
dismissPresentedViewController()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -688,10 +644,19 @@ private extension PanModalPresentationController {
|
|||
*/
|
||||
func adjust(toYPosition yPos: CGFloat) {
|
||||
presentedView.frame.origin.y = max(yPos, anchoredYPosition)
|
||||
|
||||
guard presentedView.frame.origin.y > shortFormYPosition else {
|
||||
backgroundView.dimState = .max
|
||||
return
|
||||
}
|
||||
|
||||
let maxHeight = UIScreen.main.bounds.height - longFormYPosition
|
||||
let yDisplacementFromShortForm = presentedView.frame.origin.y - shortFormYPosition
|
||||
|
||||
backgroundView.dimState = .percent(1.0 - presentedView.frame.origin.y / maxHeight)
|
||||
/**
|
||||
Once presentedView is translated below shortForm, calculate yPos relative to bottom of screen
|
||||
and apply percentage to backgroundView alpha
|
||||
*/
|
||||
backgroundView.dimState = .percent(1.0 - (yDisplacementFromShortForm / presentedView.frame.height))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -706,6 +671,16 @@ private extension PanModalPresentationController {
|
|||
else { return number }
|
||||
return nearestVal
|
||||
}
|
||||
|
||||
/**
|
||||
Dismiss presented view
|
||||
*/
|
||||
func dismissPresentedViewController() {
|
||||
presentable?.panModalWillDismiss()
|
||||
presentedViewController.dismiss(animated: true) { [weak self] in
|
||||
self?.presentable?.panModalDidDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollView Observer
|
||||
|
|
@ -915,4 +890,3 @@ private extension UIScrollView {
|
|||
return isDragging && !isDecelerating || isTracking
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -78,4 +77,3 @@ extension PanModalPresentationDelegate: UIAdaptivePresentationControllerDelegate
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct ModalViewPresentationDetent: Hashable {
|
||||
|
||||
// MARK: - Default Values
|
||||
|
||||
public static var headerOnly: ModalViewPresentationDetent {
|
||||
ModalViewPresentationDetent(height: CGFloat(Int.min))
|
||||
}
|
||||
|
||||
public static func height(_ height: CGFloat) -> ModalViewPresentationDetent {
|
||||
ModalViewPresentationDetent(height: height)
|
||||
}
|
||||
|
||||
public static var maxHeight: ModalViewPresentationDetent {
|
||||
ModalViewPresentationDetent(height: CGFloat(Int.max))
|
||||
}
|
||||
|
||||
// MARK: - Public Properties
|
||||
|
||||
public var height: CGFloat
|
||||
|
||||
// MARK: - Internal Methods
|
||||
|
||||
func panModalHeight(headerHeight: CGFloat = .zero) -> PanModalHeight {
|
||||
if self == .headerOnly {
|
||||
return .contentHeight(headerHeight)
|
||||
}
|
||||
|
||||
if self == .maxHeight {
|
||||
return .maxHeight
|
||||
}
|
||||
|
||||
return .contentHeight(height)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Comparable
|
||||
|
||||
extension ModalViewPresentationDetent: Comparable {
|
||||
public static func < (lhs: ModalViewPresentationDetent, rhs: ModalViewPresentationDetent) -> Bool {
|
||||
lhs.height < rhs.height
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -41,4 +40,3 @@ public enum PanModalHeight: Equatable {
|
|||
*/
|
||||
case intrinsicHeight
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -13,30 +12,14 @@ import UIKit
|
|||
*/
|
||||
public extension PanModalPresentable where Self: UIViewController {
|
||||
|
||||
var onTapToDismiss: (() -> Void)? {
|
||||
{ [weak self] in
|
||||
self?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
var onDragToDismiss: (() -> Void)? {
|
||||
{ [weak self] in
|
||||
self?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
var topOffset: CGFloat {
|
||||
topLayoutOffset
|
||||
return topLayoutOffset + 21.0
|
||||
}
|
||||
|
||||
var shortFormHeight: PanModalHeight {
|
||||
return longFormHeight
|
||||
}
|
||||
|
||||
var mediumFormHeight: PanModalHeight {
|
||||
longFormHeight
|
||||
}
|
||||
|
||||
var longFormHeight: PanModalHeight {
|
||||
|
||||
guard let scrollView = panScrollable
|
||||
|
|
@ -47,10 +30,6 @@ public extension PanModalPresentable where Self: UIViewController {
|
|||
return .contentHeight(scrollView.contentSize.height)
|
||||
}
|
||||
|
||||
var dimmedView: DimmedView? {
|
||||
DimmedView()
|
||||
}
|
||||
|
||||
var cornerRadius: CGFloat {
|
||||
return 8.0
|
||||
}
|
||||
|
|
@ -145,4 +124,3 @@ public extension PanModalPresentable where Self: UIViewController {
|
|||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -27,11 +26,7 @@ extension PanModalPresentable where Self: UIViewController {
|
|||
Gives us the safe area inset from the top.
|
||||
*/
|
||||
var topLayoutOffset: CGFloat {
|
||||
|
||||
guard let rootVC = rootViewController
|
||||
else { return 0}
|
||||
|
||||
if #available(iOS 11.0, *) { return rootVC.view.safeAreaInsets.top } else { return rootVC.topLayoutGuide.length }
|
||||
return UIApplication.shared.keyWindow?.rootViewController?.topLayoutGuide.length ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -39,11 +34,7 @@ extension PanModalPresentable where Self: UIViewController {
|
|||
Gives us the safe area inset from the bottom.
|
||||
*/
|
||||
var bottomLayoutOffset: CGFloat {
|
||||
|
||||
guard let rootVC = rootViewController
|
||||
else { return 0}
|
||||
|
||||
if #available(iOS 11.0, *) { return rootVC.view.safeAreaInsets.bottom } else { return rootVC.bottomLayoutGuide.length }
|
||||
return UIApplication.shared.keyWindow?.rootViewController?.bottomLayoutGuide.length ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,12 +54,6 @@ extension PanModalPresentable where Self: UIViewController {
|
|||
return max(shortFormYPos, longFormYPos)
|
||||
}
|
||||
|
||||
var mediumFormYPos: CGFloat {
|
||||
let mediumFormYPos = topMargin(from: mediumFormHeight)
|
||||
|
||||
return max(mediumFormYPos, longFormYPos)
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the long form Y position
|
||||
|
||||
|
|
@ -114,13 +99,4 @@ 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,7 +5,6 @@
|
|||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -60,4 +59,3 @@ public extension PanModalPresentable where Self: UIViewController {
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2017 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -45,8 +44,6 @@ public protocol PanModalPresentable: AnyObject {
|
|||
*/
|
||||
var shortFormHeight: PanModalHeight { get }
|
||||
|
||||
var mediumFormHeight: PanModalHeight { get }
|
||||
|
||||
/**
|
||||
The height of the pan modal container view
|
||||
when in the longForm presentation state.
|
||||
|
|
@ -57,7 +54,6 @@ public protocol PanModalPresentable: AnyObject {
|
|||
*/
|
||||
var longFormHeight: PanModalHeight { get }
|
||||
|
||||
var dimmedView: DimmedView? { get }
|
||||
/**
|
||||
The corner radius used when `shouldRoundTopCorners` is enabled.
|
||||
|
||||
|
|
@ -138,10 +134,6 @@ public protocol PanModalPresentable: AnyObject {
|
|||
*/
|
||||
var allowsDragToDismiss: Bool { get }
|
||||
|
||||
var onTapToDismiss: (() -> Void)? { get }
|
||||
|
||||
var onDragToDismiss: (() -> Void)? { get }
|
||||
|
||||
/**
|
||||
A flag to determine if dismissal should be initiated when tapping on the dimmed background view.
|
||||
|
||||
|
|
@ -241,4 +233,3 @@ public protocol PanModalPresentable: AnyObject {
|
|||
*/
|
||||
func panModalDidDismiss()
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -29,10 +28,6 @@ protocol PanModalPresenter: AnyObject {
|
|||
/**
|
||||
Presents a view controller that conforms to the PanModalPresentable protocol
|
||||
*/
|
||||
func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType,
|
||||
sourceView: UIView?,
|
||||
sourceRect: CGRect,
|
||||
completion: (() -> Void)?)
|
||||
func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView?, sourceRect: CGRect)
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2019 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -35,14 +34,10 @@ 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,
|
||||
completion: (() -> Void)? = nil) {
|
||||
public func presentPanModal(_ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView? = nil, sourceRect: CGRect = .zero) {
|
||||
|
||||
/**
|
||||
Here, we deliberately do not check for size classes. More info in `PanModalPresentationDelegate`
|
||||
|
|
@ -59,8 +54,7 @@ extension UIViewController: PanModalPresenter {
|
|||
viewControllerToPresent.transitioningDelegate = PanModalPresentationDelegate.default
|
||||
}
|
||||
|
||||
present(viewControllerToPresent, animated: true, completion: completion)
|
||||
present(viewControllerToPresent, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,19 +5,18 @@
|
|||
// Copyright © 2017 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
A dim view for use as an overlay over content you want dimmed.
|
||||
*/
|
||||
open class DimmedView: UIView {
|
||||
public class DimmedView: UIView {
|
||||
|
||||
/**
|
||||
Represents the possible states of the dimmed view.
|
||||
max, off or a percentage of dimAlpha.
|
||||
*/
|
||||
public enum DimState {
|
||||
enum DimState {
|
||||
case max
|
||||
case off
|
||||
case percent(CGFloat)
|
||||
|
|
@ -30,14 +29,21 @@ open class DimmedView: UIView {
|
|||
*/
|
||||
var dimState: DimState = .off {
|
||||
didSet {
|
||||
onChange(dimState: dimState)
|
||||
switch dimState {
|
||||
case .max:
|
||||
alpha = 1.0
|
||||
case .off:
|
||||
alpha = 0.0
|
||||
case .percent(let percentage):
|
||||
alpha = max(0.0, min(1.0, percentage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The closure to be executed when a tap occurs
|
||||
*/
|
||||
var didTap: ((_ recognizer: UITapGestureRecognizer) -> Void)?
|
||||
var didTap: ((_ recognizer: UIGestureRecognizer) -> Void)?
|
||||
|
||||
/**
|
||||
Tap gesture recognizer
|
||||
|
|
@ -48,9 +54,10 @@ open class DimmedView: UIView {
|
|||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init() {
|
||||
init(dimColor: UIColor = UIColor.black.withAlphaComponent(0.7)) {
|
||||
super.init(frame: .zero)
|
||||
alpha = 0.0
|
||||
backgroundColor = dimColor
|
||||
addGestureRecognizer(tapGesture)
|
||||
}
|
||||
|
||||
|
|
@ -60,22 +67,8 @@ open class DimmedView: UIView {
|
|||
|
||||
// MARK: - Event Handlers
|
||||
|
||||
@objc private func didTapView(sender: UITapGestureRecognizer) {
|
||||
didTap?(sender)
|
||||
}
|
||||
|
||||
// MARK: - Subclass override
|
||||
|
||||
open func onChange(dimState: DimState) {
|
||||
switch dimState {
|
||||
case .max:
|
||||
alpha = 1.0
|
||||
case .off:
|
||||
alpha = 0.0
|
||||
case .percent(let percentage):
|
||||
alpha = max(0.0, min(1.0, percentage))
|
||||
}
|
||||
@objc private func didTapView() {
|
||||
didTap?(tapGesture)
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
// Copyright © 2018 Tiny Speck, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
|
|
@ -41,4 +40,3 @@ extension UIView {
|
|||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.slack.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
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 = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
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 = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
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 = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PanModalDemo.app/PanModalDemo";
|
||||
};
|
||||
|
|
@ -763,7 +763,6 @@
|
|||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -818,7 +817,6 @@
|
|||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
|
@ -839,7 +837,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
|
|
@ -860,7 +858,7 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.PanModal;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
|||
20
README.md
20
README.md
|
|
@ -1,13 +1,15 @@
|
|||
|
||||
### PanModal is an elegant and highly customizable presentation API for constructing bottom sheet modals on iOS.
|
||||
|
||||
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
|
||||
|
||||
<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_5-blueviolet.svg" alt="Language: Swift 5" /></a>
|
||||
<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://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" />
|
||||
|
|
@ -24,12 +26,6 @@
|
|||
• <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`
|
||||
|
|
@ -54,14 +50,6 @@ 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`
|
||||
|
|
@ -147,7 +135,7 @@ We will only be fixing critical bugs, thus, for any non-critical issues or featu
|
|||
|
||||
## Authors
|
||||
|
||||
[Stephen Sowole](https://github.com/ste57) • [Tosin Afolabi](https://github.com/tosinaf)
|
||||
[Stephen Sowole](https://github.com/tun57) • [Tosin Afolabi](https://github.com/tosinaf)
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue