Commit e26faf0c by Daniel Jonathan Committed by GitHub

Merge pull request #1173 from OrkhanAlikhanov/dialog

Well done :)
parents 77a779df d75d3a76
...@@ -175,6 +175,9 @@ ...@@ -175,6 +175,9 @@
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; }; 9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; };
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; }; 9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; };
9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; }; 9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; };
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DDF2170D7AF000C04DF /* Dialog.swift */; };
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE12170D7C0000C04DF /* DialogController.swift */; };
9DE25DE42170D7FF000C04DF /* DialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE25DE32170D7FF000C04DF /* DialogView.swift */; };
9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */; }; 9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */; };
9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */; }; 9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */; };
9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */; }; 9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */; };
...@@ -298,6 +301,9 @@ ...@@ -298,6 +301,9 @@
9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; }; 9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; };
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
9D9089B82118914500605DC9 /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; }; 9D9089B82118914500605DC9 /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
9DE25DDF2170D7AF000C04DF /* Dialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dialog.swift; sourceTree = "<group>"; };
9DE25DE12170D7C0000C04DF /* DialogController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogController.swift; sourceTree = "<group>"; };
9DE25DE32170D7FF000C04DF /* DialogView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DialogView.swift; sourceTree = "<group>"; };
9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = "<group>"; };
9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseButtonGroup.swift; sourceTree = "<group>"; };
9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckButtonGroup.swift; sourceTree = "<group>"; };
...@@ -550,6 +556,7 @@ ...@@ -550,6 +556,7 @@
96BCB8001CB40F0300C806FE /* Color */, 96BCB8001CB40F0300C806FE /* Color */,
96328B9A1E05C135009A4C90 /* Data */, 96328B9A1E05C135009A4C90 /* Data */,
96BCB80B1CB410CC00C806FE /* Device */, 96BCB80B1CB410CC00C806FE /* Device */,
9DE25DDE2170D779000C04DF /* Dialogs */,
96230AB61D6A51FD00AF47DC /* Divider */, 96230AB61D6A51FD00AF47DC /* Divider */,
96BCB80A1CB410A100C806FE /* Extension */, 96BCB80A1CB410A100C806FE /* Extension */,
963FBF021D6696D0008F8512 /* FABMenu */, 963FBF021D6696D0008F8512 /* FABMenu */,
...@@ -771,6 +778,16 @@ ...@@ -771,6 +778,16 @@
name = Theme; name = Theme;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9DE25DDE2170D779000C04DF /* Dialogs */ = {
isa = PBXGroup;
children = (
9DE25DDF2170D7AF000C04DF /* Dialog.swift */,
9DE25DE12170D7C0000C04DF /* DialogController.swift */,
9DE25DE32170D7FF000C04DF /* DialogView.swift */,
);
name = Dialogs;
sourceTree = "<group>";
};
9DE84D6E1FF0250E00586C8B /* ButtonGroup */ = { 9DE84D6E1FF0250E00586C8B /* ButtonGroup */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -1015,9 +1032,12 @@ ...@@ -1015,9 +1032,12 @@
9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */, 9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */,
9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */, 9D00EBB4216675FB00DBCD69 /* Theme.swift in Sources */,
965E80FE1DD4D59500D61E4B /* ToolbarController.swift in Sources */, 965E80FE1DD4D59500D61E4B /* ToolbarController.swift in Sources */,
9DE25DE22170D7C0000C04DF /* DialogController.swift in Sources */,
9DE25DE42170D7FF000C04DF /* DialogView.swift in Sources */,
96328B971E05C0BB009A4C90 /* TableView.swift in Sources */, 96328B971E05C0BB009A4C90 /* TableView.swift in Sources */,
965E80F81DD4D59500D61E4B /* ImageCard.swift in Sources */, 965E80F81DD4D59500D61E4B /* ImageCard.swift in Sources */,
96328B991E05C0CE009A4C90 /* TableViewController.swift in Sources */, 96328B991E05C0CE009A4C90 /* TableViewController.swift in Sources */,
9DE25DE02170D7AF000C04DF /* Dialog.swift in Sources */,
965E80F91DD4D59500D61E4B /* PresenterCard.swift in Sources */, 965E80F91DD4D59500D61E4B /* PresenterCard.swift in Sources */,
96E09DC81F2287E50000B121 /* TabsController.swift in Sources */, 96E09DC81F2287E50000B121 /* TabsController.swift in Sources */,
961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */, 961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */,
......
...@@ -191,9 +191,9 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable { ...@@ -191,9 +191,9 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable {
/** /**
A convenience initializer that acceps an image and tint A convenience initializer that acceps an image and tint
- Parameter image: A UIImage. - Parameter image: A UIImage.
- Parameter tintColor: A UI - Parameter tintColor: A UIColor.
*/ */
public init(image: UIImage?, tintColor: UIColor = Color.blue.base) { public init(image: UIImage?, tintColor: UIColor? = nil) {
super.init(frame: .zero) super.init(frame: .zero)
prepare() prepare()
prepare(with: image, tintColor: tintColor) prepare(with: image, tintColor: tintColor)
...@@ -202,9 +202,9 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable { ...@@ -202,9 +202,9 @@ open class Button: UIButton, Pulseable, PulseableLayer, Themeable {
/** /**
A convenience initializer that acceps a title and title A convenience initializer that acceps a title and title
- Parameter title: A String. - Parameter title: A String.
- Parameter titleColor: A UI - Parameter titleColor: A UIColor.
*/ */
public init(title: String?, titleColor: UIColor = Color.blue.base) { public init(title: String?, titleColor: UIColor? = nil) {
super.init(frame: .zero) super.init(frame: .zero)
prepare() prepare()
prepare(with: title, titleColor: titleColor) prepare(with: title, titleColor: titleColor)
...@@ -309,9 +309,9 @@ extension Button { ...@@ -309,9 +309,9 @@ extension Button {
- Parameter image: A UIImage. - Parameter image: A UIImage.
- Parameter tintColor: A UI - Parameter tintColor: A UI
*/ */
fileprivate func prepare(with image: UIImage?, tintColor: UIColor) { fileprivate func prepare(with image: UIImage?, tintColor: UIColor?) {
self.image = image self.image = image
self.tintColor = tintColor self.tintColor = tintColor ?? self.tintColor
} }
/** /**
...@@ -319,9 +319,9 @@ extension Button { ...@@ -319,9 +319,9 @@ extension Button {
- Parameter title: A String. - Parameter title: A String.
- Parameter titleColor: A UI - Parameter titleColor: A UI
*/ */
fileprivate func prepare(with title: String?, titleColor: UIColor) { fileprivate func prepare(with title: String?, titleColor: UIColor?) {
self.title = title self.title = title
self.titleColor = titleColor self.titleColor = titleColor ?? self.titleColor
} }
} }
......
/*
* Copyright (C) 2018, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (C) 2018 Orkhan Alikhanov <orkhan.alikhanov@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
import Motion
@objc
public protocol DialogDelegate {
/**
A delegation method that is executed when the Dialog is cancelled through tapping background.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogDidCancel(_ dialog: Dialog)
/**
A delegation method that is executed when the Dialog will appear.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogWillAppear(_ dialog: Dialog)
/**
A delegation method that is executed when the Dialog did disappear.
- Parameter _ dialog: A Dialog.
*/
@objc
optional func dialogDidDisappear(_ dialog: Dialog)
/**
A delegation method that is executed to determine if the Dialog should be dismissed.
- Parameter _ dialog: A Dialog.
- Parameter shouldDismiss button: The tapped button. nil if dialog is being
cancelled through tapping background.
- Returns: A Boolean.
*/
@objc
optional func dialog(_ dialog: Dialog, shouldDismiss button: Button?) -> Bool
/**
A delegation method that is executed when the positive button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapPositive button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapPositive button: Button)
/**
A delegation method that is executed when the negative button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapNegative button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapNegative button: Button)
/**
A delegation method that is executed when the neutral button of Dialog is tapped.
- Parameter _ dialog: A Dialog.
- Parameter didTapNeutral button: A Button.
*/
@objc
optional func dialog(_ dialog: Dialog, didTapNeutral button: Button)
}
/// A builder for DialogController.
open class Dialog: NSObject {
/// A reference to dialog controller.
public let controller = DialogController<DialogView>()
/// A weak reference to DialogDelegate.
open weak var delegate: DialogDelegate?
/// An empty initializer.
public override init() {
super.init()
/// Set callbacks for delegate.
shouldDismiss(handler: nil)
.positive(nil, handler: nil)
.negative(nil, handler: nil)
.neutral(nil, handler: nil)
.isCancelable(controller.isCancelable, handler: nil)
.willAppear(handler: nil)
.didDisappear(handler: nil)
}
/**
Sets title of the dialog.
- Parameter _ text: A string.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func title(_ text: String?) -> Dialog {
dialogView.titleLabel.text = text
return self
}
/**
Sets details of the dialog.
- Parameter _ text: A string.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func details(_ text: String?) -> Dialog {
dialogView.detailsLabel.text = text
return self
}
/**
Sets title and handler for positive button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func positive(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.positiveButton.title = title
controller.didTapPositiveButtonHandler = { [unowned self] in
self.delegate?.dialog?(self, didTapPositive: self.controller.dialogView.positiveButton)
handler?()
}
return self
}
/**
Sets title and handler for negative button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func negative(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.negativeButton.title = title
controller.didTapNegativeButtonHandler = { [unowned self] in
self.delegate?.dialog?(self, didTapNegative: self.controller.dialogView.negativeButton)
handler?()
}
return self
}
/**
Sets title and handler for neutral button of dialog.
- Parameter _ title: A string.
- Parameter handler: A closure handling tap.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func neutral(_ title: String?, handler: (() -> Void)?) -> Dialog {
dialogView.neutralButton.title = title
controller.didTapNeutralButtonHandler = { [unowned self] in
self.delegate?.dialog?(self, didTapNeutral: self.controller.dialogView.neutralButton)
handler?()
}
return self
}
/**
Sets cancelability of dialog and handler for when it's cancelled.
- Parameter _ value: A Bool.
- Parameter handler: A closure handling cancellation.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func isCancelable(_ value: Bool, handler: (() -> Void)? = nil) -> Dialog {
controller.isCancelable = value
controller.didCancelHandler = { [unowned self] in
self.delegate?.dialogDidCancel?(self)
handler?()
}
return self
}
/**
Sets should-dismiss handler of dialog which takes dialogView and tapped
button and returns a boolean indicating if dialog should be dismissed.
- Parameter handler: A closure handling if dialog can be dismissed.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func shouldDismiss(handler: ((DialogView, Button?) -> Bool)?) -> Dialog {
controller.shouldDismissHandler = { [unowned self] dialogView, button in
let d = self.delegate?.dialog?(self, shouldDismiss: button) ?? true
let h = handler?(dialogView, button) ?? true
return d && h
}
return self
}
/**
Sets handler for when view controller will appear.
- Parameter handler: A closure handling the event.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func willAppear(handler: (() -> Void)?) -> Dialog {
controller.willAppear = { [unowned self] in
self.delegate?.dialogWillAppear?(self)
handler?()
}
return self
}
/**
Sets handler for when view controller did disappear.
- Parameter handler: A closure handling the event.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func didDisappear(handler: (() -> Void)?) -> Dialog {
controller.didDisappear = { [unowned self] in
self.delegate?.dialogDidDisappear?(self)
handler?()
self.controller.dialog = nil
}
return self
}
/**
Sets dialog delegate.
- Parameter delegate: A DialogDelegate.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func delegate(_ delegate: DialogDelegate) -> Dialog {
self.delegate = delegate
return self
}
/**
Presents dialog modally from given viewController.
- Parameter _ viewController: A UIViewController.
- Returns: Dialog itself to allow chaining.
*/
@discardableResult
open func show(_ viewController: UIViewController) -> Dialog {
controller.dialog = self
viewController.present(controller, animated: true, completion: nil)
return self
}
}
private extension Dialog {
/// Returns dialogView of controller.
var dialogView: DialogView {
return controller.dialogView
}
}
/// A memory reference to companion Dialog instance.
private var DialogKey: UInt8 = 0
private extension DialogController {
/**
A Dialog instance attached to the dialog controller.
This is used to keep Dialog alive throughout the lifespan
of the controller.
*/
var dialog: Dialog? {
get {
return AssociatedObject.get(base: self, key: &DialogKey) {
return nil
}
}
set(value) {
AssociatedObject.set(base: self, key: &DialogKey, value: value)
}
}
}
/*
* Copyright (C) 2018, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (C) 2018 Orkhan Alikhanov <orkhan.alikhanov@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
/// A UIViewController managing DialogView.
open class DialogController<T: DialogView>: UIViewController {
/// A reference to dialogView.
public let dialogView = T()
/// A boolean indicating cancelability of dialog when user taps on background.
open var isCancelable = false
/// A reference to did-cancel handler.
open var didCancelHandler: (() -> Void)?
/**
A reference to should-dismiss handler which takes dialogView
and tapped button and returns Boolean indicating if dialog should be dismissed.
*/
open var shouldDismissHandler: ((T, Button?) -> Bool)?
/// A reference to handler for when positiveButton is tapped.
open var didTapPositiveButtonHandler: (() -> Void)?
/// A reference to handler for when negativeButton is tapped.
open var didTapNegativeButtonHandler: (() -> Void)?
/// A reference to handler for when neutralButton is tapped.
open var didTapNeutralButtonHandler: (() -> Void)?
/// A reference to handler for when controller will appear.
open var willAppear: (() -> Void)?
/// A reference to handler for when controller did disappear.
open var didDisappear: (() -> Void)?
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
prepare()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/// Prepares controller for presentation.
open func prepare() {
isMotionEnabled = true
motionTransitionType = .fade
modalPresentationStyle = .overFullScreen
}
open override func viewDidLoad() {
super.viewDidLoad()
prepareView()
prepareDialogView()
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
willAppear?()
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
didDisappear?()
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
dialogView.maxSize = CGSize(width: Screen.width * 0.8, height: Screen.height * 0.9)
}
/**
Dismisses dialog.
- Parameter isAnimated: A boolean.
*/
open func dismiss(isAnimated: Bool = true) {
dismiss(isTriggeredByUserInteraction: false, isAnimated: isAnimated)
}
/// Handler for when background scrim is tapped.
@objc
private func didTapBackgroundView() {
guard isCancelable else {
return
}
dismiss(isTriggeredByUserInteraction: true, isAnimated: true)
}
/// Handler for when one of 3 dialog buttons is tapped.
@objc
private func didTapButton(_ sender: Button) {
switch sender {
case dialogView.positiveButton:
didTapPositiveButtonHandler?()
case dialogView.negativeButton:
didTapNegativeButtonHandler?()
case dialogView.neutralButton:
didTapNeutralButtonHandler?()
default:
break
}
dismiss(isTriggeredByUserInteraction: true, isAnimated: true, using: sender)
}
}
private extension DialogController {
/// Prepares view.
func prepareView() {
let v = UIControl()
v.backgroundColor = Color.black.withAlphaComponent(0.33)
v.addTarget(self, action: #selector(didTapBackgroundView), for: .touchUpInside)
view = v
}
/// Prepares dialogView.
func prepareDialogView() {
view.layout(dialogView).center()
dialogView.buttonArea.subviews.forEach {
($0 as? Button)?.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
}
}
}
private extension DialogController {
/**
Dismisses dialog.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the action is
triggered by a user interaction
- Parameter isAnimated: A boolean indicating if the dismissal should be animated.
- Parameter using button: A button triggering the dismissal.
*/
func dismiss(isTriggeredByUserInteraction: Bool, isAnimated: Bool, using button: Button? = nil) {
if isTriggeredByUserInteraction {
guard shouldDismissHandler?(dialogView, button) ?? true else {
return
}
}
presentingViewController?.dismiss(animated: isAnimated, completion: nil)
guard isTriggeredByUserInteraction, nil == button else {
return
}
didCancelHandler?()
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment