Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c36078cda2 | |||
| b78c82194f | |||
| a112473a04 | |||
| c1b135a30a | |||
| c1a7622a26 | |||
| c3421432b0 | |||
| 99353d3610 | |||
| 3bee6898e0 | |||
| 587790b1db | |||
| 2698cfe23d | |||
| 42b82886ca | |||
| 7af77b2596 | |||
| 7c663e7760 | |||
| f561e94110 | |||
| 3bf06ae2b3 | |||
| a6d5e6f97d | |||
| 0f20afb87c | |||
| 2368747150 | |||
| 32a010e1ca | |||
| fce0efaffa | |||
| 1500c272b1 | |||
| ba859e3646 | |||
| 01b8702c4a | |||
| ec809ee1c5 | |||
| a5f41ab3d6 | |||
| 58554f1449 | |||
| 8c1b6e2c8d | |||
| 724635e387 | |||
| dfc0fb8e52 | |||
| 50c9ab4104 | |||
| bb0821d2cc | |||
| ae29eba5b9 | |||
| 0c98c3c699 | |||
| 4ecc8e96d8 | |||
| fac454f41d | |||
| ae68a1be5d | |||
| c111a20c86 | |||
| cc1b1ac604 | |||
| 265f77dd5e | |||
| aacf153d0e | |||
| 5b8fdf6590 |
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -7,6 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
self.launch(rootViewController: Controller())
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -32,6 +33,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
func launch(rootViewController: UIViewController) {
|
||||
let frame = UIScreen.main.bounds
|
||||
self.window = UIWindow(frame: frame)
|
||||
self.window!.rootViewController = rootViewController
|
||||
self.window!.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,43 @@
|
||||
import UIKit
|
||||
|
||||
class Controller: UIViewController {
|
||||
|
||||
var presentControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
var presentTableControllerButton = UIButton.init(type: UIButton.ButtonType.system)
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.view.backgroundColor = UIColor.white
|
||||
|
||||
self.presentControllerButton.setTitle("Show ViewController", for: .normal)
|
||||
self.presentControllerButton.addTarget(self, action: #selector(self.presentModalViewController), for: .touchUpInside)
|
||||
self.presentControllerButton.sizeToFit()
|
||||
self.presentControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentControllerButton.center.y = self.view.frame.height / 4 * 3
|
||||
self.view.addSubview(self.presentControllerButton)
|
||||
|
||||
self.presentTableControllerButton.setTitle("Show TableController", for: .normal)
|
||||
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
|
||||
self.presentTableControllerButton.sizeToFit()
|
||||
self.presentTableControllerButton.center.x = self.view.frame.width / 2
|
||||
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomYPosition + 10
|
||||
self.view.addSubview(self.presentTableControllerButton)
|
||||
}
|
||||
|
||||
@objc func presentModalViewController() {
|
||||
let modal = ModalViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func presentModalTableViewController() {
|
||||
let modal = ModalTableViewController()
|
||||
let transitionDelegate = SPStorkTransitioningDelegate()
|
||||
modal.transitioningDelegate = transitionDelegate
|
||||
modal.modalPresentationStyle = .custom
|
||||
present(modal, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -23,7 +23,7 @@ import UIKit
|
||||
|
||||
public struct SPStorkController {
|
||||
|
||||
static func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let controller = self.controller(for: scrollView) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
|
||||
@@ -41,6 +41,18 @@ public struct SPStorkController {
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(parent controller: UIViewController) {
|
||||
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static public func updatePresentingController(modal controller: UIViewController) {
|
||||
if let presentationController = controller.presentationController as? SPStorkPresentationController {
|
||||
presentationController.updatePresentingController()
|
||||
}
|
||||
}
|
||||
|
||||
static private func controller(for view: UIView) -> UIViewController? {
|
||||
var nextResponder = view.next
|
||||
while nextResponder != nil && !(nextResponder! is UIViewController) {
|
||||
+35
-9
@@ -24,9 +24,13 @@ import UIKit
|
||||
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var isTapAroundToDismissEnabled: Bool = true
|
||||
var showIndicator: Bool = true
|
||||
var customHeight: CGFloat? = nil
|
||||
var transitioningDelegate: SPStorkTransitioningDelegate?
|
||||
|
||||
var pan: UIPanGestureRecognizer?
|
||||
var tap: UITapGestureRecognizer?
|
||||
|
||||
private var indicatorView = SPStorkIndicatorView()
|
||||
private var gradeView: UIView = UIView()
|
||||
@@ -55,13 +59,14 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
|
||||
private var scaleForPresentingView: CGFloat {
|
||||
guard let containerView = containerView else { return 0 }
|
||||
let factor = 1 - (topSpace * 2 / containerView.frame.height)
|
||||
let factor = 1 - (self.topSpace * 2 / containerView.frame.height)
|
||||
return factor
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return .zero }
|
||||
let yOffset: CGFloat = topSpace + 13
|
||||
let additionTranslate = containerView.bounds.height - (self.customHeight ?? containerView.bounds.height)
|
||||
let yOffset: CGFloat = self.topSpace + 13 + additionTranslate
|
||||
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
|
||||
}
|
||||
|
||||
@@ -82,6 +87,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
|
||||
self.snapshotViewContainer.frame = initialFrame
|
||||
self.updateSnapshot()
|
||||
self.snapshotView?.layer.cornerRadius = 0
|
||||
self.backgroundView.backgroundColor = UIColor.black
|
||||
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
|
||||
@@ -145,14 +151,21 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let containerView = containerView else { return }
|
||||
self.updateSnapshot()
|
||||
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
|
||||
self.snapshotViewContainer.transform = .identity
|
||||
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
|
||||
self.updateSnapshotAspectRatio()
|
||||
|
||||
if self.isTapAroundToDismissEnabled {
|
||||
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
|
||||
self.tap?.cancelsTouchesInView = false
|
||||
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
|
||||
}
|
||||
|
||||
if self.isSwipeToDismissEnabled {
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
|
||||
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
|
||||
self.pan!.delegate = self
|
||||
self.pan!.maximumNumberOfTouches = 1
|
||||
self.pan!.cancelsTouchesInView = false
|
||||
@@ -160,6 +173,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleTap() {
|
||||
self.presentedViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let containerView = containerView else { return }
|
||||
@@ -270,6 +287,7 @@ extension SPStorkPresentationController {
|
||||
animations: {
|
||||
self.snapshotView?.transform = .identity
|
||||
self.presentedView?.transform = .identity
|
||||
self.gradeView.alpha = self.alpha
|
||||
})
|
||||
}
|
||||
default:
|
||||
@@ -283,12 +301,17 @@ extension SPStorkPresentationController {
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentingController() {
|
||||
if self.startDismissing { return }
|
||||
self.updateSnapshot()
|
||||
}
|
||||
|
||||
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
|
||||
if self.startDismissing { return }
|
||||
|
||||
let elasticThreshold: CGFloat = 120
|
||||
let translationFactor: CGFloat = 1 / 2
|
||||
|
||||
|
||||
if translation >= 0 {
|
||||
let translationForModal: CGFloat = {
|
||||
if translation >= elasticThreshold {
|
||||
@@ -302,11 +325,9 @@ extension SPStorkPresentationController {
|
||||
|
||||
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
|
||||
|
||||
if !self.presentingViewController.isPresentedAsStork {
|
||||
let factor = 1 + (translationForModal / 6000)
|
||||
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
|
||||
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
|
||||
}
|
||||
let factor = 1 + (translationForModal / 6000)
|
||||
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
|
||||
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,6 +370,11 @@ extension SPStorkPresentationController {
|
||||
self.snapshotViewContainer.addSubview(currentSnapshotView)
|
||||
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
|
||||
self.snapshotView = currentSnapshotView
|
||||
self.snapshotView?.layer.cornerRadius = self.cornerRadius
|
||||
self.snapshotView?.layer.masksToBounds = true
|
||||
if #available(iOS 11.0, *) {
|
||||
snapshotView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
self.gradeView.removeFromSuperview()
|
||||
self.gradeView.backgroundColor = UIColor.black
|
||||
self.snapshotView!.addSubview(self.gradeView)
|
||||
+8
-2
@@ -23,11 +23,17 @@ import UIKit
|
||||
|
||||
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
var isSwipeToDismissEnabled: Bool = true
|
||||
var showIndicator: Bool = true
|
||||
public var isSwipeToDismissEnabled: Bool = true
|
||||
public var isTapAroundToDismissEnabled: Bool = true
|
||||
public var showIndicator: Bool = true
|
||||
public var customHeight: CGFloat? = nil
|
||||
|
||||
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
controller.isSwipeToDismissEnabled = self.isSwipeToDismissEnabled
|
||||
controller.isTapAroundToDismissEnabled = self.isTapAroundToDismissEnabled
|
||||
controller.showIndicator = self.showIndicator
|
||||
controller.customHeight = self.customHeight
|
||||
controller.transitioningDelegate = self
|
||||
return controller
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// The MIT License (MIT)
|
||||
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
|
||||
//
|
||||
// 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 UIKit
|
||||
import LocalAuthentication
|
||||
|
||||
struct SPLocalAuthentication {
|
||||
|
||||
static var isEnable: Bool {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func request(reason: String, complecton: @escaping (Bool)->()) {
|
||||
let context = LAContext()
|
||||
var error: NSError?
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
|
||||
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
|
||||
DispatchQueue.main.async { complecton(success) }
|
||||
}
|
||||
} else {
|
||||
complecton(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
+3
@@ -30,6 +30,7 @@ struct SPDownloader {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
URLSession.shared.dataTask(with: url) { (data, response, error) in
|
||||
guard
|
||||
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
|
||||
@@ -47,6 +48,8 @@ struct SPDownloader {
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -55,12 +55,10 @@ extension UIViewController {
|
||||
extension UIViewController {
|
||||
|
||||
func save(image: UIImage) {
|
||||
if SPPermission.isAllow(.photoLibrary) {
|
||||
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
|
||||
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
|
||||
} else {
|
||||
SPPermission.request(.photoLibrary) {
|
||||
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
|
||||
}
|
||||
print("Saving image error. Not allowed permission")
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -21,7 +21,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class SPLayout {
|
||||
struct SPLayout {
|
||||
|
||||
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
|
||||
|
||||
@@ -39,4 +39,6 @@ class SPLayout {
|
||||
}
|
||||
return CGSize.init(width: prepareWidth, height: prepareHeight)
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
-6
@@ -33,12 +33,6 @@ public extension String {
|
||||
}
|
||||
}
|
||||
|
||||
public extension Bool {
|
||||
public static func random() -> Bool {
|
||||
return arc4random_uniform(2) == 0
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
public static func random(_ n: Int) -> Int {
|
||||
return Int(arc4random_uniform(UInt32(n)))
|
||||
+2
@@ -57,4 +57,6 @@ struct SPSpotlight {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
}
|
||||
-44
@@ -63,50 +63,6 @@ public enum SPSeparatorInsetStyle {
|
||||
case auto
|
||||
}
|
||||
|
||||
@objc public enum SPPermissionType: Int {
|
||||
case camera = 0
|
||||
case photoLibrary = 1
|
||||
case notification = 2
|
||||
case microphone = 3
|
||||
case calendar = 4
|
||||
case contacts = 5
|
||||
case reminders = 6
|
||||
case speech = 7
|
||||
case locationAlways = 8
|
||||
case locationWhenInUse = 9
|
||||
case locationWithBackground = 10
|
||||
case mediaLibrary = 11
|
||||
|
||||
var name: String {
|
||||
switch self {
|
||||
case .camera:
|
||||
return "Camera"
|
||||
case .photoLibrary:
|
||||
return "Photo Library"
|
||||
case .notification:
|
||||
return "Notification"
|
||||
case .microphone:
|
||||
return "Microphone"
|
||||
case .calendar:
|
||||
return "Calendar"
|
||||
case .contacts:
|
||||
return "Contacts"
|
||||
case .reminders:
|
||||
return "Reminders"
|
||||
case .speech:
|
||||
return "Speech"
|
||||
case .locationAlways:
|
||||
return "Location"
|
||||
case .locationWhenInUse:
|
||||
return "Location"
|
||||
case .locationWithBackground:
|
||||
return "Location"
|
||||
case .mediaLibrary:
|
||||
return "Media Library"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SPNavigationTitleStyle {
|
||||
case large
|
||||
case small
|
||||
-1
@@ -76,5 +76,4 @@ class SPSystemIconButton: UIButton {
|
||||
super.layoutSubviews()
|
||||
self.iconView.setEqualsFrameFromBounds(self, withWidthFactor: self.widthIconFactor, withHeightFactor: self.heightIconFactor, withCentering: true)
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user