Now playing controller from Apple Music, Mail & Podcasts Apple's apps.

Overview

SPStorkController

About

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:

https://github.com/ivanvorobei

Navigate

Requirements

Swift 4.2 & 5.0. Ready for use on iOS 10+

Installation

CocoaPods:

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

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"

Swift Package Manager

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

Manually

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.

Quick Start

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.

Usage

Light StatusBar

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
    }
}

Custom Height

Property customHeight sets custom height for controller. Default is nil:

transitionDelegate.customHeight = 350

Close Button

Property showCloseButton added circle button with dismiss action. Default is false:

transitionDelegate.showCloseButton = false

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:

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

Dismissing

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

Corner Radius

Property cornerRadius for customize corner radius of controller's view. Default is 10:

transitionDelegate.cornerRadius = 10

Haptic

Property hapticMoments allow add taptic feedback for some moments. Default is .willDismissIfRelease:

transitionDelegate.hapticMoments = [.willPresent, .willDismiss]

Snapshots

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)

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 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'

Working with UIScrollView

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 UITableView & UICollectionView

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.

Confirm before dismiss

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.

Delegate

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()
}

Storyboard

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.

Modal presentation of other controller

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.

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.

Other Projects

SPAlert

You can find this alerts in AppStore after feedback or after added song to library in Apple Music. Contains popular Done, Heart presets and many other. Done preset present with draw path animation like original. Also available simple present message without icon. Usage in one line code.

SPPerspective

Animation of widgets from iOS 14. 3D transform with dynamic shadow. Look video preview. Available deep customisation 3D and shadow. Also you can use static transform without animation.

SPPermissions

Using for request and check state of permissions. Available native UI for request multiple permissions at the same time. Simple integration and usage like 2 lines code.

SPDiffable

Simplifies working with animated changes in table and collections. Apple's diffable API required models for each object type. If you want use it in many place, you pass time to implement it and get over duplicates codes. This project help do it elegant with shared models and special cell providers. Support side bar iOS14 and already has native cell providers and views.

SparrowKit

Collection of native Swift extensions to boost your development. Support tvOS and watchOS.

Russian Community

В телеграм-канале Код Воробья пишу о iOS разработке. Помощь можно найти в нашем чате. Видео-туториалы выклыдываю на YouTube:

Tutorials on YouTube

Comments
  • Improve dismissing ScrollView

    Improve dismissing ScrollView

    • Support swipe in UIViewController embedded in UINavigationController
    • Fix position of UITableView header view - reason: applying transformation to tableHeaderView itself - as one of scrollView.subviews doesn't work as expected, it need's to be applied to header view content
    opened by dstranz 18
  • Leak when presenting UINavigationController.

    Leak when presenting UINavigationController.

    It seems that for some reasons, controller being presented is leaked when it is UINavigationController.

    How to reproduce:

    1. Download example project
    2. Go to presentModalTableViewController method.
    3. Edit as follows:
        @objc func presentModalTableViewController() {
            let modal = ModalTableViewController()
            let transitionDelegate = SPStorkTransitioningDelegate()
    
            let navVC = UINavigationController(rootViewController: modal)
            transitionDelegate.storkDelegate = self
            transitionDelegate.confirmDelegate = modal
            navVC.transitioningDelegate = transitionDelegate
            navVC.modalPresentationStyle = .custom
    
            self.present(navVC, animated: true, completion: nil)
        }
    
    1. Launch the app. Present Table Controller and then dismiss it.
    2. Open debugger / instruments - you will notice that ModalTableViewController (and its navigation controller) is leaked.

    Not sure yet where exactly the leak occurs. Perhaps you might have some clue.

    Cheers

    bug 
    opened by Moriquendi 16
  • Unable to Use it In Storyboards

    Unable to Use it In Storyboards

    I want to use SPStorkController in storyboard via Segues. But I am not be able to do it. i tried this.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            let vc = segue.destination
            let transitionDelegate = SPStorkTransitioningDelegate()
            vc.transitioningDelegate = transitionDelegate
            vc.modalPresentationStyle = .custom
        }
    

    In storyborad my segue is kind of presentModally

    bug 
    opened by KhaliqCitrusbits 10
  • Doesn't play nice with black navigation bar(s)

    Doesn't play nice with black navigation bar(s)

    Hi! First of all thanks for this great library! I am using cocoapods 1.4.2 and found out that the transition doesn't work right if the source controller is embedded in an navigation controller with barStyle = .black Most of the times the transition doesn't happen and when it does, it does with significant delay. If the barStyle is default there is no problem.

    opened by magnett 10
  • Pass information to parent view controller

    Pass information to parent view controller

    Hi, loving the library however i have one question (might be an easy one). How do i pass information from the modal view controller to the parent on custom dismiss? E.g. i've got a tableview cell in the modal view controller that will dismiss the modal on tap and set a label text in the parent view. I assume something like:

    SPStorkController.dismissWithConfirmation(controller: self, completion: {
        //pass information here?
    })
    

    Thanks!

    wait more 
    opened by joekw 9
  • Snapshot frame width

    Snapshot frame width

    Hello Ivan. I was looking at Apple Music card UI and noticed that the snapshot card frame is wider than SPStorkController and I think it looks better this way. Can I make the frame wider or can you update it and make it wider liker Apple Music please?

    Untitled

    enhancement 
    opened by fawzialrifai 7
  • Not dismissing after scroll

    Not dismissing after scroll

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        SPStorkController.scrollViewDidScroll(scrollView)
    }
    

    Adding this code does make it pan with scroll but letting go after pulling down still does not make it dismiss.

    opened by hiroshihorie 7
  • Graphic glitch after a

    Graphic glitch after a "full screen" UIViewController over SPStorkController is dismissed

    Hello Ivan. I have this small issue but i'd love to get rid of it if possible. I have SPStorkController working properly, but when I tap on email address a "full screen" view controller will present over it, and when I dismiss this view controller the top side of SPStorkController is going "full screen" with no rounded corners, and then when it recalculates its height, it "bounce" down and restore the rounded corners. Is there a way to fix this?

    Here is a simple project to check it: https://github.com/fawzirifai/TestingUITextView/

    Thank you.

    RPReplay_Final1557242907

    minor bug 
    opened by fawzialrifai 6
  • Weird issue with UITableView

    Weird issue with UITableView

    I am having a weird issue when scrolling. Cells start disappearing when you bounce the view. From my testing is caused by SPStorkController.scrollViewDidScroll(scrollView) but not sure why yet, is this issue known?

    bug

    opened by robertodias180 6
  • Loading modal from Xib file

    Loading modal from Xib file

    While loading modal ViewController from a xib file, arror indicator centres according to the xib width, not the device width: Снимок экрана 2019-08-01 в 14 43 27 Is there any way to fix that?

    opened by DyakoVlad 5
  • Search bar jump down

    Search bar jump down

    I have a UIViewController with UITableView and UISearchController, when I tap on search bar it jumps one row down. I created a very simple project you can check it. https://github.com/fawzirifai/TestingSearchBar/

    ScreenRecording_04-30-2019-18-04-47

    opened by fawzialrifai 5
  • Custom Height after presenting controller.

    Custom Height after presenting controller.

    #97

    @Nurka11 Yes, In my UI also something like that design, after presenting I will know the height of the content view I need to change the height depending on it. @ivanvorobei kindly do something to pass Notification Observer or delegates to change the height. it might be needful. Thanks for your awesome workman. 👍

    opened by syedrazackimran 0
  • iOS 14 bug : swiping down the sheet do not pull down the sheet

    iOS 14 bug : swiping down the sheet do not pull down the sheet

    Bug:

    When you swipe down the view, it do not pull down gradually the sheet. When you swipe down enough it will close it suddenly. It was working great before and the bug appear only on iOS 14.

    Code used :

    `

        let transitionDelegate = SPStorkTransitioningDelegate()
        transitionDelegate.storkDelegate = navController
        transitionDelegate.customHeight = viewController.panelHeight
        transitionDelegate.confirmDelegate = navController
        navController.transitioningDelegate = transitionDelegate
    
        navController.modalPresentationStyle = .custom
        
        self.topNavigationController().present(navController, animated: true, completion: nil)`
    
    opened by blitzvb 11
  • Transition Bug (formSheet) / Screenshot

    Transition Bug (formSheet) / Screenshot

    see gif OhneTitel2

       func present(animated: Bool = false) -> Self {
          let controller = self.viewController ?? createViewController()
          let transitionDelegate = SPStorkTransitioningDelegate()
          transitionDelegate.customHeight = 100
          controller.transitioningDelegate = transitionDelegate
          controller.modalPresentationStyle = .custom
          controller.modalPresentationCapturesStatusBarAppearance = false
          navigationController.hero.isEnabled = false
          navigationController.present(controller, animated: true, completion: nil)
          return self
       }
    
    opened by StefaniOSApps 4
  • Can't integrate pod in objective-c project

    Can't integrate pod in objective-c project

    Hi, I'm integrating the pod in an objective-c project, but I'm facing several issues.

    I have the following code:

    SPStorkTransitioningDelegate * transitionDelegate = [[SPStorkTransitioningDelegate alloc] init]; transitionDelegate.storkDelegate = self; <---------- ERROR transitionDelegate.confirmDelegate = dialPad; dialPad.modalPresentationStyle = UIModalPresentationCustom; dialPad.transitioningDelegate = transitionDelegate;

    The error is: Property 'storkDelegate' not found on object of type 'SPStorkTransitioningDelegate *'

    Any advise?

    help wanted minor bug 
    opened by punto2018 10
  • Support for disabling swipe gesture

    Support for disabling swipe gesture

    There is no option for disabling swipe gesture during runtime. For example, I have a UITableView with editing mode and reordering cells. While moving cell down SPStorkController fires UIPanGestureRecognizer and dismisses the screen.

    opened by Hans92 0
Releases(1.8.5)
Owner
Ivan Vorobei
iOS Developer
Ivan Vorobei
A custom modal transition that presents and dismiss a controller with an expanding bubble effect.

A custom modal transition that presents and dismiss a controller inside an expanding and shrinking bubble. Screenshot Usage Install through CocoaPods:

Andrea Mazzini 3.3k Dec 28, 2022
SPLarkController - Custom transition between controllers. Settings controller for your iOS app.

SPLarkController About Transition between controllers to top. You can change animatable height after presentation controller. For presentation and dis

Ivan Vorobei 965 Dec 17, 2022
This component implements transition animation to crumble view-controller into tiny pieces.

StarWars Animation This component implements transition animation to crumble view-controller into tiny pieces. Check this project on dribbble. Also, r

Yalantis 3.7k Dec 29, 2022
A library to recreate the iOS Apple Music now playing transition

DeckTransition DeckTransition is an attempt to recreate the card-like transition found in the iOS 10 Apple Music and iMessage apps. Hereʼs a GIF showi

Harshil Shah 2.2k Dec 15, 2022
LNPopupController is a framework for presenting view controllers as popups of other view controllers, much like the Apple Music and Podcasts apps.

LNPopupController LNPopupController is a framework for presenting view controllers as popups of other view controllers, much like the Apple Music and

Leo Natan 2.9k Jan 2, 2023
A modern HUD inspired by Apple Music and Apple Podcasts

HUD A modern HUD inspired by Apple Music and Apple Podcasts. Appearance Light Dark HUD Activity Indicator HUD Requirements iOS 13+ Installation You ca

Bei Li 30 Nov 18, 2022
AIB indicates for your app users which audio is playing. Just like the Podcasts app.

Audio Indicator Bars for iOS and tvOS Indicates for your app users which audio is playing. Just like the Podcasts app. Index Requirements and Details

Leonardo Cardoso 285 Nov 23, 2022
🐵Fooling around with Apples private framework AvatarKit

Fooling around with Apples private framework AvatarKit, the framework used in Messages.app for recording Animoji videos. If you are looking to create your own Animoji, take a look at SBSCustomAnimoji.

Simon Støvring 968 Dec 25, 2022
Integrate Pianobar with the Now Playing feature of macOS

PianobarNowPlayable - Integrate Pianobar with the Now Playing feature of macOS Ever wanted to control Pianobar like you do most other music applicatio

Dominic 4 Aug 30, 2022
Repository for the first challenge of the SwiftUI Animation Challenges. Create the likeable now playing animation from the Spotify app.

Repository for the first challenge of the SwiftUI Animation Challenges. Create the likeable now playing animation from the Spotify app.

null 18 Aug 16, 2022
Swift-music - swift-music is a swift package that provides an easy-to-use API for music related developments.

?? swift-music Introduction swift-music is a swift package that provides an easy-to-use API for music related developments. Currently available module

Jin Zhang 4 Feb 8, 2022
Classical music front-end for Apple Music: iOS app

concertino_ios Concertino is a classical music front-end for Apple Music. It's splitted in several projects. This one provides only the iOS app. (Ther

Open Opus 107 Dec 22, 2022
A pure Swift Spotify Music App in Apple Music style

HBMusic A pure Swift Spotify Music App in Apple Music style. How to run pod inst

haoboxuxu 6 Dec 29, 2021
AudioPlayer is a simple class for playing audio in iOS, macOS and tvOS apps.

AudioPlayer AudioPlayer is a simple class for playing audio in iOS, macOS and tvOS apps.

Tom Baranes 260 Nov 27, 2022
iOS music player app that downloads music from the internet, even YouTube

About YouTag is an iOS music player app that downloads music from the internet, even YouTube, and manages it in a local library. Music videos can also

null 263 Jan 8, 2023
Music Room: a mobile app that offers a new way of experiencing music

?? Music Room - 42 School Project ?? ???? Music Room is a mobile app that offers

Marie-Lise Picard 3 Feb 18, 2022
SwiftUIMailView - MailView allows you to send mail from SwiftUI

SwiftUIMailView The MailView allows you to send mail from SwiftUI. You can: Determine if you can send mail or not. Pass subject, message and recipient

Gordan Glavaš 13 Nov 24, 2022
iOS-mail — ProtonMail iOS client app

iOS-mail Introduction iOS-mail — ProtonMail iOS client app The app is intended for all users of the ProtonMail service. Whether they are paid or free,

null 1.2k Jan 3, 2023
TempBox - Instant disposable emails for Mac powered by Mail.tm

TempBox Instant disposable emails for Mac powered by Mail.tm Features Native Mac app Create multiple accounts Download message source Download Attachm

Waseem akram 217 Dec 30, 2022
MailCore 2 provide a simple and asynchronous API to work with e-mail protocols IMAP, POP and SMTP.

MailCore 2: Introduction MailCore 2 provides a simple and asynchronous Objective-C API to work with the e-mail protocols IMAP, POP and SMTP. The API h

MailCore 2.5k Jan 1, 2023