Compare commits

...

17 Commits

Author SHA1 Message Date
Ivan Vorobei 223ec2d2c0 Update to 1.7.7
Fix memory leak when use UINavigationController
2019-08-05 23:19:16 +03:00
Ivan Vorobei a0ca7e9e2a Merge pull request #92 from Moriquendi/master
#88 Prevent controller leaking when embedded in UINavigationController
2019-08-05 23:12:42 +03:00
Michał Śmiałko f0a1914145 Check if controller's modalPresentationStyle is custom before accessing presentationController. 2019-08-05 14:33:44 +02:00
Ivan Vorobei 4355f5eb09 Update UserInterfaceState.xcuserstate 2019-07-22 20:34:29 +03:00
Ivan Vorobei 6b080f503d Update README.md 2019-07-22 11:20:40 +03:00
Ivan Vorobei c23ed83bf0 Update UserInterfaceState.xcuserstate 2019-07-22 11:20:00 +03:00
Ivan Vorobei 8b1c77d743 Update README.md 2019-07-18 17:30:44 +03:00
Ivan Vorobei 915ef85cff Update README.md 2019-07-18 17:26:46 +03:00
Ivan Vorobei dc1050be2a Update README.md 2019-07-18 13:04:34 +03:00
Ivan Vorobei 99ba8bf657 Update README.md 2019-07-18 13:03:33 +03:00
Ivan Vorobei 2b7e4198cf Update SPStorkController.podspec 2019-07-16 20:29:33 +03:00
Ivan Vorobei 0969cceac8 Update close method for UIAccessibility
Also update example project
2019-07-16 20:29:24 +03:00
Ivan Vorobei f349dea851 Merge pull request #81 from irskep/irskep-accessibility
Improve accessibility (fix #80)
2019-07-16 20:20:00 +03:00
Ivan Vorobei b8e8f8d0ce Update to 1.7.5
Fix completion for confirmation.
2019-07-11 19:21:29 +03:00
Ivan Vorobei ee5f9ee9de Update to 1.7.4
Add `dismissWithConfirmation` method for custom buttons.
2019-07-11 01:35:56 +03:00
Steve Johnson 97c8e8aa32 Fix indentation 2019-07-04 14:12:16 -07:00
Steve Johnson 7be93d548e Improve accessibility 2019-07-04 14:11:08 -07:00
12 changed files with 112 additions and 39 deletions
@@ -23,7 +23,7 @@ import UIKit
public enum SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView, indicatorInset: CGFloat? = nil) {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
if let presentationController = self.presentationController(for: controller) {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
@@ -35,7 +35,7 @@ public enum SPStorkController {
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.dismissWithConfirmation(prepare: nil, completion: {
self.dismissWithConfirmation(controller: controller, completion: {
presentationController.storkDelegate?.didDismissStorkBySwipe?()
})
return
@@ -58,6 +58,14 @@ public enum SPStorkController {
}
}
static public func dismissWithConfirmation(controller: UIViewController, completion: (()->())?) {
if let controller = self.presentationController(for: controller) {
controller.dismissWithConfirmation(prepare: nil, completion: {
completion?()
})
}
}
static public var topScrollIndicatorInset: CGFloat {
return 6
}
@@ -75,6 +83,8 @@ public enum SPStorkController {
}
static private func presentationController(for controller: UIViewController) -> SPStorkPresentationController? {
guard controller.modalPresentationStyle == .custom else { return nil }
if let presentationController = controller.presentationController as? SPStorkPresentationController {
return presentationController
}
@@ -82,7 +92,6 @@ public enum SPStorkController {
if let presentationController = controller.parent?.presentationController as? SPStorkPresentationController {
return presentationController
}
return nil
}
@@ -105,22 +105,40 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
let closeTitle = NSLocalizedString("Close", comment: "Close")
if self.showIndicator {
self.indicatorView.color = self.indicatorColor
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapIndicator))
tap.cancelsTouchesInView = false
self.indicatorView.addGestureRecognizer(tap)
self.indicatorView.accessibilityLabel = closeTitle
presentedView.addSubview(self.indicatorView)
self.indicatorView.translatesAutoresizingMaskIntoConstraints = false
self.indicatorView.widthAnchor.constraint(equalToConstant: 36).isActive = true
self.indicatorView.heightAnchor.constraint(equalToConstant: 13).isActive = true
self.indicatorView.centerXAnchor.constraint(equalTo: presentedView.centerXAnchor).isActive = true
self.indicatorView.topAnchor.constraint(equalTo: presentedView.topAnchor, constant: 12).isActive = true
if UIAccessibility.isVoiceOverRunning {
let accessibleIndicatorOverlayButton = UIButton(type: .custom)
accessibleIndicatorOverlayButton.addTarget(self, action: #selector(self.tapIndicator), for: .touchUpInside)
accessibleIndicatorOverlayButton.accessibilityLabel = closeTitle
presentedView.addSubview(accessibleIndicatorOverlayButton)
accessibleIndicatorOverlayButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
accessibleIndicatorOverlayButton.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor),
accessibleIndicatorOverlayButton.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor),
accessibleIndicatorOverlayButton.topAnchor.constraint(equalTo: presentedView.topAnchor),
accessibleIndicatorOverlayButton.bottomAnchor.constraint(equalTo: self.indicatorView.bottomAnchor),
])
}
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
self.closeButton.accessibilityLabel = closeTitle
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.tapCloseButton), for: .touchUpInside)
presentedView.addSubview(self.closeButton)
@@ -21,6 +21,21 @@
import UIKit
extension UIView {
var isDarkMode: Bool {
if #available(iOS 12.0, *) {
if self.traitCollection.userInterfaceStyle == .dark {
return true
} else {
return false
}
} else {
return false
}
}
}
extension UIView {
var viewController: UIViewController? {
@@ -63,7 +63,7 @@ struct SPLocalNotification {
if let category = self.category {
if #available(iOS 12.0, *) {
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: category.actions, intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
UNUserNotificationCenter.current().setNotificationCategories([notificationCategory])
content.categoryIdentifier = notificationCategory.identifier
}
@@ -81,6 +81,7 @@ struct SPLocalNotificationCategory {
var identifier: String
var summary: String
var actions: [UNNotificationAction] = []
static var countSymbol: String {
return "%u"
@@ -24,8 +24,9 @@ import UIKit
class SPFooterActionsView: SPView {
var sectionLabels = SPSectionLabelsView()
private var buttons: [SPFooterActionButton] = []
private var separators: [SPSeparatorView] = []
var buttons: [SPFooterActionButton] = []
var separators: [SPSeparatorView] = []
override func commonInit() {
super.commonInit()
@@ -49,11 +49,7 @@ class ModalTableViewController: UIViewController {
}
@objc func dismissAction() {
if let storkPresentationController = self.presentationController as? SPStorkPresentationController {
storkPresentationController.dismissWithConfirmation(prepare: nil, completion: {
print("Custom completion for confirmation. Confirmation is optional.")
})
}
SPStorkController.dismissWithConfirmation(controller: self, completion: nil)
}
}
@@ -20,11 +20,7 @@ class ModalViewController: UIViewController {
}
@objc func dismissAction() {
if let storkPresentationController = self.presentationController as? SPStorkPresentationController {
storkPresentationController.dismissWithConfirmation(prepare: nil, completion: {
print("Custom completion for confirmation. Confirmation is optional.")
})
}
SPStorkController.dismissWithConfirmation(controller: self, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
@@ -39,7 +35,7 @@ class ModalViewController: UIViewController {
extension ModalViewController: SPStorkControllerConfirmDelegate {
var needConfirm: Bool {
return true
return false
}
func confirm(_ completion: @escaping (Bool) -> ()) {
+26 -16
View File
@@ -17,20 +17,24 @@ If you like the project, do not forget to `put star ★` and follow me on GitHub
- [Requirements](#requirements)
- [Installation](#installation)
- [CocoaPods](#cocoapods)
- [Carthage](#carthage)
- [Swift Package Manager](#swift-package-manager)
- [Manually](#manually)
- [Quick Start](#quick-start)
- [Usage](#usage)
- [Light StatusBar](#light-statusbar)
- [Custom Height](#custom-height)
- [Close Button](#close-button)
- [Indicator](#indicator)
- [Arrow Indicator](#arrow-indicator)
- [Dismissing](#dismissing)
- [Corner Radius](#corner-radius)
- [Haptic](#haptic)
- [Snapshots](#snapshots)
- [Add Navigation Bar](#add-navigation-bar)
- [Navigation Bar](#navigation-bar)
- [Working with UIScrollView](#working-with-uiscrollview)
- [UITableView & UICollectionView](#working-with-uitableview--uicollectionview)
- [Confirm](#confirm)
- [Confirm before dismiss](#confirm-before-dismiss)
- [Delegate](#delegate)
- [Storyboard](#storyboard)
- [Sheets in iOS 13](#sheets-in-ios-13)
@@ -148,7 +152,7 @@ Property `showCloseButton` added circle button with dismiss action. Default is `
transitionDelegate.showCloseButton = false
```
### Indicator
### Arrow Indicator
On the top of controller you can add arrow indicator with animatable states. It simple configure.
Property `showIndicator` shows or hides top arrow indicator. Default is `true`:
@@ -216,7 +220,13 @@ SPStorkController.updatePresentingController(modal: controller)
and pass the controller, which is modal and uses `SPStorkTransitioningDelegate`.
### Add Navigation Bar
If the parent controller scrollings and you try to show `SPStorkController`, you will see how it froze, and in a second its final position is updated. I recommend before present `SPStorkController` stop scrolling force:
```swift
scrollView.setContentOffset(self.contentOffset, animated: false)
```
### Navigation Bar
You may want to add a navigation bar to your modal controller. Since it became impossible to change or customize the native controller in swift 4 (I couldnt even find a way to change the height of the bar), I had to recreate navigation bar from the ground up. Visually it looks real, but it doesnt execute the necessary functions:
@@ -275,9 +285,9 @@ tableView.scrollIndicatorInsets.top = self.navBar.height
Please, also use `SPStorkController.scrollViewDidScroll` function in scroll delegate for more interactiveness with your collection or table view.
### Confirm
### Confirm before dismiss
For confirm closing by swipe, use `SPStorkControllerConfirmDelegate`. Implenet protocol:
For confirm closing by swipe, tap around, close button and indicator use `SPStorkControllerConfirmDelegate`. Implenet protocol:
```swift
@objc public protocol SPStorkControllerConfirmDelegate: class {
@@ -288,7 +298,15 @@ For confirm closing by swipe, use `SPStorkControllerConfirmDelegate`. Implenet p
}
```
and set `confirmDelegate` property to object, which protocol impleneted. Function `confirm` call if `needConfirm` set to `true` and controller try closing by swipe. Pass `isConfirmed` with result. Best options use `UIAlertController` with `.actionSheet` style for confirmation.
and set `confirmDelegate` property to object, which protocol impleneted. Function `confirm` call if `needConfirm` return true. Pass `isConfirmed` with result. Best options use `UIAlertController` with `.actionSheet` style for confirmation.
If you use custom buttons, in the target use this code:
```swift
SPStorkController.dismissWithConfirmation(controller: self, completion: nil)
```
It call `confirm` func and check result of confirmation. See example project for more details.
### Delegate
@@ -341,14 +359,6 @@ controller.modalPresentationStyle = .custom
Its needed for correct presentation and dismissal of all modal controllers.
### Stop scroll
`SPStorkController` use snapshots. If the parent controller scrollings and you try to show `SPStorkController`, you will see how it froze, and in a second its final position is updated. I recommend to stop scrolling force:
```swift
scrollView.setContentOffset(self.contentOffset, animated: false)
```
## Sheets in iOS 13
Apple present in `WWDC 2019` new modal presentation style - `Sheets`. It ready use Support interactive dismiss and work with navigations bars. Available since iOS 13. I will add more information when I study this in more detail. You can see presentation [here](https://developer.apple.com/videos/play/wwdc2019/224/).
+1 -1
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "SPStorkController"
s.version = "1.7.2"
s.version = "1.7.7"
s.summary = "Very similar to the controllers displayed in Apple Music, Podcasts and Mail Apple's applications."
s.homepage = "https://github.com/IvanVorobei/SPStorkController"
s.source = { :git => "https://github.com/IvanVorobei/SPStorkController.git", :tag => s.version }
@@ -23,7 +23,7 @@ import UIKit
public enum SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView, indicatorInset: CGFloat? = nil) {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
if let presentationController = self.presentationController(for: controller) {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
@@ -35,7 +35,7 @@ public enum SPStorkController {
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.dismissWithConfirmation(prepare: nil, completion: {
self.dismissWithConfirmation(controller: controller, completion: {
presentationController.storkDelegate?.didDismissStorkBySwipe?()
})
return
@@ -58,6 +58,14 @@ public enum SPStorkController {
}
}
static public func dismissWithConfirmation(controller: UIViewController, completion: (()->())?) {
if let controller = self.presentationController(for: controller) {
controller.dismissWithConfirmation(prepare: nil, completion: {
completion?()
})
}
}
static public var topScrollIndicatorInset: CGFloat {
return 6
}
@@ -75,6 +83,8 @@ public enum SPStorkController {
}
static private func presentationController(for controller: UIViewController) -> SPStorkPresentationController? {
guard controller.modalPresentationStyle == .custom else { return nil }
if let presentationController = controller.presentationController as? SPStorkPresentationController {
return presentationController
}
@@ -82,7 +92,6 @@ public enum SPStorkController {
if let presentationController = controller.parent?.presentationController as? SPStorkPresentationController {
return presentationController
}
return nil
}
@@ -105,22 +105,40 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
let closeTitle = NSLocalizedString("Close", comment: "Close")
if self.showIndicator {
self.indicatorView.color = self.indicatorColor
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapIndicator))
tap.cancelsTouchesInView = false
self.indicatorView.addGestureRecognizer(tap)
self.indicatorView.accessibilityLabel = closeTitle
presentedView.addSubview(self.indicatorView)
self.indicatorView.translatesAutoresizingMaskIntoConstraints = false
self.indicatorView.widthAnchor.constraint(equalToConstant: 36).isActive = true
self.indicatorView.heightAnchor.constraint(equalToConstant: 13).isActive = true
self.indicatorView.centerXAnchor.constraint(equalTo: presentedView.centerXAnchor).isActive = true
self.indicatorView.topAnchor.constraint(equalTo: presentedView.topAnchor, constant: 12).isActive = true
if UIAccessibility.isVoiceOverRunning {
let accessibleIndicatorOverlayButton = UIButton(type: .custom)
accessibleIndicatorOverlayButton.addTarget(self, action: #selector(self.tapIndicator), for: .touchUpInside)
accessibleIndicatorOverlayButton.accessibilityLabel = closeTitle
presentedView.addSubview(accessibleIndicatorOverlayButton)
accessibleIndicatorOverlayButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
accessibleIndicatorOverlayButton.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor),
accessibleIndicatorOverlayButton.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor),
accessibleIndicatorOverlayButton.topAnchor.constraint(equalTo: presentedView.topAnchor),
accessibleIndicatorOverlayButton.bottomAnchor.constraint(equalTo: self.indicatorView.bottomAnchor),
])
}
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
self.closeButton.accessibilityLabel = closeTitle
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.tapCloseButton), for: .touchUpInside)
presentedView.addSubview(self.closeButton)