/Sheet

A UIAlertController like action sheet using custom UI that you provide.

Primary LanguageSwift

Sheet

Cool pop ups assembled using interface builder and minimal code.

|

Just a normal storyboard

Demo

Usage

final class ViewController: UIViewController {

    private let sheet = Sheet(animation: .slideLeft)
        
    @objc 
    func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
        let storyboard = UIStoryboard(name: "WelcomeSheet", bundle: nil)
        let viewController = storyboard.instantiateInitialViewController()!
        sheet.show(viewController, above: self)
    }
}

To handle touch events outside of the action sheett.

sheet.chromeTapped = { [unowned self] in
    self.dismiss(animated: true)
}

To handle dismiss at the end of the workflow, set up an observer at the beginning and post a notification at the end.

extension Notification.Name {
    static let dismiss = Notification.Name(rawValue: "Dismiss")
}

NotificationCenter.default.addObserver(
    forName: .dismiss, 
    object: nil, 
    queue: nil) { [weak self] _ in
        self?.dismiss(animated: true)
}

This will also give you the opportunity to dismiss from other locations, should you deem it necessary.

func applicationDidEnterBackground(_ application: UIApplication) {
    NotificationCenter.default.post(
        name: .dismiss,
        object: self,
        userInfo: [dismissSheetAnimatedKey : false]
    )
}

Responding to size class changes or dynamic type is the responsibility of your view controllers.

final class CompleteSheetViewController: UIViewController {

    @IBOutlet weak var iconImageView: UIImageView!
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        iconImageView.isHidden = (traitCollection.verticalSizeClass == .compact)
    }
    
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
    
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)
        iconImageView.isHidden = (newCollection.verticalSizeClass == .compact)
        coordinator.animate(alongsideTransition: { [unowned self] _ in
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}

If your view controller workflow uses func show(_ vc: UIViewContoller, sender: Any?), then you're good to go.

@IBAction func startButtonPressed(_ sender: UIButton) {
    let viewController = UIStoryboard(name: "CompleteSheet", bundle: nil).instantiateInitialViewController()!
    show(viewController, sender: self)
}

If you would like to use custom transition animations, instantiate Sheet with .custom and sublcass StoryboardSegue.

final class FlipFromLeftSegue: StoryboardSegue {
    
    override func executeTransition(_ completion: @escaping () -> Void) {
        UIView.transition(
            from: source.view!,
            to: destination.view!,
            duration: 0.3,
            options: [.transitionFlipFromLeft]) { (_) in
                completion()
            }
    }
}

final class DropSegue: StoryboardSegue {

    override func executeTransition(_ completion: @escaping () -> Void) {
        destination.view.layoutIfNeeded()
        destination.view.transform = CGAffineTransform(translationX: 0, y: destination.view.bounds.height)
        let height = source.view.bounds.height
        UIView.animate(withDuration: 0.3, animations: {
            self.source.view.transform = CGAffineTransform(translationX: 0, y: height)
            self.destination.view.transform = CGAffineTransform.identity
        }) { _ in
            completion()
        }
    }
}