Last active
June 19, 2020 17:04
-
-
Save nekonora/54512bf5479db51b29df3b90d15ddf8b to your computer and use it in GitHub Desktop.
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
import Foundation | |
import UIKit | |
class ModalScreenVC: UIViewController { | |
// MARK: - Outlets | |
/// A subview filling this controller's view with desired top margin | |
@IBOutlet private var contentView: UIView! | |
// MARK: - Properties | |
private var contentViewInitialYOrigin: CGFloat = 0 | |
// MARK: - Lifecycle | |
override func viewWillAppear(_ animated: Bool) { | |
super.viewWillAppear(animated) | |
presentContent() | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
modalPresentationStyle = .overFullScreen | |
modalTransitionStyle = .crossDissolve | |
addGesture() | |
} | |
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { | |
dismissContent() | |
} | |
// MARK: - Actions | |
@objc private func onGestureCalled(_ gesture: UIPanGestureRecognizer) { | |
handleGesture(gesture) | |
} | |
@IBAction private func didTapClose(_ sender: UIButton) { | |
dismiss(animated: false) | |
} | |
} | |
private extension ModalScreenVC { | |
func presentContent() { | |
guard let presentingVC = presentingViewController else { return } | |
view.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.6) | |
contentView.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height) | |
presentingVC.view.layer.cornerRadius = 10 | |
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.3, options: [.curveEaseOut], animations: { | |
presentingVC.view.transform = CGAffineTransform(scaleX: 0.9, y: 0.9) | |
presentingVC.view.alpha = 0.4 | |
self.contentView.transform = .identity | |
}) { _ in | |
self.contentViewInitialYOrigin = self.contentView.frame.origin.y | |
} | |
} | |
func addGesture() { | |
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onGestureCalled(_:))) | |
contentView.addGestureRecognizer(panGesture) | |
} | |
func handleGesture(_ gesture: UIPanGestureRecognizer) { | |
switch gesture.state { | |
case .changed: | |
contentView.frame.origin.y = contentViewInitialYOrigin + (gesture.translation(in: view).y / 2) | |
case .cancelled, .ended, .failed: | |
if gesture.translation(in: view).y > UIScreen.main.bounds.height / 4 { | |
dismiss(animated: false) | |
} else { | |
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.3, options: [.curveEaseOut], animations: { | |
self.contentView.frame.origin.y = self.contentViewInitialYOrigin | |
}, completion: nil) | |
} | |
default: break | |
} | |
} | |
func dismissContent() { | |
guard let presentingVC = presentingViewController else { super.dismiss(animated: false, completion: nil); return } | |
UIView.animate(withDuration: 0.3, animations: { | |
self.contentView.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height * 1.5) | |
presentingVC.view.transform = .identity | |
presentingVC.view.alpha = 1 | |
self.view.backgroundColor = .clear | |
}) { _ in | |
super.dismiss(animated: false, completion: { presentingVC.setNeedsStatusBarAppearanceUpdate() }) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment