Compare commits

..

10 Commits

Author SHA1 Message Date
Ivan Vorobei 84256152dc Update to 1.6
Update duration animation for show / hide indicator view when scrolling.
2019-03-26 14:19:27 +03:00
Ivan Vorobei ac366c84fd Update to 1.5.8 2019-03-25 23:30:30 +03:00
Ivan Vorobei 64bebe0645 Update to 1.5.7
Add `closeButton` and parametres.
2019-03-25 15:42:10 +03:00
Ivan Vorobei 1efa55aefb Update to 1.5.6
Add events delegate
2019-03-24 18:57:27 +03:00
Ivan Vorobei e5371ca20c Update to 1.5.5
Add new parameter `hideIndicatorWhenScroll` - it allow shows and hide indicator when scrolling. Also fixed bug when invalid frame for dismissing action.
2019-03-24 13:23:08 +03:00
Ivan Vorobei fdbc1e3527 Update README.md 2019-03-23 23:28:22 +03:00
Ivan Vorobei 306547671c Update README.md 2019-03-23 19:46:22 +03:00
Ivan Vorobei 1fede9eb64 Update to 1.5.4
Fix bug with dublicate indicator
2019-03-07 11:34:01 +03:00
Ivan Vorobei 3ef2ff81bc Update README.md 2019-03-04 15:34:07 +03:00
Ivan Vorobei 83330bb509 Update to 1.5.2 2019-03-03 18:48:30 +03:00
19 changed files with 406 additions and 85 deletions
@@ -7,6 +7,13 @@
objects = {
/* Begin PBXBuildFile section */
F40DB702222A9202004E1CCB /* SPStorkViewControllerExtenshion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB6FB222A9202004E1CCB /* SPStorkViewControllerExtenshion.swift */; };
F40DB703222A9202004E1CCB /* SPStorkTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB6FC222A9202004E1CCB /* SPStorkTransitioningDelegate.swift */; };
F40DB704222A9202004E1CCB /* SPStorkPresentingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB6FD222A9202004E1CCB /* SPStorkPresentingAnimationController.swift */; };
F40DB705222A9202004E1CCB /* SPStorkPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB6FE222A9202004E1CCB /* SPStorkPresentationController.swift */; };
F40DB706222A9202004E1CCB /* SPStorkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB6FF222A9202004E1CCB /* SPStorkController.swift */; };
F40DB707222A9202004E1CCB /* SPStorkIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB700222A9202004E1CCB /* SPStorkIndicatorView.swift */; };
F40DB708222A9202004E1CCB /* SPStorkDismissingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40DB701222A9202004E1CCB /* SPStorkDismissingAnimationController.swift */; };
F445CA8721AED92600184254 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F445CA8621AED92600184254 /* AppDelegate.swift */; };
F445CA8921AED92600184254 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = F445CA8821AED92600184254 /* Controller.swift */; };
F445CA8E21AED92700184254 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F445CA8D21AED92700184254 /* Assets.xcassets */; };
@@ -131,16 +138,16 @@
F455279E221C009E00F40446 /* SPShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4552721221C009D00F40446 /* SPShare.swift */; };
F459D75B21C3AB9A00798524 /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F459D75A21C3AB9A00798524 /* ModalViewController.swift */; };
F459D75D21C3ABBD00798524 /* ModalTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F459D75C21C3ABBD00798524 /* ModalTableViewController.swift */; };
F4A9E9D4221C6DBA0031492C /* SPStorkViewControllerExtenshion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9CD221C6DBA0031492C /* SPStorkViewControllerExtenshion.swift */; };
F4A9E9D5221C6DBA0031492C /* SPStorkTransitioningDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9CE221C6DBA0031492C /* SPStorkTransitioningDelegate.swift */; };
F4A9E9D6221C6DBA0031492C /* SPStorkPresentingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9CF221C6DBA0031492C /* SPStorkPresentingAnimationController.swift */; };
F4A9E9D7221C6DBA0031492C /* SPStorkPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9D0221C6DBA0031492C /* SPStorkPresentationController.swift */; };
F4A9E9D8221C6DBA0031492C /* SPStorkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9D1221C6DBA0031492C /* SPStorkController.swift */; };
F4A9E9D9221C6DBA0031492C /* SPStorkIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9D2221C6DBA0031492C /* SPStorkIndicatorView.swift */; };
F4A9E9DA221C6DBA0031492C /* SPStorkDismissingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A9E9D3221C6DBA0031492C /* SPStorkDismissingAnimationController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
F40DB6FB222A9202004E1CCB /* SPStorkViewControllerExtenshion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkViewControllerExtenshion.swift; sourceTree = "<group>"; };
F40DB6FC222A9202004E1CCB /* SPStorkTransitioningDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkTransitioningDelegate.swift; sourceTree = "<group>"; };
F40DB6FD222A9202004E1CCB /* SPStorkPresentingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkPresentingAnimationController.swift; sourceTree = "<group>"; };
F40DB6FE222A9202004E1CCB /* SPStorkPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkPresentationController.swift; sourceTree = "<group>"; };
F40DB6FF222A9202004E1CCB /* SPStorkController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkController.swift; sourceTree = "<group>"; };
F40DB700222A9202004E1CCB /* SPStorkIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkIndicatorView.swift; sourceTree = "<group>"; };
F40DB701222A9202004E1CCB /* SPStorkDismissingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkDismissingAnimationController.swift; sourceTree = "<group>"; };
F445CA8321AED92600184254 /* stork-controller.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "stork-controller.app"; sourceTree = BUILT_PRODUCTS_DIR; };
F445CA8621AED92600184254 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F445CA8821AED92600184254 /* Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = "<group>"; };
@@ -267,13 +274,6 @@
F4552721221C009D00F40446 /* SPShare.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPShare.swift; sourceTree = "<group>"; };
F459D75A21C3AB9A00798524 /* ModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalViewController.swift; sourceTree = "<group>"; };
F459D75C21C3ABBD00798524 /* ModalTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalTableViewController.swift; sourceTree = "<group>"; };
F4A9E9CD221C6DBA0031492C /* SPStorkViewControllerExtenshion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkViewControllerExtenshion.swift; sourceTree = "<group>"; };
F4A9E9CE221C6DBA0031492C /* SPStorkTransitioningDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkTransitioningDelegate.swift; sourceTree = "<group>"; };
F4A9E9CF221C6DBA0031492C /* SPStorkPresentingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkPresentingAnimationController.swift; sourceTree = "<group>"; };
F4A9E9D0221C6DBA0031492C /* SPStorkPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkPresentationController.swift; sourceTree = "<group>"; };
F4A9E9D1221C6DBA0031492C /* SPStorkController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkController.swift; sourceTree = "<group>"; };
F4A9E9D2221C6DBA0031492C /* SPStorkIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkIndicatorView.swift; sourceTree = "<group>"; };
F4A9E9D3221C6DBA0031492C /* SPStorkDismissingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPStorkDismissingAnimationController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -287,6 +287,20 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
F40DB6FA222A9202004E1CCB /* SPStorkController */ = {
isa = PBXGroup;
children = (
F40DB6FB222A9202004E1CCB /* SPStorkViewControllerExtenshion.swift */,
F40DB6FC222A9202004E1CCB /* SPStorkTransitioningDelegate.swift */,
F40DB6FD222A9202004E1CCB /* SPStorkPresentingAnimationController.swift */,
F40DB6FE222A9202004E1CCB /* SPStorkPresentationController.swift */,
F40DB6FF222A9202004E1CCB /* SPStorkController.swift */,
F40DB700222A9202004E1CCB /* SPStorkIndicatorView.swift */,
F40DB701222A9202004E1CCB /* SPStorkDismissingAnimationController.swift */,
);
path = SPStorkController;
sourceTree = "<group>";
};
F445CA7A21AED92600184254 = {
isa = PBXGroup;
children = (
@@ -322,7 +336,7 @@
F445CA9821AED98500184254 /* Frameworks */ = {
isa = PBXGroup;
children = (
F4A9E9CC221C6DBA0031492C /* SPStorkController */,
F40DB6FA222A9202004E1CCB /* SPStorkController */,
F4552681221C009D00F40446 /* SparrowKit */,
);
path = Frameworks;
@@ -789,20 +803,6 @@
path = Share;
sourceTree = "<group>";
};
F4A9E9CC221C6DBA0031492C /* SPStorkController */ = {
isa = PBXGroup;
children = (
F4A9E9CD221C6DBA0031492C /* SPStorkViewControllerExtenshion.swift */,
F4A9E9CE221C6DBA0031492C /* SPStorkTransitioningDelegate.swift */,
F4A9E9CF221C6DBA0031492C /* SPStorkPresentingAnimationController.swift */,
F4A9E9D0221C6DBA0031492C /* SPStorkPresentationController.swift */,
F4A9E9D1221C6DBA0031492C /* SPStorkController.swift */,
F4A9E9D2221C6DBA0031492C /* SPStorkIndicatorView.swift */,
F4A9E9D3221C6DBA0031492C /* SPStorkDismissingAnimationController.swift */,
);
path = SPStorkController;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -882,11 +882,11 @@
F455277E221C009E00F40446 /* SPAppBadge.swift in Sources */,
F4552756221C009E00F40446 /* SPGolubevIconView.swift in Sources */,
F4552752221C009E00F40446 /* SPCenterLabelsView.swift in Sources */,
F4A9E9D4221C6DBA0031492C /* SPStorkViewControllerExtenshion.swift in Sources */,
F40DB702222A9202004E1CCB /* SPStorkViewControllerExtenshion.swift in Sources */,
F4552776221C009E00F40446 /* SPUILabelExtenshion.swift in Sources */,
F445CA8921AED92600184254 /* Controller.swift in Sources */,
F4552775221C009E00F40446 /* SPCGRectExtension.swift in Sources */,
F4A9E9D8221C6DBA0031492C /* SPStorkController.swift in Sources */,
F40DB706222A9202004E1CCB /* SPStorkController.swift in Sources */,
F4552779221C009E00F40446 /* SPUIImageViewExtenshion.swift in Sources */,
F455278B221C009E00F40446 /* SPAnimationAlpha.swift in Sources */,
F455273A221C009E00F40446 /* SPAppleMusicSectionButtonsView.swift in Sources */,
@@ -900,9 +900,9 @@
F455273F221C009E00F40446 /* SPButton.swift in Sources */,
F4552759221C009E00F40446 /* SPTableController.swift in Sources */,
F4552786221C009E00F40446 /* SPLayout.swift in Sources */,
F4A9E9DA221C6DBA0031492C /* SPStorkDismissingAnimationController.swift in Sources */,
F40DB708222A9202004E1CCB /* SPStorkDismissingAnimationController.swift in Sources */,
F455279E221C009E00F40446 /* SPShare.swift in Sources */,
F4A9E9D5221C6DBA0031492C /* SPStorkTransitioningDelegate.swift in Sources */,
F40DB703222A9202004E1CCB /* SPStorkTransitioningDelegate.swift in Sources */,
F4552774221C009E00F40446 /* SPUINavigationControllerExtenshion.swift in Sources */,
F455275B221C009E00F40446 /* SPController.swift in Sources */,
F455279A221C009E00F40446 /* SPGolubevGolubevIconPack.swift in Sources */,
@@ -925,7 +925,7 @@
F455276D221C009E00F40446 /* SPUIViewControllerExtenshion.swift in Sources */,
F4552736221C009D00F40446 /* SPTableViewCell.swift in Sources */,
F459D75D21C3ABBD00798524 /* ModalTableViewController.swift in Sources */,
F4A9E9D7221C6DBA0031492C /* SPStorkPresentationController.swift in Sources */,
F40DB705222A9202004E1CCB /* SPStorkPresentationController.swift in Sources */,
F4552798221C009E00F40446 /* SPCodeSystemIconPack.swift in Sources */,
F4552769221C009E00F40446 /* SPUITableView.swift in Sources */,
F455277A221C009E00F40446 /* SPUIColorExtension.swift in Sources */,
@@ -958,7 +958,7 @@
F455274B221C009E00F40446 /* SPGradientView.swift in Sources */,
F4552747221C009E00F40446 /* SPTextView.swift in Sources */,
F455275C221C009E00F40446 /* SPCollectionViewCell.swift in Sources */,
F4A9E9D6221C6DBA0031492C /* SPStorkPresentingAnimationController.swift in Sources */,
F40DB704222A9202004E1CCB /* SPStorkPresentingAnimationController.swift in Sources */,
F4552764221C009E00F40446 /* SPUIAlertControllerExtenshion.swift in Sources */,
F4552787221C009E00F40446 /* SPShadow.swift in Sources */,
F4552740221C009E00F40446 /* SPAppStoreActionButton.swift in Sources */,
@@ -973,7 +973,7 @@
F459D75B21C3AB9A00798524 /* ModalViewController.swift in Sources */,
F4552771221C009E00F40446 /* SPUICollectionViewExtenshion.swift in Sources */,
F455277D221C009E00F40446 /* SPApp.swift in Sources */,
F4A9E9D9221C6DBA0031492C /* SPStorkIndicatorView.swift in Sources */,
F40DB707222A9202004E1CCB /* SPStorkIndicatorView.swift in Sources */,
F455274E221C009E00F40446 /* SPBlurView.swift in Sources */,
F4552784221C009E00F40446 /* SPLocalNotification.swift in Sources */,
F4552757221C009E00F40446 /* SPProposeController.swift in Sources */,
@@ -28,26 +28,20 @@ public struct SPStorkController {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
if translation >= 0 {
if controller.isBeingPresented { return }
scrollView.subviews.forEach {
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
}
presentationController.setIndicator(style: scrollView.isDragging ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.65 {
if !scrollView.isDragging {
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.presentedViewController.dismiss(animated: true, completion: nil)
return
}
}
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
presentationController.scrollViewDidScroll(translation)
presentationController.scrollViewDidScroll(translation * 2)
}
} else {
presentationController.setIndicator(style: .arrow)
presentationController.scrollViewDidScroll(0)
@@ -56,6 +50,10 @@ public struct SPStorkController {
}
}
static var topScrollIndicatorInset: CGFloat {
return 6
}
static public func updatePresentingController(parent controller: UIViewController) {
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
presentationController.updatePresentingController()
@@ -28,7 +28,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
var showIndicator: Bool = true
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
var customHeight: CGFloat? = nil
var translateForDismiss: CGFloat = 240
var translateForDismiss: CGFloat = 200
var transitioningDelegate: SPStorkTransitioningDelegate?
@@ -44,6 +44,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
private var snapshotViewTopConstraint: NSLayoutConstraint?
private var snapshotViewWidthConstraint: NSLayoutConstraint?
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
private var workGester: Bool = false
private var startDismissing: Bool = false
@@ -52,13 +53,8 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
return (statusBarHeight < 25) ? 30 : statusBarHeight
}
private var alpha: CGFloat {
return 0.51
}
private var cornerRadius: CGFloat {
return 10
}
private let alpha: CGFloat = 0.51
var cornerRadius: CGFloat = 10
private var scaleForPresentingView: CGFloat {
guard let containerView = containerView else { return 0 }
@@ -348,9 +344,12 @@ extension SPStorkPresentationController {
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
let factor = 1 + (translationForModal / 6000)
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
let scaleFactor = 1 + (translationForModal / 5000)
self.snapshotView?.transform = CGAffineTransform.init(scaleX: scaleFactor, y: scaleFactor)
let gradeFactor = 1 + (translationForModal / 7000)
self.gradeView.alpha = self.alpha - ((gradeFactor - 1) * 15)
} else {
self.presentedView?.transform = CGAffineTransform.identity
}
}
}
@@ -28,7 +28,8 @@ public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTrans
public var showIndicator: Bool = true
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
public var customHeight: CGFloat? = nil
public var translateForDismiss: CGFloat = 240
public var translateForDismiss: CGFloat = 200
public var cornerRadius: CGFloat = 10
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
@@ -38,6 +39,7 @@ public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTrans
controller.indicatorColor = self.indicatorColor
controller.customHeight = self.customHeight
controller.translateForDismiss = self.translateForDismiss
controller.cornerRadius = self.cornerRadius
controller.transitioningDelegate = self
return controller
}
@@ -29,9 +29,10 @@ extension UIViewController {
&& presentingViewController != nil
}
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil, complection: (() -> Void)? = nil) {
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil, showIndicator: Bool = false, complection: (() -> Void)? = nil) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
transitionDelegate.showIndicator = showIndicator
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
+21 -10
View File
@@ -6,7 +6,7 @@ Preview GIF is loading `3mb`. Please, wait.
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Preview.gif" width="500">
You can download example [from AppStore](https://itunes.apple.com/app/id1446635818). Also in the app you can donate me a cup of coffee. If you want to buy source code of the app shown on the GIF above, please go to [xcode-shop.com](https://xcode-shop.com). Price: $200.
You can download example [from AppStore](https://itunes.apple.com/app/id1446635818) or see [video preview](https://xcode-shop.com/assets/preview/debts.mov). If you want to buy source code of the full app (plus app for Apple Watch), please go to [xcode-shop.com](https://xcode-shop.com). Price: $200 only GitHub users!
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Shop.svg"/>
@@ -109,6 +109,10 @@ transitionDelegate.translateForDismiss = 100
```swift
transitionDelegate.tapAroundToDismissEnabled = true
```
- Parameter `showCloseButton` added circle button with dismiss action. Default is `false`:
```swift
transitionDelegate.showCloseButton = false
```
- Parameter `showIndicator` shows or hides top arrow indicator. Default is `true`:
```swift
@@ -120,6 +124,11 @@ transitionDelegate.showIndicator = true
transitionDelegate.indicatorColor = UIColor.white
```
- Parameter `hideIndicatorWhenScroll` shows or hides indicator when scrolling. Default is `false`:
```swift
transitionDelegate.hideIndicatorWhenScroll = true
```
- Parameter `cornerRadius` for customize corner radius of controller's view. Default is `10`:
```swift
transitionDelegate.cornerRadius = 10
@@ -140,6 +149,7 @@ You may want to add a navigation bar to your modal controller. Since it became i
```swift
import UIKit
import SPFakeBar
class ModalController: UIViewController {
@@ -163,10 +173,10 @@ You only need to add a navigation bar to the main view, it will automatically la
<img src="https://github.com/IvanVorobei/SPStorkController/blob/master/Resources/Navigation%20Bar.jpg"/>
To use `SPFakeBarView` you need to install [SparrowKit](https://github.com/IvanVorobei/SparrowKit) pod:
To use `SPFakeBarView` you need to install [SPFakeBar](https://github.com/IvanVorobei/SPFakeBar) pod:
```ruby
pod 'SparrowKit'
pod 'SPFakeBar'
```
### Working with UIScrollView
@@ -192,6 +202,14 @@ tableView.scrollIndicatorInsets.top = self.navBar.height
Please, also use `SPStorkController.scrollViewDidScroll` function in scroll delegate for more interactiveness with your collection or table view.
### Delegate
You can check events by implement `SPStorkControllerDelegate` and set delegate for `transitionDelegate`:
```swift
transitionDelegate.storkDelegate = self
```
### Modal presentation of different controller
If you want to present modal controller on SPStorkController, please set:
@@ -211,13 +229,6 @@ Project [SPPermission](https://github.com/IvanVorobei/SPPermission) for managing
<img src="https://github.com/IvanVorobei/SPPermission/blob/master/Resources/Preview.gif" width="500">
### SparrowKit
`SPStorkController` was formerly a part of [SparrowKit](https://github.com/IvanVorobei/SparrowKit) library. In the library you can find many useful extensions & classes. To install via CocoaPods use:
```ruby
pod 'SparrowKit'
```
## License
`SPStorkController` is released under the MIT license. Check `LICENSE.md` for details.
+5 -4
View File
@@ -1,6 +1,7 @@
Pod::Spec.new do |s|
s.name = "SPStorkController"
s.version = "1.5.1"
s.version = "1.6"
s.summary = "Modal controller as mail or Apple music application"
s.homepage = "https://github.com/IvanVorobei/SPStorkController"
s.source = { :git => "https://github.com/IvanVorobei/SPStorkController.git", :tag => s.version }
@@ -8,11 +9,11 @@ Pod::Spec.new do |s|
s.author = { "Ivan Vorobei" => "hello@ivanvorobei.by" }
s.swift_version = '4.2'
s.swift_version = '4.2', '5.0'
s.platform = :ios
s.ios.framework = 'UIKit'
s.ios.deployment_target = "10.0"
s.source_files = "Source/SPStorkController/**/*.swift"
end
end
@@ -0,0 +1,88 @@
import UIKit
class SPStorkCodeDraw : NSObject {
private struct Cache {
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
}
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 92.02, y: 22.92))
bezierPath.addLine(to: CGPoint(x: 64.42, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 92.02, y: 78.13))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 92.99), controlPoint1: CGPoint(x: 96.13, y: 82.23), controlPoint2: CGPoint(x: 96.13, y: 88.89))
bezierPath.addCurve(to: CGPoint(x: 84.59, y: 96.07), controlPoint1: CGPoint(x: 89.97, y: 95.05), controlPoint2: CGPoint(x: 87.28, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 77.16, y: 92.99), controlPoint1: CGPoint(x: 81.9, y: 96.07), controlPoint2: CGPoint(x: 79.22, y: 95.05))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 65.38))
bezierPath.addLine(to: CGPoint(x: 21.95, y: 92.99))
bezierPath.addCurve(to: CGPoint(x: 14.51, y: 96.07), controlPoint1: CGPoint(x: 19.89, y: 95.05), controlPoint2: CGPoint(x: 17.2, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 92.99), controlPoint1: CGPoint(x: 11.82, y: 96.07), controlPoint2: CGPoint(x: 9.13, y: 95.05))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 78.13), controlPoint1: CGPoint(x: 2.97, y: 88.89), controlPoint2: CGPoint(x: 2.97, y: 82.23))
bezierPath.addLine(to: CGPoint(x: 34.69, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 7.08, y: 22.92))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 8.04), controlPoint1: CGPoint(x: 2.97, y: 18.8), controlPoint2: CGPoint(x: 2.97, y: 12.15))
bezierPath.addCurve(to: CGPoint(x: 21.94, y: 8.04), controlPoint1: CGPoint(x: 11.18, y: 3.94), controlPoint2: CGPoint(x: 17.84, y: 3.94))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 35.65))
bezierPath.addLine(to: CGPoint(x: 77.16, y: 8.04))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 8.04), controlPoint1: CGPoint(x: 81.26, y: 3.94), controlPoint2: CGPoint(x: 87.92, y: 3.94))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 22.92), controlPoint1: CGPoint(x: 96.13, y: 12.15), controlPoint2: CGPoint(x: 96.13, y: 18.8))
bezierPath.close()
color.setFill()
bezierPath.fill()
context.restoreGState()
}
@objc(StyleKitNameResizingBehavior)
enum ResizingBehavior: Int {
case aspectFit
case aspectFill
case stretch
case center
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
var scales = CGSize.zero
scales.width = abs(target.width / rect.width)
scales.height = abs(target.height / rect.height)
switch self {
case .aspectFit:
scales.width = min(scales.width, scales.height)
scales.height = scales.width
case .aspectFill:
scales.width = max(scales.width, scales.height)
scales.height = scales.width
case .stretch:
break
case .center:
scales.width = 1
scales.height = 1
}
var result = rect.standardized
result.size.width *= scales.width
result.size.height *= scales.height
result.origin.x = target.minX + (target.width - result.width) / 2
result.origin.y = target.minY + (target.height - result.height) / 2
return result
}
}
private override init() {}
}
@@ -29,10 +29,21 @@ extension UIViewController {
&& presentingViewController != nil
}
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil, showIndicator: Bool = false, complection: (() -> Void)? = nil) {
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
self.present(controller, animated: true, completion: nil)
}
public func presentAsStork(_ controller: UIViewController, height: CGFloat?, showIndicator: Bool, hideIndicatorWhenScroll: Bool, showCloseButton: Bool, complection: (() -> Void)?) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
transitionDelegate.showCloseButton = showCloseButton
transitionDelegate.showIndicator = showIndicator
transitionDelegate.hideIndicatorWhenScroll = hideIndicatorWhenScroll
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
@@ -0,0 +1,29 @@
// 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
@objc public protocol SPStorkControllerDelegate: class {
@objc optional func didDismissStorkBySwipe()
@objc optional func didDismissStorkByTap()
}
@@ -35,7 +35,9 @@ public struct SPStorkController {
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.presentedViewController.dismiss(animated: true, completion: nil)
presentationController.presentedViewController.dismiss(animated: true, completion: {
presentationController.storkDelegate?.didDismissStorkBySwipe?()
})
return
}
}
@@ -46,6 +48,12 @@ public struct SPStorkController {
presentationController.setIndicator(style: .arrow)
presentationController.scrollViewDidScroll(0)
}
if translation < -15 {
presentationController.setIndicator(visible: false)
} else {
presentationController.setIndicator(visible: true)
}
}
}
}
@@ -29,8 +29,10 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
return
}
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
let containerView = transitionContext.containerView
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: finalFrameForPresentedView.width, height: finalFrameForPresentedView.height)
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
@@ -25,16 +25,20 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
var swipeToDismissEnabled: Bool = true
var tapAroundToDismissEnabled: Bool = true
var showCloseButton: Bool = false
var showIndicator: Bool = true
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
var hideIndicatorWhenScroll: Bool = false
var customHeight: CGFloat? = nil
var translateForDismiss: CGFloat = 200
var transitioningDelegate: SPStorkTransitioningDelegate?
weak var storkDelegate: SPStorkControllerDelegate?
var pan: UIPanGestureRecognizer?
var tap: UITapGestureRecognizer?
private var closeButton = SPStorkCloseButton()
private var indicatorView = SPStorkIndicatorView()
private var gradeView: UIView = UIView()
private let snapshotViewContainer = UIView()
@@ -64,15 +68,18 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else { return .zero }
let baseY: CGFloat = self.topSpace + 13
let maxHeight: CGFloat = containerView.bounds.height - baseY
var height: CGFloat = maxHeight
var customHeight = self.customHeight ?? containerView.bounds.height
if customHeight > containerView.bounds.height {
customHeight = containerView.bounds.height
print("SPStorkController - Custom height change to default value. Your height more maximum value")
if let customHeight = self.customHeight {
if customHeight < maxHeight {
height = customHeight
} else {
print("SPStorkController - Custom height change to default value. Your height more maximum value")
}
}
let additionTranslate = containerView.bounds.height - customHeight
let yOffset: CGFloat = self.topSpace + 13 + additionTranslate
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
return CGRect(x: 0, y: containerView.bounds.height - height, width: containerView.bounds.width, height: height)
}
override func presentationTransitionWillBegin() {
@@ -91,6 +98,12 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
presentedView.addSubview(self.closeButton)
}
self.updateLayoutCloseButton()
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
@@ -186,7 +199,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
@objc func dismissAction() {
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
self.presentedViewController.dismiss(animated: true, completion: nil)
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
override func dismissalTransitionWillBegin() {
@@ -258,6 +273,8 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
self.backgroundView.removeFromSuperview()
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.removeFromSuperview()
self.indicatorView.removeFromSuperview()
self.closeButton.removeFromSuperview()
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
presentedViewController.view.frame = offscreenFrame
@@ -290,7 +307,9 @@ extension SPStorkPresentationController {
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
if translation >= self.translateForDismiss {
self.presentedViewController.dismiss(animated: true, completion: nil)
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkBySwipe?()
})
} else {
self.indicatorView.style = .arrow
UIView.animate(
@@ -325,6 +344,17 @@ extension SPStorkPresentationController {
self.indicatorView.style = style
}
func setIndicator(visible: Bool) {
guard self.hideIndicatorWhenScroll else { return }
let newAlpha: CGFloat = visible ? 1 : 0
if self.indicatorView.alpha == newAlpha {
return
}
UIView.animate(withDuration: 0.18, animations: {
self.indicatorView.alpha = newAlpha
})
}
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
if self.startDismissing { return }
@@ -372,6 +402,7 @@ extension SPStorkPresentationController {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { contex in
self.updateLayoutIndicator()
self.updateLayoutCloseButton()
}, completion: { [weak self] _ in
self?.updateSnapshotAspectRatio()
self?.updateSnapshot()
@@ -386,6 +417,12 @@ extension SPStorkPresentationController {
self.indicatorView.center.x = presentedView.frame.width / 2
}
private func updateLayoutCloseButton() {
guard let presentedView = self.presentedView else { return }
self.closeButton.sizeToFit()
self.closeButton.layout(bottomX: presentedView.frame.width - 19, y: 19)
}
private func updateSnapshot() {
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
self.snapshotView?.removeFromSuperview()
@@ -25,22 +25,28 @@ public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTrans
public var swipeToDismissEnabled: Bool = true
public var tapAroundToDismissEnabled: Bool = true
public var showCloseButton: Bool = false
public var showIndicator: Bool = true
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
public var hideIndicatorWhenScroll: Bool = false
public var customHeight: CGFloat? = nil
public var translateForDismiss: CGFloat = 200
public var cornerRadius: CGFloat = 10
public weak var storkDelegate: SPStorkControllerDelegate? = nil
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
controller.swipeToDismissEnabled = self.swipeToDismissEnabled
controller.tapAroundToDismissEnabled = self.tapAroundToDismissEnabled
controller.showCloseButton = self.showCloseButton
controller.showIndicator = self.showIndicator
controller.indicatorColor = self.indicatorColor
controller.hideIndicatorWhenScroll = self.hideIndicatorWhenScroll
controller.customHeight = self.customHeight
controller.translateForDismiss = self.translateForDismiss
controller.cornerRadius = self.cornerRadius
controller.transitioningDelegate = self
controller.storkDelegate = self.storkDelegate
return controller
}
@@ -0,0 +1,78 @@
// 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
open class SPStorkCloseButton: UIButton {
let iconView = SPStorkCloseView()
var widthIconFactor: CGFloat = 1
var heightIconFactor: CGFloat = 1
var color = UIColor.blue {
didSet {
self.iconView.color = self.color
}
}
override open var isHighlighted: Bool {
didSet {
self.iconView.color = self.color.withAlphaComponent(self.isHighlighted ? 0.7 : 1)
}
}
init() {
super.init(frame: .zero)
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func commonInit() {
self.iconView.isUserInteractionEnabled = false
self.addSubview(self.iconView)
self.backgroundColor = UIColor.init(red: 239 / 255.0, green: 239 / 255.0, blue: 244 / 255.0, alpha: 1)
self.color = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 147 / 255.0, alpha: 1)
self.widthIconFactor = 0.36
self.heightIconFactor = 0.36
}
func layout(bottomX: CGFloat, y: CGFloat) {
self.sizeToFit()
self.frame.origin.x = bottomX - self.frame.width
self.frame.origin.y = y
}
override open func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.width / 2
self.iconView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width * self.widthIconFactor, height: self.frame.height * self.heightIconFactor)
self.iconView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
}
override open func sizeToFit() {
super.sizeToFit()
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 30, height: 30)
}
}
@@ -0,0 +1,50 @@
// 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
open class SPStorkCloseView: UIView {
var color = UIColor.blue {
didSet {
self.setNeedsDisplay()
}
}
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
private func commonInit() {
self.backgroundColor = UIColor.clear
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override open func draw(_ rect: CGRect) {
super.draw(rect)
SPStorkCodeDraw.drawClose(frame: rect, resizing: .aspectFit, color: self.color)
}
}