|
@@ -12,19 +12,25 @@
|
|
|
use it to create action sheets and present them in any view
|
|
|
controller, from any source view or bar button item.
|
|
|
|
|
|
- To create an action sheet, just call the initializer with a
|
|
|
- list of items and buttons and a block that should be called
|
|
|
- whenever an item is selected.
|
|
|
|
|
|
+ ## Creating action sheet instances
|
|
|
|
|
|
- ## Items
|
|
|
+ You create instances of this class by providing `init(...)`
|
|
|
+ with the items to present and an action to call whenever an
|
|
|
+ item is selected. If you must have an action sheet instance
|
|
|
+ before you can setup its items (this may happen when you're
|
|
|
+ subclassing), you can setup the items afterwards by calling
|
|
|
+ the `setup(items:)` function.
|
|
|
|
|
|
- You provide an action sheet with a collection of items when
|
|
|
- you create it. The sheet will automatically split the items
|
|
|
- into items and buttons. You can also create an action sheet
|
|
|
- with an empty item collection, then call `setup(items:)` at
|
|
|
- a later time. This is sometimes required if you must create
|
|
|
- the action sheet before you can create the items.
|
|
|
+
|
|
|
+ ## Subclassing
|
|
|
+
|
|
|
+ This class can be subclassed, which is a good practice when
|
|
|
+ you want to use your own models in a controlled way. If you
|
|
|
+ have a podcast app, you could have a `SleepTimerActionSheet`
|
|
|
+ that automatically sets up its `SleepTimerTime` options and
|
|
|
+ streamlines how you work with a sleep timer. This is a good
|
|
|
+ way to setup the base action sheet for specific use cases.
|
|
|
|
|
|
|
|
|
## Presentation
|
|
@@ -32,28 +38,16 @@
|
|
|
You can inject a custom presenter if you want to change how
|
|
|
the sheet is presented and dismissed. The default presenter
|
|
|
for iPhone devices is `ActionSheetStandardPresenter`, while
|
|
|
- iPad devices get `ActionSheetPopoverPresenter` instead.
|
|
|
-
|
|
|
-
|
|
|
- ## Subclassing
|
|
|
-
|
|
|
- `ActionSheet` can be subclassed, which may be nice whenever
|
|
|
- you want to use your own domain model. For instance, if you
|
|
|
- want to present a list of `Food` items, you should create a
|
|
|
- `FoodActionSheet` sheet, then populate it with `Food` items.
|
|
|
- The selected value will then be of the type `Food`. You can
|
|
|
- either override the initializers or the `setup` function to
|
|
|
- change how you populate the sheet with items.
|
|
|
+ iPad devices most often get an `ActionSheetPopoverPresenter`.
|
|
|
|
|
|
|
|
|
## Appearance
|
|
|
|
|
|
- Sheeeeeeeeet's action sheet appearance if easily customized.
|
|
|
- To change the global appearance for every sheet in your app,
|
|
|
- just modify `ActionSheetAppearance.standard`. To change the
|
|
|
- appearance of a single action sheet, modify the `appearance`
|
|
|
- property. To change the appearance of a single item, modify
|
|
|
- its `customAppearance` property.
|
|
|
+ To change the global appearance for all action sheets, just
|
|
|
+ modify the `ActionSheetAppearance.standard` to look the way
|
|
|
+ you want. To change the appearance of a single action sheet,
|
|
|
+ modify its `appearance` property. To change the appearances
|
|
|
+ of single items, modify their `customAppearance` property.
|
|
|
|
|
|
|
|
|
## Handling item selections
|
|
@@ -61,14 +55,14 @@
|
|
|
The `selectAction` is triggered when a user taps an item in
|
|
|
the action sheet. It provides you with the action sheet and
|
|
|
the selected item. It is very important to use `[weak self]`
|
|
|
- in this block to avoid memory leaks.
|
|
|
+ in this block, to avoid memory leaks.
|
|
|
|
|
|
|
|
|
## Handling item taps
|
|
|
|
|
|
Action sheets receive a call to `handleTap(on:)` every time
|
|
|
- an item is tapped. You can override it when you create your
|
|
|
- own action sheet subclasses, but you probably shouldn't.
|
|
|
+ an item is tapped. You can override it if you, for instance,
|
|
|
+ want to perform any animations before calling `super`.
|
|
|
|
|
|
*/
|
|
|
|
|
@@ -77,28 +71,33 @@ import UIKit
|
|
|
open class ActionSheet: UIViewController {
|
|
|
|
|
|
|
|
|
+ // MARK: - Deprecated Members
|
|
|
+
|
|
|
+ @available(*, deprecated, message: "setupItemsAndButtons(with:) is deprecated and will be removed shortly. Use `setup(items:)` instead")
|
|
|
+ open func setupItemsAndButtons(with items: [ActionSheetItem]) { setup(items: items) }
|
|
|
+
|
|
|
+ @available(*, deprecated, message: "itemSelectAction is deprecated and will be removed in shortly. Use `selectAction` instead")
|
|
|
+ open var itemSelectAction: SelectAction { return selectAction }
|
|
|
+
|
|
|
+
|
|
|
// MARK: - Initialization
|
|
|
|
|
|
public init(
|
|
|
- items: [ActionSheetItem],
|
|
|
+ items: [ActionSheetItem] = [],
|
|
|
presenter: ActionSheetPresenter = ActionSheet.defaultPresenter,
|
|
|
action: @escaping SelectAction) {
|
|
|
self.presenter = presenter
|
|
|
selectAction = action
|
|
|
- super.init(nibName: ActionSheet.className, bundle: Bundle(for: ActionSheet.self))
|
|
|
+ super.init(nibName: ActionSheet.className, bundle: ActionSheet.bundle)
|
|
|
setup(items: items)
|
|
|
- setup()
|
|
|
}
|
|
|
|
|
|
public required init?(coder aDecoder: NSCoder) {
|
|
|
presenter = ActionSheet.defaultPresenter
|
|
|
selectAction = { _, _ in print("itemSelectAction is not set") }
|
|
|
super.init(coder: aDecoder)
|
|
|
- setup()
|
|
|
}
|
|
|
|
|
|
- deinit { print("\(type(of: self)) deinit") }
|
|
|
-
|
|
|
|
|
|
// MARK: - Setup
|
|
|
|
|
@@ -110,14 +109,16 @@ open class ActionSheet: UIViewController {
|
|
|
reloadData()
|
|
|
}
|
|
|
|
|
|
- @available(*, deprecated, message: "setupItemsAndButtons(with:) is deprecated. Use setup(items:) instead")
|
|
|
- open func setupItemsAndButtons(with items: [ActionSheetItem]) {
|
|
|
- setup(items: items)
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
// MARK: - View Controller Lifecycle
|
|
|
|
|
|
+ open override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+ setup()
|
|
|
+ setup(itemsTableView, with: itemHandler)
|
|
|
+ setup(buttonsTableView, with: buttonHandler)
|
|
|
+ }
|
|
|
+
|
|
|
open override func viewDidLayoutSubviews() {
|
|
|
super.viewDidLayoutSubviews()
|
|
|
refresh()
|
|
@@ -129,83 +130,61 @@ open class ActionSheet: UIViewController {
|
|
|
public typealias SelectAction = (ActionSheet, ActionSheetItem) -> ()
|
|
|
|
|
|
|
|
|
- // MARK: - Properties
|
|
|
-
|
|
|
- open var appearance = ActionSheetAppearance(copy: .standard)
|
|
|
+ // MARK: - Init properties
|
|
|
|
|
|
- public let presenter: ActionSheetPresenter
|
|
|
-
|
|
|
+ public var presenter: ActionSheetPresenter
|
|
|
public var selectAction: SelectAction
|
|
|
|
|
|
- @available(*, deprecated, message: "itemSelectAction is deprecated. Use selectAction instead")
|
|
|
- open var itemSelectAction: SelectAction { return selectAction }
|
|
|
-
|
|
|
|
|
|
- // MARK: - Margin Outlets
|
|
|
+ // MARK: - Appearance
|
|
|
|
|
|
- @IBOutlet weak var topMargin: NSLayoutConstraint?
|
|
|
- @IBOutlet weak var leftMargin: NSLayoutConstraint?
|
|
|
- @IBOutlet weak var rightMargin: NSLayoutConstraint?
|
|
|
- @IBOutlet weak var bottomMargin: NSLayoutConstraint?
|
|
|
+ public var appearance = ActionSheetAppearance(copy: .standard)
|
|
|
|
|
|
|
|
|
- // MARK: - View Outlets
|
|
|
+ // MARK: - Outlets
|
|
|
|
|
|
@IBOutlet weak var backgroundView: UIView?
|
|
|
@IBOutlet weak var stackView: UIStackView?
|
|
|
|
|
|
+ @IBOutlet weak var topMargin: NSLayoutConstraint?
|
|
|
+ @IBOutlet weak var leftMargin: NSLayoutConstraint?
|
|
|
+ @IBOutlet weak var rightMargin: NSLayoutConstraint?
|
|
|
+ @IBOutlet weak var bottomMargin: NSLayoutConstraint?
|
|
|
+
|
|
|
|
|
|
// MARK: - Header Properties
|
|
|
|
|
|
- open var headerView: UIView? {
|
|
|
- didSet { refresh() }
|
|
|
- }
|
|
|
+ open var headerView: UIView?
|
|
|
|
|
|
- @IBOutlet weak var headerViewContainer: UIView? {
|
|
|
- didSet {
|
|
|
- headerViewContainer?.backgroundColor = .clear
|
|
|
- refreshHeaderVisibility()
|
|
|
- }
|
|
|
- }
|
|
|
+ @IBOutlet weak var headerViewContainer: UIView?
|
|
|
|
|
|
- @IBOutlet weak var headerViewContainerHeight: NSLayoutConstraint! {
|
|
|
- didSet { refreshHeaderVisibility() }
|
|
|
- }
|
|
|
+ @IBOutlet weak var headerViewContainerHeight: NSLayoutConstraint?
|
|
|
|
|
|
|
|
|
// MARK: - Item Properties
|
|
|
|
|
|
- public var items = [ActionSheetItem]()
|
|
|
+ public internal(set) var items = [ActionSheetItem]()
|
|
|
|
|
|
public var itemsHeight: CGFloat { return totalHeight(for: items) }
|
|
|
|
|
|
public lazy var itemHandler = ActionSheetItemHandler(actionSheet: self, itemType: .items)
|
|
|
|
|
|
- @IBOutlet weak var itemsTableView: ActionSheetTableView? {
|
|
|
- didSet { setup(itemsTableView, with: itemHandler) }
|
|
|
- }
|
|
|
+ @IBOutlet weak var itemsTableView: ActionSheetTableView?
|
|
|
|
|
|
@IBOutlet weak var itemsTableViewHeight: NSLayoutConstraint?
|
|
|
|
|
|
|
|
|
// MARK: - Button Properties
|
|
|
|
|
|
- public var buttons = [ActionSheetButton]()
|
|
|
+ public internal(set) var buttons = [ActionSheetButton]()
|
|
|
|
|
|
public var buttonsHeight: CGFloat { return totalHeight(for: buttons) }
|
|
|
|
|
|
public lazy var buttonHandler = ActionSheetItemHandler(actionSheet: self, itemType: .buttons)
|
|
|
|
|
|
- @IBOutlet weak var buttonsTableView: ActionSheetTableView? {
|
|
|
- didSet {
|
|
|
- setup(buttonsTableView, with: buttonHandler)
|
|
|
- refreshButtonsVisibility()
|
|
|
- }
|
|
|
- }
|
|
|
+ @IBOutlet weak var buttonsTableView: ActionSheetTableView?
|
|
|
|
|
|
- @IBOutlet weak var buttonsTableViewHeight: NSLayoutConstraint? {
|
|
|
- didSet { refreshButtonsVisibility() }
|
|
|
- }
|
|
|
+ @IBOutlet weak var buttonsTableViewHeight: NSLayoutConstraint?
|
|
|
|
|
|
|
|
|
// MARK: - Presentation Functions
|
|
@@ -219,9 +198,9 @@ open class ActionSheet: UIViewController {
|
|
|
presenter.present(sheet: self, in: vc.rootViewController, from: view, completion: completion)
|
|
|
}
|
|
|
|
|
|
- open func present(in vc: UIViewController, from barButtonItem: UIBarButtonItem, completion: @escaping () -> () = {}) {
|
|
|
+ open func present(in vc: UIViewController, from item: UIBarButtonItem, completion: @escaping () -> () = {}) {
|
|
|
refresh()
|
|
|
- presenter.present(sheet: self, in: vc.rootViewController, from: barButtonItem, completion: completion)
|
|
|
+ presenter.present(sheet: self, in: vc.rootViewController, from: item, completion: completion)
|
|
|
}
|
|
|
|
|
|
|
|
@@ -237,17 +216,13 @@ open class ActionSheet: UIViewController {
|
|
|
}
|
|
|
|
|
|
open func refreshHeader() {
|
|
|
- refreshHeaderVisibility()
|
|
|
let height = headerView?.frame.height ?? 0
|
|
|
headerViewContainerHeight?.constant = height
|
|
|
+ headerViewContainer?.isHidden = headerView == nil
|
|
|
guard let view = headerView else { return }
|
|
|
headerViewContainer?.addSubviewToFill(view)
|
|
|
}
|
|
|
|
|
|
- open func refreshHeaderVisibility() {
|
|
|
- headerViewContainer?.isHidden = headerView == nil
|
|
|
- }
|
|
|
-
|
|
|
open func refreshItems() {
|
|
|
items.forEach { $0.applyAppearance(appearance) }
|
|
|
itemsTableView?.backgroundColor = appearance.itemsBackgroundColor
|
|
@@ -256,26 +231,20 @@ open class ActionSheet: UIViewController {
|
|
|
}
|
|
|
|
|
|
open func refreshButtons() {
|
|
|
- refreshButtonsVisibility()
|
|
|
+ buttonsTableView?.isHidden = buttons.count == 0
|
|
|
buttons.forEach { $0.applyAppearance(appearance) }
|
|
|
buttonsTableView?.backgroundColor = appearance.buttonsBackgroundColor
|
|
|
buttonsTableView?.separatorColor = appearance.buttonsSeparatorColor
|
|
|
buttonsTableViewHeight?.constant = buttonsHeight
|
|
|
}
|
|
|
|
|
|
- open func refreshButtonsVisibility() {
|
|
|
- buttonsTableView?.isHidden = buttons.count == 0
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
// MARK: - Protected Functions
|
|
|
|
|
|
open func handleTap(on item: ActionSheetItem) {
|
|
|
reloadData()
|
|
|
- guard item.tapBehavior == .dismiss else { return selectAction(self, item) }
|
|
|
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
|
|
|
- self.dismiss { self.selectAction(self, item) }
|
|
|
- }
|
|
|
+ if item.tapBehavior != .dismiss { return selectAction(self, item) }
|
|
|
+ self.dismiss { self.selectAction(self, item) }
|
|
|
}
|
|
|
|
|
|
open func margin(at margin: ActionSheetMargin) -> CGFloat {
|
|
@@ -310,10 +279,6 @@ private extension ActionSheet {
|
|
|
tableView?.delegate = handler
|
|
|
tableView?.dataSource = handler
|
|
|
tableView?.alwaysBounceVertical = false
|
|
|
- setupAppearance(for: tableView)
|
|
|
- }
|
|
|
-
|
|
|
- func setupAppearance(for tableView: UITableView?) {
|
|
|
tableView?.estimatedRowHeight = 44
|
|
|
tableView?.rowHeight = UITableView.automaticDimension
|
|
|
tableView?.cellLayoutMarginsFollowReadableWidth = false
|