-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
82be861
commit c2e86d4
Showing
16 changed files
with
1,036 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
AVStackController.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
AVStackController.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
Binary file added
BIN
+15.6 KB
...proj/project.xcworkspace/xcuserdata/anandverma.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
14 changes: 14 additions & 0 deletions
14
...Controller.xcodeproj/xcuserdata/anandverma.xcuserdatad/xcschemes/xcschememanagement.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>SchemeUserState</key> | ||
<dict> | ||
<key>AVStackController.xcscheme_^#shared#^_</key> | ||
<dict> | ||
<key>orderHint</key> | ||
<integer>0</integer> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// AVStackController.h | ||
// AVStackController | ||
// | ||
// Created by Anand Verma on 11/08/20. | ||
// Copyright © 2020 AVSwiftHub. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
//! Project version number for AVStackController. | ||
FOUNDATION_EXPORT double AVStackControllerVersionNumber; | ||
|
||
//! Project version string for AVStackController. | ||
FOUNDATION_EXPORT const unsigned char AVStackControllerVersionString[]; | ||
|
||
// In this header, you should import all the public headers of your framework using statements like #import <AVStackController/PublicHeader.h> | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
</dict> | ||
</plist> |
33 changes: 33 additions & 0 deletions
33
AVStackController/Sources/BottomPopupDismissAnimator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// | ||
// DraggableDismissAnimator.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
|
||
import UIKit | ||
|
||
final class BottomPopupDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning { | ||
private unowned var attributesOwner: BottomPresentableViewController | ||
|
||
init(attributesOwner: BottomPresentableViewController) { | ||
self.attributesOwner = attributesOwner | ||
} | ||
|
||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | ||
return attributesOwner.popupDismissDuration | ||
} | ||
|
||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | ||
let fromVC = transitionContext.viewController(forKey: .from)! | ||
let dismissFrame = CGRect(origin: CGPoint(x: 0, y: UIScreen.main.bounds.size.height), size: fromVC.view.frame.size) | ||
|
||
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { | ||
fromVC.view.frame = dismissFrame | ||
}) { (_) in | ||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled) | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
AVStackController/Sources/BottomPopupDismissInteractionController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// | ||
// DraggableDismissInteractionController.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
protocol BottomPopupDismissInteractionControllerDelegate: class { | ||
func dismissInteractionPercentChanged(from oldValue: CGFloat, to newValue: CGFloat) | ||
} | ||
|
||
final class BottomPopupDismissInteractionController: UIPercentDrivenInteractiveTransition { | ||
private let kMinPercentOfVisiblePartToCompleteAnimation = CGFloat(0.5) | ||
private let kSwipeDownThreshold = CGFloat(1000) | ||
private weak var presentedViewController: BottomPresentableViewController? | ||
private weak var transitioningDelegate: BottomPopupTransitionHandler? | ||
private unowned var attributesDelegate: BottomPopupAttributesDelegate | ||
weak var delegate: BottomPopupDismissInteractionControllerDelegate? | ||
|
||
private var currentPercent: CGFloat = 0 { | ||
didSet { | ||
delegate?.dismissInteractionPercentChanged(from: oldValue, to: currentPercent) | ||
} | ||
} | ||
|
||
init(presentedViewController: BottomPresentableViewController?, attributesDelegate: BottomPopupAttributesDelegate) { | ||
self.presentedViewController = presentedViewController | ||
self.transitioningDelegate = presentedViewController?.transitioningDelegate as? BottomPopupTransitionHandler | ||
self.attributesDelegate = attributesDelegate | ||
super.init() | ||
preparePanGesture(in: presentedViewController?.view) | ||
} | ||
|
||
private func finishAnimation(withVelocity velocity: CGPoint) { | ||
if currentPercent > kMinPercentOfVisiblePartToCompleteAnimation || velocity.y > kSwipeDownThreshold { | ||
finish() | ||
} else { | ||
cancel() | ||
} | ||
} | ||
|
||
private func preparePanGesture(in view: UIView?) { | ||
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) | ||
presentedViewController?.view?.addGestureRecognizer(panGesture) | ||
} | ||
|
||
@objc private func handlePanGesture(_ pan: UIPanGestureRecognizer) { | ||
guard attributesDelegate.popupShouldBeganDismiss else { return } | ||
|
||
let translationY = pan.translation(in: presentedViewController?.view).y | ||
currentPercent = min(max(translationY/(presentedViewController?.view.frame.size.height ?? 0), 0), 1) | ||
|
||
switch pan.state { | ||
case .began: | ||
transitioningDelegate?.isInteractiveDismissStarted = true | ||
presentedViewController?.dismiss(animated: true, completion: nil) | ||
case .changed: | ||
update(currentPercent) | ||
default: | ||
let velocity = pan.velocity(in: presentedViewController?.view) | ||
transitioningDelegate?.isInteractiveDismissStarted = false | ||
finishAnimation(withVelocity: velocity) | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
AVStackController/Sources/BottomPopupPresentAnimator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// DraggablePresentAnimator.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
final class BottomPopupPresentAnimator: NSObject, UIViewControllerAnimatedTransitioning { | ||
private unowned var attributesOwner: BottomPresentableViewController | ||
|
||
init(attributesOwner: BottomPresentableViewController) { | ||
self.attributesOwner = attributesOwner | ||
} | ||
|
||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | ||
return attributesOwner.popupPresentDuration | ||
} | ||
|
||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | ||
let toVC = transitionContext.viewController(forKey: .to)! | ||
transitionContext.containerView.addSubview(toVC.view) | ||
let presentFrame = transitionContext.finalFrame(for: toVC) | ||
let initialFrame = CGRect(origin: CGPoint(x: 0, y: UIScreen.main.bounds.size.height), size: presentFrame.size) | ||
toVC.view.frame = initialFrame | ||
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { | ||
toVC.view.frame = presentFrame | ||
}) { (_) in | ||
transitionContext.completeTransition(true) | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
AVStackController/Sources/BottomPopupPresentationController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// | ||
// BottomPopupPresentationController.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
|
||
import UIKit | ||
|
||
final class BottomPopupPresentationController: UIPresentationController { | ||
private var dimmingView: UIView! | ||
private unowned var attributesDelegate: BottomPopupAttributesDelegate | ||
|
||
override var frameOfPresentedViewInContainerView: CGRect { | ||
get { | ||
return CGRect(origin: CGPoint(x: 0, y: UIScreen.main.bounds.size.height - attributesDelegate.popupHeight), size: CGSize(width: presentedViewController.view.frame.size.width, height: attributesDelegate.popupHeight)) | ||
} | ||
} | ||
|
||
private func changeDimmingViewAlphaAlongWithAnimation(to alpha: CGFloat) { | ||
guard let coordinator = presentedViewController.transitionCoordinator else { | ||
dimmingView.backgroundColor = UIColor.black.withAlphaComponent(alpha) | ||
return | ||
} | ||
|
||
coordinator.animate(alongsideTransition: { _ in | ||
self.dimmingView.backgroundColor = UIColor.black.withAlphaComponent(alpha) | ||
}) | ||
} | ||
|
||
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, attributesDelegate: BottomPopupAttributesDelegate) { | ||
self.attributesDelegate = attributesDelegate | ||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController) | ||
setupDimmingView() | ||
} | ||
|
||
override func containerViewWillLayoutSubviews() { | ||
presentedView?.frame = frameOfPresentedViewInContainerView | ||
} | ||
|
||
override func presentationTransitionWillBegin() { | ||
containerView?.insertSubview(dimmingView, at: 0) | ||
changeDimmingViewAlphaAlongWithAnimation(to: attributesDelegate.popupDimmingViewAlpha) | ||
} | ||
|
||
override func dismissalTransitionWillBegin() { | ||
changeDimmingViewAlphaAlongWithAnimation(to: 0) | ||
} | ||
|
||
@objc private func handleTap(_ tap: UITapGestureRecognizer) { | ||
guard attributesDelegate.popupShouldBeganDismiss else { return } | ||
presentedViewController.dismiss(animated: true, completion: nil) | ||
} | ||
|
||
@objc private func handleSwipe(_ swipe: UISwipeGestureRecognizer) { | ||
guard attributesDelegate.popupShouldBeganDismiss else { return } | ||
presentedViewController.dismiss(animated: true, completion: nil) | ||
} | ||
} | ||
|
||
private extension BottomPopupPresentationController { | ||
func setupDimmingView() { | ||
dimmingView = UIView() | ||
dimmingView.frame = CGRect(origin: .zero, size: UIScreen.main.bounds.size) | ||
dimmingView.backgroundColor = UIColor.black.withAlphaComponent(0) | ||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) | ||
let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:))) | ||
swipeGesture.direction = [.down, .up] | ||
dimmingView.isUserInteractionEnabled = true | ||
[tapGesture, swipeGesture].forEach { dimmingView.addGestureRecognizer($0) } | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
AVStackController/Sources/BottomPopupTransitionHandler.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// | ||
// DraggableTransitioningDelegate.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
final class BottomPopupTransitionHandler: NSObject, UIViewControllerTransitioningDelegate { | ||
private let presentAnimator: BottomPopupPresentAnimator | ||
private let dismissAnimator: BottomPopupDismissAnimator | ||
private var interactionController: BottomPopupDismissInteractionController? | ||
private unowned var popupViewController: BottomPresentableViewController | ||
fileprivate weak var popupDelegate: BottomPopupDelegate? | ||
|
||
var isInteractiveDismissStarted = false | ||
|
||
init(popupViewController: BottomPresentableViewController) { | ||
self.popupViewController = popupViewController | ||
|
||
presentAnimator = BottomPopupPresentAnimator(attributesOwner: popupViewController) | ||
dismissAnimator = BottomPopupDismissAnimator(attributesOwner: popupViewController) | ||
} | ||
|
||
//MARK: Public | ||
func notifyViewLoaded(withPopupDelegate delegate: BottomPopupDelegate?) { | ||
self.popupDelegate = delegate | ||
if popupViewController.popupShouldDismissInteractivelty { | ||
interactionController = BottomPopupDismissInteractionController(presentedViewController: popupViewController, attributesDelegate: popupViewController) | ||
interactionController?.delegate = self | ||
} | ||
} | ||
|
||
//MARK: Specific animators | ||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { | ||
return BottomPopupPresentationController(presentedViewController: presented, presenting: presenting, attributesDelegate: popupViewController) | ||
} | ||
|
||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { | ||
return presentAnimator | ||
} | ||
|
||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { | ||
return dismissAnimator | ||
} | ||
|
||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { | ||
return isInteractiveDismissStarted ? interactionController : nil | ||
} | ||
} | ||
|
||
extension BottomPopupTransitionHandler: BottomPopupDismissInteractionControllerDelegate { | ||
func dismissInteractionPercentChanged(from oldValue: CGFloat, to newValue: CGFloat) { | ||
popupDelegate?.bottomPopupDismissInteractionPercentChanged(from: oldValue, to: newValue) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// | ||
// BottomPopupUtils.swift | ||
// CREDAssignment | ||
// | ||
// Created by Anand Verma on 10/08/20. | ||
// Copyright © 2020 CRED. All rights reserved. | ||
// | ||
|
||
|
||
import UIKit | ||
|
||
typealias BottomPresentableViewController = BottomPopupAttributesDelegate & UIViewController | ||
|
||
public protocol BottomPopupDelegate: class { | ||
func bottomPopupViewLoaded() | ||
func bottomPopupWillAppear() | ||
func bottomPopupDidAppear() | ||
func bottomPopupWillDismiss() | ||
func bottomPopupDidDismiss() | ||
func bottomPopupDismissInteractionPercentChanged(from oldValue: CGFloat, to newValue: CGFloat) | ||
} | ||
|
||
public extension BottomPopupDelegate { | ||
func bottomPopupViewLoaded() { } | ||
func bottomPopupWillAppear() { } | ||
func bottomPopupDidAppear() { } | ||
func bottomPopupWillDismiss() { } | ||
func bottomPopupDidDismiss() { } | ||
func bottomPopupDismissInteractionPercentChanged(from oldValue: CGFloat, to newValue: CGFloat) { } | ||
} | ||
|
||
public protocol BottomPopupAttributesDelegate: class { | ||
var popupHeight: CGFloat { get } | ||
var popupTopCornerRadius: CGFloat { get } | ||
var popupPresentDuration: Double { get } | ||
var popupDismissDuration: Double { get } | ||
var popupShouldDismissInteractivelty: Bool { get } | ||
var popupDimmingViewAlpha: CGFloat { get } | ||
var popupShouldBeganDismiss: Bool { get } | ||
var popupViewAccessibilityIdentifier: String { get } | ||
} | ||
|
||
public struct BottomPopupConstants { | ||
static let kDefaultHeight: CGFloat = 377.0 | ||
static let kDefaultTopCornerRadius: CGFloat = 10.0 | ||
static let kDefaultPresentDuration = 0.5 | ||
static let kDefaultDismissDuration = 0.5 | ||
static let dismissInteractively = true | ||
static let shouldBeganDismiss = true | ||
static let kDimmingViewDefaultAlphaValue: CGFloat = 0.5 | ||
static let defaultPopupViewAccessibilityIdentifier: String = "bottomPopupView" | ||
} |
Oops, something went wrong.