Controller as in Apple Music, Podcasts and Mail apps. Help if you need customize height or suppport modal style in iOS 12.
Simple adding close button and centering arrow indicator. Customizable height. Using custom TransitionDelegate
.
Alert you can find in SPAlert project. It support diffrents presets, some animatable.
If you like the project, don't forget to put star ★
and follow me on GitHub:
Swift 4.2
& 5.0
. Ready for use on iOS 10+
CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate SPStorkController
into your Xcode project using CocoaPods, specify it in your Podfile
:
pod 'SPStorkController'
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate SPStorkController
into your Xcode project using Carthage, specify it in your Cartfile
:
github "ivanvorobei/SPStorkController"
The Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
To integrate SPStorkController
into your Xcode project using Xcode 11, specify it in Project > Swift Packages
:
https://github.com/ivanvorobei/SPStorkController
If you prefer not to use any of the aforementioned dependency managers, you can integrate SPStorkController
into your project manually. Put Source/SPStorkController
folder in your Xcode project. Make sure to enable Copy items if needed
and Create groups
.
Create controller and call func presentAsStork
:
import UIKit
import SPStorkController
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let controller = UIViewController()
self.presentAsStork(controller)
}
}
If you want customize controller (remove indicator, set custom height and other), create controller and set transitioningDelegate
to SPStorkTransitioningDelegate
object. Use present
or dismiss
functions:
let controller = UIViewController()
let transitionDelegate = SPStorkTransitioningDelegate()
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
self.present(controller, animated: true, completion: nil)
Please, do not init SPStorkTransitioningDelegate
like this:
controller.transitioningDelegate = SPStorkTransitioningDelegate()
You will get an error about weak property.
To set light status bar for presented controller, use preferredStatusBarStyle
property. Also set modalPresentationCapturesStatusBarAppearance
. See example:
import UIKit
class ModalViewController: UIViewController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
Property customHeight
sets custom height for controller. Default is nil
:
transitionDelegate.customHeight = 350
Property showCloseButton
added circle button with dismiss action. Default is false
:
transitionDelegate.showCloseButton = false
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
:
transitionDelegate.showIndicator = true
Property Parameter indicatorColor
for customize color of arrow. Default is gray
:
transitionDelegate.indicatorColor = UIColor.white
Property hideIndicatorWhenScroll
shows or hides indicator when scrolling. Default is false
:
transitionDelegate.hideIndicatorWhenScroll = true
You can set always line or arrow indicator. Set indicatorMode
:
transitionDelegate.indicatorMode = .alwaysLine
You can also configure events that will dimiss the controller.
Property swipeToDismissEnabled
enables dismissal by swipe gesture. Default is true
:
transitionDelegate.swipeToDismissEnabled = true
Property translateForDismiss
sets how much need to swipe down to close the controller. Work only if swipeToDismissEnabled
is true. Default is 240
:
transitionDelegate.translateForDismiss = 100
Property tapAroundToDismissEnabled
enables dismissal by tapping parent controller. Default is true
:
transitionDelegate.tapAroundToDismissEnabled = true
Property cornerRadius
for customize corner radius of controller's view. Default is 10
:
transitionDelegate.cornerRadius = 10
Property hapticMoments
allow add taptic feedback for some moments. Default is .willDismissIfRelease
:
transitionDelegate.hapticMoments = [.willPresent, .willDismiss]
The project uses a snapshot of the screen in order to avoid compatibility and customisation issues. Before controller presentation, a snapshot of the parent view is made, and size and position are changed for the snapshot. Sometimes you will need to update the screenshot of the parent view, for that use static func:
SPStorkController.updatePresentingController(modal: controller)
and pass the controller, which is modal and uses SPStorkTransitioningDelegate
.
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:
scrollView.setContentOffset(self.contentOffset, animated: false)
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 couldn’t 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 doesn’t execute the necessary functions:
import UIKit
import SPFakeBar
class ModalController: UIViewController {
let navBar = SPFakeBarView(style: .stork)
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.navBar.titleLabel.text = "Title"
self.navBar.leftButton.setTitle("Cancel", for: .normal)
self.navBar.leftButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
self.view.addSubview(self.navBar)
}
}
You only need to add a navigation bar to the main view, it will automatically layout. Use style .stork
in init of SPFakeBarView
. Here is visual preview with Navigation Bar and without it:
To use it, you need to install SPFakeBar pod:
pod 'SPFakeBar'
If you use UIScrollView
(or UITableView & UICollectionView) on controller, I recommend making it more interactive. When scrolling reaches the top position, the controller will interactively drag down, simulating a closing animation. Also available close controller by drag down on UIScrollView
. To do this, set the delegate and in the function scrollViewDidScroll
call:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
SPStorkController.scrollViewDidScroll(scrollView)
}
Working with a collections classes is not difficult. In the Example
folder you can find an implementation. However, I will give a couple of tips for making the table look better.
Firstly, if you use SPFakeBarView
, don't forget to set top insets for content & scroll indicator. Also, I recommend setting bottom insets (it optional):
tableView.contentInset.top = self.navBar.height
tableView.scrollIndicatorInsets.top = self.navBar.height
Please, also use SPStorkController.scrollViewDidScroll
function in scroll delegate for more interactiveness with your collection or table view.
For confirm closing by swipe, tap around, close button and indicator use SPStorkControllerConfirmDelegate
. Implenet protocol:
@objc public protocol SPStorkControllerConfirmDelegate: class {
var needConfirm: Bool { get }
func confirm(_ completion: @escaping (_ isConfirmed: Bool)->())
}
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:
SPStorkController.dismissWithConfirmation(controller: self, completion: nil)
It call confirm
func and check result of confirmation. See example project for more details.
You can check events by implement SPStorkControllerDelegate
and set delegate for transitionDelegate
:
transitionDelegate.storkDelegate = self
Delagate has this functions:
protocol SPStorkControllerDelegate: class {
optional func didDismissStorkBySwipe()
optional func didDismissStorkByTap()
}
If need using SPStorkController
with storyboard, set class SPStorkSegue
for transition setting in storyboard file. I will give the class code so that you understand what it does:
import UIKit
class SPStorkSegue: UIStoryboardSegue {
public var transitioningDelegate: SPStorkTransitioningDelegate?
override func perform() {
transitioningDelegate = transitioningDelegate ?? SPStorkTransitioningDelegate()
destination.transitioningDelegate = transitioningDelegate
destination.modalPresentationStyle = .custom
super.perform()
}
}
Open your storyboard, choose transition and open right menu. Open Attributes Inspector
and in Class section insert SPStorkSegue
.
If you want to present modal controller on SPStorkController
, please set:
controller.modalPresentationStyle = .custom
It’s needed for correct presentation and dismissal of all modal controllers.
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.
I love being helpful. Here I have provided a list of libraries that I keep up to date. For see video previews
of libraries without install open opensource.ivanvorobei.by website.
I have libraries with native interface and managing permissions. Also available pack of useful extensions for boost your development process.
Подписывайся в телеграмм-канал, если хочешь получать уведомления о новых туториалах.
Со сложными и непонятными задачами помогут в чате.
Видео-туториалы выклыдываю на YouTube: