Browse Source

update carthage

Marino Faggiana 6 years ago
parent
commit
709d5c6151
82 changed files with 1948 additions and 1692 deletions
  1. 1 1
      Cartfile
  2. 1 1
      Cartfile.resolved
  3. 1 1
      Carthage/Checkouts/Sheeeeeeeeet/.travis.yml
  4. 1 2
      Carthage/Checkouts/Sheeeeeeeeet/Fastlane/Fastfile
  5. 60 2
      Carthage/Checkouts/Sheeeeeeeeet/RELEASE_NOTES.md
  6. 1 1
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet.podspec
  7. 63 56
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.swift
  8. 4 4
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.xib
  9. 5 2
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetItemHandler.swift
  10. 5 6
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetMargin.swift
  11. 0 109
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetAppearance.swift
  12. 0 54
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetItemAppearance.swift
  13. 0 28
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetPopoverAppearance.swift
  14. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetButtonAppearance.swift
  15. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetCancelButtonAppearance.swift
  16. 0 22
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetDangerButtonAppearance.swift
  17. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetOkButtonAppearance.swift
  18. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetCollectionItemAppearance.swift
  19. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetCustomItemAppearance.swift
  20. 0 30
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetLinkItemAppearance.swift
  21. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetMultiSelectItemAppearance.swift
  22. 0 32
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetMultiSelectToggleItemAppearance.swift
  23. 0 53
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetSelectItemAppearance.swift
  24. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetSingleSelectItemAppearance.swift
  25. 0 25
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetSectionMarginAppearance.swift
  26. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetSectionTitleAppearance.swift
  27. 0 11
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetTitleAppearance.swift
  28. 3 0
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/NSObject+ClassName.swift
  29. 4 1
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIView+Nib.swift
  30. 3 0
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIView+Subviews.swift
  31. 3 0
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIViewController+RootViewController.swift
  32. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Info.plist
  33. 116 37
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/ActionSheetItem.swift
  34. 21 7
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetButton.swift
  35. 16 3
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetCancelButton.swift
  36. 18 4
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetDangerButton.swift
  37. 17 4
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetOkButton.swift
  38. 60 15
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetCollectionItem.swift
  39. 28 14
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetCustomItem.swift
  40. 25 9
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetLinkItem.swift
  41. 14 3
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectItem.swift
  42. 42 15
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectToggleItem.swift
  43. 84 17
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetSelectItem.swift
  44. 15 2
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetSingleSelectItem.swift
  45. 15 7
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetSectionMargin.swift
  46. 15 8
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetSectionTitle.swift
  47. 19 6
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetTitle.swift
  48. 6 16
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPopoverPresenter.swift
  49. 6 9
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPresenter.swift
  50. 14 7
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetStandardPresenter.swift
  51. 0 34
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemCell.swift
  52. 0 46
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemCell.xib
  53. 0 27
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemContentCell.swift
  54. 0 22
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCustomItemCell.swift
  55. 0 19
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetItemCell.swift
  56. 89 63
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/AppDelegate+Appearance.swift
  57. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Info.plist
  58. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheet+PresenterTests.swift
  59. 21 17
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetItemHandlerTests.swift
  60. 14 7
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetMarginTests.swift
  61. 358 300
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetTests.swift
  62. 22 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/MockActionSheet.swift
  63. 4 4
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Extensions/NSObject+ClassNameTests.swift
  64. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Info.plist
  65. 90 135
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/ActionSheetItemTests.swift
  66. 41 38
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetButtonTests.swift
  67. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetCancelButtonTests.swift
  68. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetDangerButtonTests.swift
  69. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetOkButtonTests.swift
  70. 24 10
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetLinkItemTests.swift
  71. 46 15
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectItemTests.swift
  72. 59 11
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectToggleItemTests.swift
  73. 83 38
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSelectItemTests.swift
  74. 29 63
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSingleSelectItemTests.swift
  75. 2 17
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Mocks/MockActionSheetItem.swift
  76. 15 11
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionMarginTests.swift
  77. 13 14
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionTitleTests.swift
  78. 13 15
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetTitleTests.swift
  79. 175 4
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetPopoverPresenterTests.swift
  80. 81 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetStandardPresenterTests.swift
  81. 20 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Views/MockTableView.swift
  82. 7 0
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

+ 1 - 1
Cartfile

@@ -1,6 +1,6 @@
 github "tilltue/TLPhotoPicker"
 github "kishikawakatsumi/UICKeyChainStore"
-github "danielsaidi/Sheeeeeeeeet"
+github "danielsaidi/Sheeeeeeeeet" == 1.2.2
 github "sgr-ksmt/PDFGenerator" ~> 2.1
 github "MortimerGoro/MGSwipeTableCell"
 github "dzenbot/DZNEmptyDataSet"

+ 1 - 1
Cartfile.resolved

@@ -4,7 +4,7 @@ github "MortimerGoro/MGSwipeTableCell" "1.6.8"
 github "SVGKit/SVGKit" "bfe62f6d45f55c9fe39619d9e91fed1844c59da6"
 github "WeTransfer/WeScan" "v0.9.1"
 github "calimarkus/JDStatusBarNotification" "1.6.0"
-github "danielsaidi/Sheeeeeeeeet" "1.1.0"
+github "danielsaidi/Sheeeeeeeeet" "1.2.2"
 github "dzenbot/DZNEmptyDataSet" "v1.8.1"
 github "ealeksandrov/EAIntroView" "2.12.0"
 github "ealeksandrov/EARestrictedScrollView" "1.1.0"

+ 1 - 1
Carthage/Checkouts/Sheeeeeeeeet/.travis.yml

@@ -11,5 +11,5 @@ before_install:
 
 script:
   - set -o pipefail && xcodebuild -project "Sheeeeeeeeet.xcodeproj" -scheme "Sheeeeeeeeet" -sdk "iphonesimulator" | xcpretty -c
-  - pod lib lint
+  - pod lib lint --allow-warnings
   - fastlane test

+ 1 - 2
Carthage/Checkouts/Sheeeeeeeeet/Fastlane/Fastfile

@@ -14,7 +14,6 @@ platform :ios do
     test
     version = version_bump_podspec(path: "Sheeeeeeeeet.podspec", version_number: options[:name])
     increment_version_number(version_number: version)
-    increment_build_number(build_number: `date "+%y%m%d%H%M"`)
 
     git_commit(
       path: [
@@ -28,7 +27,7 @@ platform :ios do
     add_git_tag(tag: version)
     push_git_tags()
     push_to_git_remote()
-    pod_push()
+#pod_push()
   end
 
   

+ 60 - 2
Carthage/Checkouts/Sheeeeeeeeet/RELEASE_NOTES.md

@@ -1,6 +1,61 @@
 # Release Notes
 
 
+## 1.2.2
+
+This hotfix adds two new properties to `ActionSheetSelectItem`, that can be used
+to style the selected fonts: `selectedTitleFont` and `selectedSubtitleFont`.
+
+
+## 1.2.1
+
+This hotfix fixes a font bug in the title item and color bugs in the select item. 
+
+
+## 1.2.0
+
+This is a huge update, that completely rewrites how action sheet appearances are
+handled. Instead of the old appearance model, Sheeeeeeeeet now relies on the iOS
+appearance proxy model as much as possible.
+
+The old appearance model is still around, but has been marked as deprecated, and
+will be removed in `1.4.0`. Make sure that you switch over to the new appearance
+model as soon as possible. Have a look at the example app and [here][Appearance]
+to see how you should customize the action sheet appearance from now on.
+
+In short, item appearance customizations are handled in three different ways now:
+
+* Item appearances such as colors and fonts, are customized with cell properties,
+for instance: `ActionSheetSelectItemCell.appearance().titleColor = .green`.
+* Item heights are now customized by setting the `height` property of every item
+type you want to customize, for instance: `ActionSheetTitle.height = 22`.
+* Action sheet margins, insets etc. are now customized by setting the properties
+of each `ActionSheet` instance. If you want to change the default values for all
+action sheets in your app, you have to subclass `ActionSheet`.
+
+All built-in action sheet items now have their own cells. Your custom items only
+have to use custom cells if you want to apply custom item appearances to them.
+
+Sheeeeeeeeet now contains several new views, which are used by the action sheets:
+
+  * `ActionSheetTableView`
+  * `ActionSheetItemTableView`
+  * `ActionSheetButtonTableView`
+  * `ActionSheetBackgroundView`
+  * `ActionSheetStackView`
+
+The new classes make it easy to modify the appearance of these views, since they
+have appearance properties as well. For instance, to change the corner radius of
+the table views, just type: `ActionSheetTableView.appearance().cornerRadius = 8`.
+
+`ActionSheet` has two new extensions: 
+  * `items<T>(ofType:)`
+  * `scrollToFirstSelectedItem(at:)`
+
+This new version has also rebuilt all unit tests from scratch. They are now more
+robust and easier to maintain.
+
+
 ## 1.1.0
 
 This version increases the action sheet integrity by restricting what you can do
@@ -8,7 +63,7 @@ with it. This involves some breaking changes, but they should not affect you. If
 you think any new rule is bad or affect you, please let me know.
 
 
-### New Features
+**New Features**
 
 @sebbo176 has added support for subtitles in the various select items, which now
 also changes the cell style of an item if the subtitle is set. He has also added
@@ -16,7 +71,7 @@ an unselected icon to the select items, which means that you can now have images
 for unselected items as well (e.g. an unchecked checkbox).
 
 
-### Breaking Changes - ActionSheet:
+**Breaking Changes - ActionSheet:**
 
 * The `items` and `buttons` properties are now `internal(set)`, which means that
 they can only be set with `init(...)` or with `setup(items:)`. This protects the
@@ -377,3 +432,6 @@ Select items can now have a separate select tint color for the left icon.
 
 We have added a subtitle to the section title item and clarified the examples by
 moving action sheets into their own separate classes.
+
+
+[Appearance]: https://github.com/danielsaidi/Sheeeeeeeeet/blob/master/Readmes/Appearance.md

+ 1 - 1
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet.podspec

@@ -2,7 +2,7 @@
 
 Pod::Spec.new do |s|
   s.name             = 'Sheeeeeeeeet'
-  s.version          = '1.1.0.1'
+  s.version          = '1.2.2'
   s.summary          = 'Sheeeeeeeeet is a Swift library for custom iOS action sheets.'
 
   s.description      = <<-DESC

+ 63 - 56
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.swift

@@ -29,8 +29,35 @@
  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.
+ streamlines how you work with a sleep timer.
+ 
+ 
+ ## Appearance
+ 
+ Customizing the appearance of the various action sheet item
+ types in Sheeeeeeeeet (as well as of your own custom items),
+ is mainly done using the iOS appearance proxy for each item
+ cell type. For instance, to change the title text color for
+ all `ActionSheetSelectItem` instances (including subclasses),
+ type `ActionSheetSelectItem.appearance().titleColor`. It is
+ also possible to set these properties for each item as well.
+ 
+ While most appearance is modified on a cell level, some are
+ not. For instance, some views in `Views` have apperances of
+ their own (e.g. `ActionSheetHeaderView.cornerRadius`). This
+ means that you can change more than cell appearance. Have a
+ look at the readme for more info on what you can customize.
+ 
+ Action sheet insets, margins and widths are not part of the
+ appearance model, but have to be changed for each sheet. If
+ you want to change these values for each sheet in youer app,
+ I recommend subclassing `ActionSheet` and set these values.
+ 
+ Neither item heights are part of the appearance model. Item
+ heights are instead changed by setting the static height of
+ each item type, e.g. `ActionSheetTitleItem.height = 20`. It
+ is not part of the cell appearance model since an item must
+ know about the height before it creates any cells.
  
  
  ## Presentation
@@ -38,16 +65,7 @@
  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 most often get an `ActionSheetPopoverPresenter`.
- 
- 
- ## Appearance
- 
- 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.
+ iPad devices (most often) use `ActionSheetPopoverPresenter`.
  
  
  ## Handling item selections
@@ -55,7 +73,7 @@
  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 these action closures, to avoid memory leaks.
  
  
  ## Handling item taps
@@ -71,13 +89,14 @@ 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")
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "appearance will be removed in 1.4.0. Use the new appearance model instead")
+    public var appearance = ActionSheetAppearance(copy: .standard)
+    @available(*, deprecated, message: "setupItemsAndButtons(with:) will be removed in 1.4.0. 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")
+    @available(*, deprecated, message: "itemSelectAction will be removed in 1.4.0. Use `selectAction` instead")
     open var itemSelectAction: SelectAction { return selectAction }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
     
     
     // MARK: - Initialization
@@ -101,7 +120,9 @@ open class ActionSheet: UIViewController {
     
     // MARK: - Setup
     
-    open func setup() {}
+    open func setup() {
+        preferredContentSize.width = preferredPopoverWidth
+    }
     
     open func setup(items: [ActionSheetItem]) {
         self.items = items.filter { !($0 is ActionSheetButton) }
@@ -138,13 +159,21 @@ open class ActionSheet: UIViewController {
     
     // MARK: - Appearance
     
-    public var appearance = ActionSheetAppearance(copy: .standard)
+    public var minimumContentInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
+    public var preferredPopoverWidth: CGFloat = 300
+    public var sectionMargins: CGFloat = 15
     
     
     // MARK: - Outlets
     
-    @IBOutlet weak var backgroundView: UIView?
+    @IBOutlet weak var backgroundView: ActionSheetBackgroundView?
     @IBOutlet weak var stackView: UIStackView?
+    @IBOutlet weak var headerViewContainer: ActionSheetHeaderView?
+    @IBOutlet weak var headerViewContainerHeight: NSLayoutConstraint?
+    @IBOutlet weak var itemsTableView: ActionSheetItemTableView?
+    @IBOutlet weak var itemsTableViewHeight: NSLayoutConstraint?
+    @IBOutlet weak var buttonsTableView: ActionSheetButtonTableView?
+    @IBOutlet weak var buttonsTableViewHeight: NSLayoutConstraint?
     
     @IBOutlet weak var topMargin: NSLayoutConstraint?
     @IBOutlet weak var leftMargin: NSLayoutConstraint?
@@ -156,10 +185,6 @@ open class ActionSheet: UIViewController {
     
     open var headerView: UIView?
     
-    @IBOutlet weak var headerViewContainer: UIView?
-    
-    @IBOutlet weak var headerViewContainerHeight: NSLayoutConstraint?
-    
     
     // MARK: - Item Properties
     
@@ -169,10 +194,6 @@ open class ActionSheet: UIViewController {
     
     public lazy var itemHandler = ActionSheetItemHandler(actionSheet: self, itemType: .items)
     
-    @IBOutlet weak var itemsTableView: ActionSheetTableView?
-    
-    @IBOutlet weak var itemsTableViewHeight: NSLayoutConstraint?
-    
     
     // MARK: - Button Properties
     
@@ -182,10 +203,6 @@ open class ActionSheet: UIViewController {
     
     public lazy var buttonHandler = ActionSheetItemHandler(actionSheet: self, itemType: .buttons)
     
-    @IBOutlet weak var buttonsTableView: ActionSheetTableView?
-    
-    @IBOutlet weak var buttonsTableViewHeight: NSLayoutConstraint?
-    
     
     // MARK: - Presentation Functions
     
@@ -207,11 +224,11 @@ open class ActionSheet: UIViewController {
     // MARK: - Refresh Functions
     
     open func refresh() {
-        applyRoundCorners()
+        applyLegacyAppearance()
         refreshHeader()
         refreshItems()
         refreshButtons()
-        stackView?.spacing = appearance.groupMargins
+        stackView?.spacing = sectionMargins
         presenter.refreshActionSheet()
     }
     
@@ -224,17 +241,13 @@ open class ActionSheet: UIViewController {
     }
     
     open func refreshItems() {
-        items.forEach { $0.applyAppearance(appearance) }
-        itemsTableView?.backgroundColor = appearance.itemsBackgroundColor
-        itemsTableView?.separatorColor = appearance.itemsSeparatorColor
+        items.forEach { $0.applyAppearance(appearance) }    // TODO: Deprecated - Remove in 1.4.0
         itemsTableViewHeight?.constant = itemsHeight
     }
     
     open func refreshButtons() {
         buttonsTableView?.isHidden = buttons.count == 0
-        buttons.forEach { $0.applyAppearance(appearance) }
-        buttonsTableView?.backgroundColor = appearance.buttonsBackgroundColor
-        buttonsTableView?.separatorColor = appearance.buttonsSeparatorColor
+        buttons.forEach { $0.applyAppearance(appearance) }  // TODO: Deprecated - Remove in 1.4.0
         buttonsTableViewHeight?.constant = buttonsHeight
     }
     
@@ -248,8 +261,13 @@ open class ActionSheet: UIViewController {
     }
     
     open func margin(at margin: ActionSheetMargin) -> CGFloat {
-        let minimum = appearance.contentInset
-        return margin.value(in: view.superview, minimum: minimum)
+        let view: UIView! = self.view.superview ?? self.view
+        switch margin {
+        case .top: return margin.value(in: view, minimum: minimumContentInsets.top)
+        case .left: return margin.value(in: view, minimum: minimumContentInsets.left)
+        case .right: return margin.value(in: view, minimum: minimumContentInsets.right)
+        case .bottom: return margin.value(in: view, minimum: minimumContentInsets.bottom)
+        }
     }
 
     open func reloadData() {
@@ -263,18 +281,6 @@ open class ActionSheet: UIViewController {
 
 private extension ActionSheet {
     
-    func applyRoundCorners() {
-        applyRoundCorners(to: headerView)
-        applyRoundCorners(to: headerViewContainer)
-        applyRoundCorners(to: itemsTableView)
-        applyRoundCorners(to: buttonsTableView)
-    }
-
-    func applyRoundCorners(to view: UIView?) {
-        view?.clipsToBounds = true
-        view?.layer.cornerRadius = appearance.cornerRadius
-    }
-    
     func setup(_ tableView: UITableView?, with handler: ActionSheetItemHandler) {
         tableView?.delegate = handler
         tableView?.dataSource = handler
@@ -285,6 +291,7 @@ private extension ActionSheet {
     }
     
     func totalHeight(for items: [ActionSheetItem]) -> CGFloat {
-        return items.reduce(0) { $0 + $1.appearance.height }
+        let height = items.reduce(0) { $0 + $1.height }
+        return height
     }
 }

+ 4 - 4
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.xib

@@ -32,28 +32,28 @@
             <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
             <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
             <subviews>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SY3-WL-g9a">
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SY3-WL-g9a" customClass="ActionSheetBackgroundView" customModule="Sheeeeeeeeet" customModuleProvider="target">
                     <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                     <color key="backgroundColor" white="0.0" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                 </view>
                 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="a3t-st-aTv">
                     <rect key="frame" x="0.0" y="277" width="375" height="390"/>
                     <subviews>
-                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Eio-5P-omJ">
+                        <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Eio-5P-omJ" customClass="ActionSheetHeaderView" customModule="Sheeeeeeeeet" customModuleProvider="target">
                             <rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
                             <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                             <constraints>
                                 <constraint firstAttribute="height" constant="150" id="ZKw-6e-7h3"/>
                             </constraints>
                         </view>
-                        <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="iQT-dy-e7d" customClass="ActionSheetTableView" customModule="Sheeeeeeeeet" customModuleProvider="target">
+                        <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="iQT-dy-e7d" customClass="ActionSheetItemTableView" customModule="Sheeeeeeeeet" customModuleProvider="target">
                             <rect key="frame" x="0.0" y="170" width="375" height="100"/>
                             <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                             <constraints>
                                 <constraint firstAttribute="height" priority="900" constant="100" id="929-kf-L2B"/>
                             </constraints>
                         </tableView>
-                        <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="4ww-lg-Kl7" customClass="ActionSheetTableView" customModule="Sheeeeeeeeet" customModuleProvider="target">
+                        <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" scrollEnabled="NO" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="4ww-lg-Kl7" customClass="ActionSheetButtonTableView" customModule="Sheeeeeeeeet" customModuleProvider="target">
                             <rect key="frame" x="0.0" y="290" width="375" height="100"/>
                             <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                             <constraints>

+ 5 - 2
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetItemHandler.swift

@@ -68,12 +68,15 @@ extension ActionSheetItemHandler: UITableViewDataSource {
     
     public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         guard let item = self.item(at: indexPath) else { return UITableViewCell(frame: .zero) }
-        return item.cell(for: tableView)
+        let cell = item.cell(for: tableView)
+        item.applyAppearance(to: cell)                      // TODO: Deprecated - Remove in 1.4.0
+        cell.refresh(with: item)
+        return cell
     }
     
     public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         guard let item = self.item(at: indexPath) else { return 0 }
-        return CGFloat(item.appearance.height)
+        return CGFloat(item.height)
     }
 }
 

+ 5 - 6
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetMargin.swift

@@ -9,10 +9,10 @@
 import UIKit
 
 public enum ActionSheetMargin {
+    
     case top, left, right, bottom
     
-    func value(in view: UIView?) -> CGFloat? {
-        guard let view = view else { return nil }
+    func value(in view: UIView) -> CGFloat {
         if #available(iOS 11.0, *) {
             let insets = view.safeAreaInsets
             switch self {
@@ -22,12 +22,11 @@ public enum ActionSheetMargin {
             case .bottom: return insets.bottom
             }
         } else {
-            return nil
+            return 0
         }
     }
     
-    func value(in view: UIView?, minimum: CGFloat) -> CGFloat {
-        guard let value = self.value(in: view) else { return minimum }
-        return max(value, minimum)
+    func value(in view: UIView, minimum: CGFloat) -> CGFloat {
+        return max(value(in: view), minimum)
     }
 }

+ 0 - 109
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetAppearance.swift

@@ -1,109 +0,0 @@
-//
-//  ActionSheetAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-18.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-/*
- 
- This class is used to specify the appearance for all action
- sheets and action sheet items provided by Sheeeeeeeeet. Use
- `ActionSheetAppearance.standard` to style all action sheets
- in an entire app. You can then apply individual appearances
- to individual action sheets and item types.
- 
- The `item` appearance property is used as the base template
- for all other item appearances.
- 
- */
-
-import UIKit
-
-open class ActionSheetAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public init() {}
-    
-    public init(copy: ActionSheetAppearance) {
-        cornerRadius = copy.cornerRadius
-        contentInset = copy.contentInset
-        groupMargins = copy.groupMargins
-        
-        backgroundColor = copy.backgroundColor
-        separatorColor = copy.separatorColor
-        itemsBackgroundColor = copy.itemsBackgroundColor ?? backgroundColor
-        itemsSeparatorColor = copy.itemsSeparatorColor ?? separatorColor
-        buttonsSeparatorColor = copy.buttonsSeparatorColor ?? backgroundColor
-        buttonsSeparatorColor = copy.buttonsSeparatorColor ?? separatorColor
-        
-        popover = ActionSheetPopoverAppearance(copy: copy.popover)
-        
-        item = ActionSheetItemAppearance(copy: copy.item)
-        collectionItem = ActionSheetCollectionItemAppearance(copy: copy.collectionItem)
-        customItem = ActionSheetCustomItemAppearance(copy: copy.customItem)
-        linkItem = ActionSheetLinkItemAppearance(copy: copy.linkItem)
-        multiSelectItem = ActionSheetMultiSelectItemAppearance(copy: copy.multiSelectItem)
-        multiSelectToggleItem = ActionSheetMultiSelectToggleItemAppearance(copy: copy.multiSelectToggleItem)
-        selectItem = ActionSheetSelectItemAppearance(copy: copy.selectItem)
-        singleSelectItem = ActionSheetSingleSelectItemAppearance(copy: copy.singleSelectItem)
-        
-        cancelButton = ActionSheetCancelButtonAppearance(copy: copy.cancelButton)
-        dangerButton = ActionSheetDangerButtonAppearance(copy: copy.dangerButton)
-        okButton = ActionSheetOkButtonAppearance(copy: copy.okButton)
-        
-        sectionMargin = ActionSheetSectionMarginAppearance(copy: copy.sectionMargin)
-        sectionTitle = ActionSheetSectionTitleAppearance(copy: copy.sectionTitle)
-        title = ActionSheetTitleAppearance(copy: copy.title)
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var cornerRadius: CGFloat = 10
-    public var contentInset: CGFloat = 15
-    public var groupMargins: CGFloat = 15
-    
-    public var backgroundColor: UIColor?
-    public var separatorColor: UIColor?
-    public var itemsBackgroundColor: UIColor?
-    public var itemsSeparatorColor: UIColor?
-    public var buttonsBackgroundColor: UIColor?
-    public var buttonsSeparatorColor: UIColor?
-    
-    
-    // MARK: - Appearance Properties
-    
-    public static var standard = ActionSheetAppearance()
-    
-    public lazy var popover = ActionSheetPopoverAppearance(width: 300)
-    
-    
-    // MARK: - Items
-    
-    public lazy var item = ActionSheetItemAppearance()
-    public lazy var collectionItem = ActionSheetCollectionItemAppearance(copy: item)
-    public lazy var customItem = ActionSheetCustomItemAppearance(copy: item)
-    public lazy var linkItem = ActionSheetLinkItemAppearance(copy: item)
-    public lazy var multiSelectItem = ActionSheetMultiSelectItemAppearance(copy: selectItem)
-    public lazy var multiSelectToggleItem = ActionSheetMultiSelectToggleItemAppearance(copy: item)
-    public lazy var selectItem = ActionSheetSelectItemAppearance(copy: item)
-    public lazy var singleSelectItem = ActionSheetSingleSelectItemAppearance(copy: selectItem)
-    
-    
-    // MARK: - Buttons
-    
-    public lazy var cancelButton = ActionSheetCancelButtonAppearance(copy: item)
-    public lazy var dangerButton = ActionSheetDangerButtonAppearance(copy: item)
-    public lazy var okButton = ActionSheetOkButtonAppearance(copy: item)
-    
-    
-    // MARK: - Titles
-    
-    public lazy var sectionMargin = ActionSheetSectionMarginAppearance(copy: item)
-    public lazy var sectionTitle = ActionSheetSectionTitleAppearance(copy: item)
-    public lazy var title = ActionSheetTitleAppearance(copy: item)
-}

+ 0 - 54
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetItemAppearance.swift

@@ -1,54 +0,0 @@
-//
-//  ActionSheetItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetItemAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public init() {}
-    
-    public init(copy: ActionSheetItemAppearance) {
-        backgroundColor = copy.backgroundColor
-        font = copy.font
-        height = copy.height
-        separatorInsets = copy.separatorInsets
-        textColor = copy.textColor
-        tintColor = copy.tintColor
-        subtitleFont = copy.subtitleFont
-        subtitleTextColor = copy.subtitleTextColor
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var backgroundColor: UIColor?
-    public var font: UIFont?
-    public var height: CGFloat = 50
-    public var separatorInsets: UIEdgeInsets = .zero
-    public var textColor: UIColor?
-    public var tintColor: UIColor?
-    public var subtitleFont: UIFont?
-    public var subtitleTextColor: UIColor?
-}
-
-
-// MARK: - Public Extensions
-
-public extension ActionSheetItemAppearance {
-    
-    public static var noSeparator: UIEdgeInsets {
-        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 100_000)
-    }
-    
-    public func hideSeparator() {
-        separatorInsets = ActionSheetItemAppearance.noSeparator
-    }
-}

+ 0 - 28
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetPopoverAppearance.swift

@@ -1,28 +0,0 @@
-//
-//  ActionSheetPopoverAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetPopoverAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public init(width: CGFloat) {
-        self.width = width
-    }
-    
-    public init(copy: ActionSheetPopoverAppearance) {
-        self.width = copy.width
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var width: CGFloat
-}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetButtonAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetButtonAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetButtonAppearance: ActionSheetItemAppearance {}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetCancelButtonAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetCancelButtonAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetCancelButtonAppearance: ActionSheetButtonAppearance {}

+ 0 - 22
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetDangerButtonAppearance.swift

@@ -1,22 +0,0 @@
-//
-//  ActionSheetDangerButtonAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-27.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetDangerButtonAppearance: ActionSheetButtonAppearance {
-    
-    public override init() {
-        super.init()
-        textColor = .red
-    }
-    
-    public override init(copy: ActionSheetItemAppearance) {
-        super.init(copy: copy)
-        textColor = .red
-    }
-}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Buttons/ActionSheetOkButtonAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetOkButtonAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetOkButtonAppearance: ActionSheetButtonAppearance {}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetCollectionItemAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetCollectionItemAppearance.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström (ullstrm) on 2018-02-23.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetCollectionItemAppearance: ActionSheetItemAppearance {}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetCustomItemAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetCustomItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2018-10-08.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetCustomItemAppearance: ActionSheetItemAppearance {}

+ 0 - 30
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetLinkItemAppearance.swift

@@ -1,30 +0,0 @@
-//
-//  ActionSheetLinkItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetLinkItemAppearance: ActionSheetItemAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public override init() {
-        super.init()
-    }
-    
-    public override init(copy: ActionSheetItemAppearance) {
-        super.init(copy: copy)
-        let copy = copy as? ActionSheetLinkItemAppearance
-        linkIcon = copy?.linkIcon
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var linkIcon: UIImage?
-}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetMultiSelectItemAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetMultiSelectItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2018-03-31.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetMultiSelectItemAppearance: ActionSheetSelectItemAppearance {}

+ 0 - 32
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetMultiSelectToggleItemAppearance.swift

@@ -1,32 +0,0 @@
-//
-//  ActionSheetMultiSelectToggleItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2018-03-31.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-
-open class ActionSheetMultiSelectToggleItemAppearance: ActionSheetItemAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public override init() {
-        super.init()
-    }
-    
-    public override init(copy: ActionSheetItemAppearance) {
-        super.init(copy: copy)
-        let copy = copy as? ActionSheetMultiSelectToggleItemAppearance
-        deselectAllTextColor = copy?.deselectAllTextColor
-        selectAllTextColor = copy?.selectAllTextColor
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var deselectAllTextColor: UIColor?
-    public var selectAllTextColor: UIColor?
-}

+ 0 - 53
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetSelectItemAppearance.swift

@@ -1,53 +0,0 @@
-//
-//  ActionSheetSelectItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-/*
- 
- This appearance inherits the base appearance and applies to
- select items. The additional properties are applied when an
- item is selected:
- 
- * `selectedIcon` is displayed rightmost, e.g. a checkmark
- * `selectedTextColor` is applied to the text (duh)
- * `selectedTintColor` is applied to both icons if they are rendered as template images
- * `selectedIconTintColor` can override `selectedTintColor` for the selected icon
- 
- */
-
-import UIKit
-
-open class ActionSheetSelectItemAppearance: ActionSheetItemAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public override init() {
-        super.init()
-    }
-    
-    public override init(copy: ActionSheetItemAppearance) {
-        super.init(copy: copy)
-        selectedTextColor = copy.textColor
-        selectedTintColor = copy.tintColor
-        let copy = copy as? ActionSheetSelectItemAppearance
-        selectedIcon = copy?.selectedIcon
-        selectedTextColor = copy?.selectedTextColor ?? selectedTextColor
-        selectedTintColor = copy?.selectedTintColor ?? selectedTintColor
-        selectedIconTintColor = copy?.selectedIconTintColor ?? selectedTintColor
-        unselectedIcon = copy?.unselectedIcon
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var selectedIcon: UIImage?
-    public var selectedIconTintColor: UIColor?
-    public var selectedTextColor: UIColor?
-    public var selectedTintColor: UIColor?
-    public var unselectedIcon: UIImage?
-}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Items/ActionSheetSingleSelectItemAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetSingleSelectItemAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2018-03-19.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetSingleSelectItemAppearance: ActionSheetSelectItemAppearance {}

+ 0 - 25
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetSectionMarginAppearance.swift

@@ -1,25 +0,0 @@
-//
-//  ActionSheetSectionMarginAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-27.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetSectionMarginAppearance: ActionSheetItemAppearance {
-    
-    
-    // MARK: - Initialization
-    
-    public override init() {
-        super.init()
-        hideSeparator()
-    }
-    
-    public override init(copy: ActionSheetItemAppearance) {
-        super.init(copy: copy)
-        hideSeparator()
-    }
-}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetSectionTitleAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetSectionTitleAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-19.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetSectionTitleAppearance: ActionSheetItemAppearance {}

+ 0 - 11
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/Titles/ActionSheetTitleAppearance.swift

@@ -1,11 +0,0 @@
-//
-//  ActionSheetTitleAppearance.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-19.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-import Foundation
-
-open class ActionSheetTitleAppearance: ActionSheetItemAppearance { }

+ 3 - 0
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/NSObject+ClassName.swift

@@ -6,6 +6,9 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
+//  This file contains internal util functions for resolving
+//  the class name of classes and class instances.
+
 import UIKit
 
 extension NSObject {

+ 4 - 1
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIView+Nib.swift

@@ -6,11 +6,14 @@
 //  Copyright © 2018 Daniel Saidi. All rights reserved.
 //
 
+//  This file contains internal util functions for resolving
+//  the default nib of a certain view instance.
+
 import UIKit
 
 extension UIView {
     
     static var defaultNib: UINib {
-        return UINib(nibName: className, bundle: self.bundle)
+        return UINib(nibName: className, bundle: bundle)
     }
 }

+ 3 - 0
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIView+Subviews.swift

@@ -6,6 +6,9 @@
 //  Copyright © 2018 Daniel Saidi. All rights reserved.
 //
 
+//  This file contains an internal function that can be used
+//  to add subviews in a way that lets them fill the parent.
+
 import UIKit
 
 extension UIView {

+ 3 - 0
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Extensions/UIViewController+RootViewController.swift

@@ -6,6 +6,9 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
+//  This file contains internal util functions for resolving
+//  the root view controller for the current app.
+
 import UIKit
 
 extension UIViewController {

+ 2 - 2
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Info.plist

@@ -15,9 +15,9 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.1.0</string>
+	<string>1.2.2</string>
 	<key>CFBundleVersion</key>
-	<string>1901101458</string>
+	<string>1</string>
 	<key>NSPrincipalClass</key>
 	<string></string>
 </dict>

+ 116 - 37
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/ActionSheetItem.swift

@@ -9,8 +9,47 @@
 /*
  
  This class represents a regular action sheet item, like the
- ones used in UIAlertController. It has a title, an optional
- value and an optional image. All other items builds on this.
+ one used in UIAlertController. It has a title as well as an
+ optional subtitle, value and image. All other items inherit
+ this class, even if they don't make use of these properties.
+ 
+ 
+ ## Subclassing
+ 
+ You can subclass any item class and customize it in any way
+ you need. If you need your subclass to use a different cell,
+ just override `cell(for:)` to return the cell you need.
+ 
+ 
+ ## Appearance
+ 
+ Customizing the appearance of the various action sheet item
+ types in Sheeeeeeeeet (as well as of your own custom items),
+ is mainly done using the iOS appearance proxy for each item
+ cell type. For instance, to change the title text color for
+ all `ActionSheetSelectItem` instances (including subclasses),
+ type `ActionSheetSelectItem.appearance().titleColor`. It is
+ also possible to set these properties for each item as well.
+ 
+ While most appearance is modified on a cell level, some are
+ not. For instance, some views in `Views` have apperances of
+ their own (e.g. `ActionSheetHeaderView.cornerRadius`). This
+ means that you can change more than cell appearance. Have a
+ look at the readme for more info on what you can customize.
+ 
+ Action sheet insets, margins and widths are not part of the
+ appearance model, but have to be changed for each sheet. If
+ you want to change these values for each sheet in youer app,
+ I recommend subclassing `ActionSheet` and set these values.
+ 
+ Neither item heights are part of the appearance model. Item
+ heights are instead changed by setting the static height of
+ each item type, e.g. `ActionSheetTitleItem.height = 20`. It
+ is not part of the cell appearance model since an item must
+ know about the height before it creates any cells.
+ 
+ 
+ ## Tap behavior
  
  The default tap behavior of action sheet items is "dismiss",
  which means that the action sheet will dismiss itself after
@@ -18,16 +57,23 @@
  don't want the action sheet to be dismissed when an item is
  tapped. Some item types uses `.none` by default.
  
- The item appearance is set by the sheet. It either uses the
- global appearance or an individual instance. To use a fully
- custom appearances for a single action sheet item, just set
- the `customAppearance` property.
- 
  */
 
 import UIKit
 
 open class ActionSheetItem: NSObject {
+
+    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "appearance will be removed in 1.4.0. Use the new appearance model instead.")
+    public lazy internal(set) var appearance = ActionSheetItemAppearance(copy: ActionSheetAppearance.standard.item)
+    @available(*, deprecated, message: "customAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    public var customAppearance: ActionSheetItemAppearance?
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open func applyAppearance(_ appearance: ActionSheetAppearance) { self.appearance = customAppearance ?? ActionSheetItemAppearance(copy: appearance.item) }
+    @available(*, deprecated, message: "applyAppearance(to:) will be removed in 1.4.0. Use the new appearance model instead.")
+    open func applyAppearance(to cell: UITableViewCell) { applyLegacyAppearance(to: cell) }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
     
     
     // MARK: - Initialization
@@ -38,15 +84,13 @@ open class ActionSheetItem: NSObject {
         value: Any? = nil,
         image: UIImage? = nil,
         tapBehavior: TapBehavior = .dismiss) {
-        let appearance = ActionSheetAppearance.standard.item
         self.title = title
         self.subtitle = subtitle
         self.value = value
         self.image = image
         self.tapBehavior = tapBehavior
-        self.appearance = ActionSheetItemAppearance(copy: appearance)
+        self.cellStyle = subtitle == nil ? .default : .value1
         super.init()
-        if subtitle != nil { self.cellStyle = .value1}
     }
     
     
@@ -59,7 +103,6 @@ open class ActionSheetItem: NSObject {
 
     // MARK: - Properties
     
-    public internal(set) var appearance: ActionSheetItemAppearance
     public var image: UIImage?
     public var subtitle: String?
     public var tapBehavior: TapBehavior
@@ -67,40 +110,76 @@ open class ActionSheetItem: NSObject {
     public var value: Any?
     
     public var cellReuseIdentifier: String { return className }
-    public var cellStyle: UITableViewCell.CellStyle = .default
-    public var customAppearance: ActionSheetItemAppearance?
+    public var cellStyle: UITableViewCell.CellStyle
     
     
-    // MARK: - Functions
+    // MARK: - Height Logic
+    
+    private static var heights = [String: CGFloat]()
     
-    open func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = customAppearance ?? ActionSheetItemAppearance(copy: appearance.item)
+    public static var height: CGFloat {
+        get { return heights[className] ?? 50 }
+        set { heights[className] = newValue }
     }
     
-    open func applyAppearance(to cell: UITableViewCell) {
-        if let color = appearance.backgroundColor {
-            cell.backgroundColor = color
-        }
-        cell.imageView?.image = image
-        cell.selectionStyle = .default
-        cell.separatorInset = appearance.separatorInsets
-        cell.tintColor = appearance.tintColor
-        cell.textLabel?.text = title
-        cell.textLabel?.textAlignment = .left
-        cell.textLabel?.textColor = appearance.textColor
-        cell.textLabel?.font = appearance.font
-        cell.detailTextLabel?.text = subtitle
-        cell.detailTextLabel?.font = appearance.subtitleFont
-        cell.detailTextLabel?.textColor = appearance.subtitleTextColor
+    public var height: CGFloat {
+        return type(of: self).height
     }
     
-    open func cell(for tableView: UITableView) -> UITableViewCell {
-        let id = cellReuseIdentifier
-        let cell = tableView.dequeueReusableCell(withIdentifier: id) as? ActionSheetItemCell
-            ?? ActionSheetItemCell(style: cellStyle, reuseIdentifier: id)
-        applyAppearance(to: cell)
-        return cell
+    
+    // MARK: - Functions
+    
+    open func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
     
     open func handleTap(in actionSheet: ActionSheet) {}
 }
+
+
+// MARK: -
+
+open class ActionSheetItemCell: UITableViewCell {
+    
+    
+    // MARK: - Layout
+    
+    open override func didMoveToWindow() {
+        super.didMoveToWindow()
+        refresh()
+    }
+    
+    
+    // MARK: - Appearance Properties
+    
+    @objc public dynamic var titleColor: UIColor?
+    @objc public dynamic var titleFont: UIFont?
+    @objc public dynamic var subtitleColor: UIColor?
+    @objc public dynamic var subtitleFont: UIFont?
+    
+    
+    // MARK: - Private Properties
+    
+    public private(set) weak var item: ActionSheetItem?
+    
+    
+    // MARK: - Functions
+    
+    open func refresh() {
+        guard let item = item else { return }
+        imageView?.image = item.image
+        selectionStyle = item.tapBehavior == .none ? .none : .default
+        textLabel?.font = titleFont
+        textLabel?.text = item.title
+        textLabel?.textAlignment = .left
+        textLabel?.textColor = titleColor
+        detailTextLabel?.font = subtitleFont
+        detailTextLabel?.text = item.subtitle
+        detailTextLabel?.textColor = subtitleColor
+    }
+    
+    func refresh(with item: ActionSheetItem) {
+        self.item = item
+        refresh()
+    }
+}

+ 21 - 7
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetButton.swift

@@ -10,7 +10,7 @@
  
  This class is a base class for all action sheet buttons. It
  is not intended to be used directly. Instead, use the built
- in buttons or subclass it to create your own button type.
+ in buttons or subclass it to create your own buttons.
  
  */
 
@@ -19,6 +19,14 @@ import UIKit
 open class ActionSheetButton: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = customAppearance ?? ActionSheetButtonAppearance(copy: appearance.okButton)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String, value: Any?) {
@@ -39,18 +47,24 @@ open class ActionSheetButton: ActionSheetItem {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = customAppearance ?? ActionSheetButtonAppearance(copy: appearance.okButton)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetButtonCell(style: .default, reuseIdentifier: cellReuseIdentifier)
     }
+}
+
+
+// MARK: -
+
+open class ActionSheetButtonCell: ActionSheetItemCell {
     
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        cell.textLabel?.textAlignment = .center
+    open override func refresh() {
+        super.refresh()
+        textLabel?.textAlignment = .center
     }
 }
 
 
-// MARK: - ActionSheetItem Extensions
+// MARK: - Button Extensions
 
 public extension ActionSheetItem {
     

+ 16 - 3
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetCancelButton.swift

@@ -11,7 +11,7 @@
  Cancel buttons have no special behavior, but can be used in
  sheets where a user applies changes by tapping an OK button.
  
- The value of a cancel button is `ButtonType.cancel`.
+ The value of a cancel button is `.cancel`.
  
  */
 
@@ -20,6 +20,14 @@ import UIKit
 open class ActionSheetCancelButton: ActionSheetButton {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = customAppearance ?? ActionSheetCancelButtonAppearance(copy: appearance.cancelButton)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String) {
@@ -29,7 +37,12 @@ open class ActionSheetCancelButton: ActionSheetButton {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = customAppearance ?? ActionSheetCancelButtonAppearance(copy: appearance.cancelButton)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetCancelButtonCell(style: .default, reuseIdentifier: cellReuseIdentifier)
     }
 }
+
+
+// MARK: - 
+
+open class ActionSheetCancelButtonCell: ActionSheetButtonCell {}

+ 18 - 4
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetDangerButton.swift

@@ -10,9 +10,10 @@
  
  Danger buttons have no special behavior, but can be used to
  indicate that the effect of the action sheet is destructive.
- They are basically just OK buttons with a "red alert" style.
+ They should be styled as "dangerous" (e.g. red text), using
+ the appearance proxy.
  
- The value of a danger button is `ButtonType.ok`.
+ The value of a danger button is `.ok`.
  
  */
 
@@ -21,9 +22,22 @@ import UIKit
 open class ActionSheetDangerButton: ActionSheetOkButton {
     
     
-    // MARK: - Functions
-    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
     open override func applyAppearance(_ appearance: ActionSheetAppearance) {
         self.appearance = customAppearance ?? ActionSheetDangerButtonAppearance(copy: appearance.dangerButton)
     }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
+    // MARK: - Functions
+    
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetDangerButtonCell(style: .default, reuseIdentifier: cellReuseIdentifier)
+    }
 }
+
+
+// MARK: -
+
+open class ActionSheetDangerButtonCell: ActionSheetButtonCell {}

+ 17 - 4
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Buttons/ActionSheetOkButton.swift

@@ -9,9 +9,9 @@
 /*
  
  OK buttons have no special behavior, but can be used when a
- user should apply action sheet changes by tapping an button.
+ user should apply action sheet changes by tapping a button.
  
- The value of an OK button is `ButtonType.ok`.
+ The value of an OK button is `.ok`.
  
  */
 
@@ -20,6 +20,14 @@ import UIKit
 open class ActionSheetOkButton: ActionSheetButton {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = customAppearance ?? ActionSheetOkButtonAppearance(copy: appearance.okButton)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String) {
@@ -29,7 +37,12 @@ open class ActionSheetOkButton: ActionSheetButton {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = customAppearance ?? ActionSheetOkButtonAppearance(copy: appearance.okButton)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetOkButtonCell(style: .default, reuseIdentifier: cellReuseIdentifier)
     }
 }
+
+
+// MARK: - 
+
+open class ActionSheetOkButtonCell: ActionSheetButtonCell {}

+ 60 - 15
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetCollectionItem.swift

@@ -17,11 +17,29 @@
  action sheet items. If you look at `cell(for: ...)`, you'll
  see that it uses `ActionSheetCollectionItemCell` for its id.
  
+ TODO: Unit test
+ 
  */
 
 import Foundation
 
-open class ActionSheetCollectionItem<T>: ActionSheetItem, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout where T: ActionSheetCollectionItemContentCell {
+open class ActionSheetCollectionItem<T: ActionSheetCollectionItemContentCell>: ActionSheetItem, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
+    
+    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetCollectionItemAppearance(copy: appearance.collectionItem)
+        self.appearance.height = T.defaultSize.height + T.topInset + T.bottomInset + 0.5
+    }
+    @available(*, deprecated, message: "applyAppearance(to:) will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(to cell: UITableViewCell) {
+        super.applyAppearance(to: cell)
+        guard let itemCell = cell as? ActionSheetCollectionItemCell else { return }
+        itemCell.setup(withNib: T.nib, owner: self)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
     
     
     // MARK: - Initialization
@@ -46,6 +64,7 @@ open class ActionSheetCollectionItem<T>: ActionSheetItem, UICollectionViewDataSo
     
     // MARK: - Properties
     
+    public override var height: CGFloat { return T.defaultSize.height }
     public let itemCellType: T.Type
     public let itemCount: Int
     public private(set) var selectionAction: CellAction
@@ -54,21 +73,11 @@ open class ActionSheetCollectionItem<T>: ActionSheetItem, UICollectionViewDataSo
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetCollectionItemAppearance(copy: appearance.collectionItem)
-        self.appearance.height = T.defaultSize.height + T.topInset + T.bottomInset + 0.5
-    }
-    
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        guard let itemCell = cell as? ActionSheetCollectionItemCell else { return }
-        itemCell.setup(withNib: T.nib, owner: self)
-    }
-    
-    open override func cell(for tableView: UITableView) -> UITableViewCell {
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
         tableView.register(ActionSheetCollectionItemCell.nib, forCellReuseIdentifier: cellReuseIdentifier)
-        return super.cell(for: tableView)
+        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier)
+        guard let typedCell = cell as? ActionSheetCollectionItemCell else { fatalError("Invalid cell type created by superclass") }
+        return typedCell
     }
     
     open func extendSelectionAction(toReload actionSheet: ActionSheet) {
@@ -127,3 +136,39 @@ open class ActionSheetCollectionItem<T>: ActionSheetItem, UICollectionViewDataSo
         return 0
     }
 }
+
+
+// MARK: -
+
+open class ActionSheetCollectionItemCell: ActionSheetItemCell {
+    
+    
+    // MARK: - Properties
+    
+    static let itemCellIdentifier = ActionSheetCollectionItemCell.className
+    
+    static let nib = ActionSheetCollectionItemCell.defaultNib
+    
+    
+    // MARK: - Outlets
+    
+    @IBOutlet weak var collectionView: UICollectionView! {
+        didSet {
+            let flow = UICollectionViewFlowLayout()
+            flow.scrollDirection = .horizontal
+            collectionView.collectionViewLayout = flow
+        }
+    }
+    
+    
+    // MARK: - Functions
+    
+    func setup(withNib nib: UINib, owner: UICollectionViewDataSource & UICollectionViewDelegate) {
+        let id = ActionSheetCollectionItemCell.itemCellIdentifier
+        collectionView.contentInset = .zero
+        collectionView.register(nib, forCellWithReuseIdentifier: id)
+        collectionView.dataSource = owner
+        collectionView.delegate = owner
+        collectionView.reloadData()
+    }
+}

+ 28 - 14
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetCustomItem.swift

@@ -8,16 +8,27 @@
 
 /*
  
- Custom items can be used to present any view in your sheets.
- Just specify the view type you want to use and make sure it
- inherits `ActionSheetItemCell`, and that it also implements
- `ActionSheetCustomItemCell`.
+ Custom items can be used to present any views in your sheet.
+ It can use any view that inherits `ActionSheetItemCell` and
+ implements `ActionSheetCustomItemCell`.
+ 
+ TODO: Unit test
  
  */
 
 import UIKit
 
-public class ActionSheetCustomItem<T>: ActionSheetItem where T: ActionSheetCustomItemCell {
+public class ActionSheetCustomItem<T: ActionSheetCustomItemCell>: ActionSheetItem {
+    
+    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    public override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetCustomItemAppearance(copy: appearance.customItem)
+        self.appearance.height = T.defaultSize.height
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
     
     
     // MARK: - Initialization
@@ -41,24 +52,27 @@ public class ActionSheetCustomItem<T>: ActionSheetItem where T: ActionSheetCusto
     
     // MARK: - Properties
     
+    public override var height: CGFloat { return T.defaultSize.height }
     public let cellType: T.Type
     public let setupAction: SetupAction
     
     
     // MARK: - Functions
     
-    public override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetCustomItemAppearance(copy: appearance.customItem)
-        self.appearance.height = T.defaultSize.height
-    }
-    
-    open override func cell(for tableView: UITableView) -> UITableViewCell {
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
         tableView.register(T.nib, forCellReuseIdentifier: cellReuseIdentifier)
-        let cell = super.cell(for: tableView)
-        cell.selectionStyle = .none
+        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier)
         guard let typedCell = cell as? T else { fatalError("Invalid cell type created by superclass") }
         setupAction(typedCell)
         return typedCell
     }
 }
+
+
+// MARK: -
+
+public protocol ActionSheetCustomItemCell where Self: ActionSheetItemCell {
+    
+    static var nib: UINib { get }
+    static var defaultSize: CGSize { get }
+}

+ 25 - 9
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetLinkItem.swift

@@ -18,23 +18,39 @@ import UIKit
 open class ActionSheetLinkItem: ActionSheetItem {
     
     
-    // MARK: - Properties
-    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "linkAppearance will be removed in 1.4.0. Use the new appearance model instead.")
     open var linkAppearance: ActionSheetLinkItemAppearance? {
         return appearance as? ActionSheetLinkItemAppearance
     }
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetLinkItemAppearance(copy: appearance.linkItem)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
     
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetLinkItemAppearance(copy: appearance.linkItem)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetLinkItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
+}
+
+
+open class ActionSheetLinkItemCell: ActionSheetItemCell {
+    
+    
+    // MARK: - Appearance Properties
+    
+    @objc public dynamic var linkIcon: UIImage?
+    
+    
+    // MARK: - Functions
     
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        guard let appearance = linkAppearance else { return }
-        cell.accessoryView = UIImageView(image: appearance.linkIcon)
+    open override func refresh() {
+        super.refresh()
+        accessoryView = UIImageView(image: linkIcon)
     }
 }

+ 14 - 3
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectItem.swift

@@ -28,6 +28,15 @@ import UIKit
 open class ActionSheetMultiSelectItem: ActionSheetSelectItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetMultiSelectItemAppearance(copy: appearance.multiSelectItem)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(
@@ -50,9 +59,8 @@ open class ActionSheetMultiSelectItem: ActionSheetSelectItem {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetMultiSelectItemAppearance(copy: appearance.multiSelectItem)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetMultiSelectItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
     
     open override func handleTap(in actionSheet: ActionSheet) {
@@ -62,3 +70,6 @@ open class ActionSheetMultiSelectItem: ActionSheetSelectItem {
         items.forEach { $0.updateState(for: actionSheet) }
     }
 }
+
+
+open class ActionSheetMultiSelectItemCell: ActionSheetSelectItemCell {}

+ 42 - 15
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectToggleItem.swift

@@ -24,6 +24,15 @@ import UIKit
 open class ActionSheetMultiSelectToggleItem: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetMultiSelectToggleItemAppearance(copy: appearance.multiSelectToggleItem)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String, state: State, group: String, selectAllTitle: String, deselectAllTitle: String) {
@@ -31,9 +40,7 @@ open class ActionSheetMultiSelectToggleItem: ActionSheetItem {
         self.state = state
         self.deselectAllTitle = deselectAllTitle
         self.selectAllTitle = selectAllTitle
-        super.init(
-            title: title,
-            tapBehavior: .none)
+        super.init(title: title, tapBehavior: .none)
         cellStyle = .value1
     }
     
@@ -55,18 +62,8 @@ open class ActionSheetMultiSelectToggleItem: ActionSheetItem {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetMultiSelectToggleItemAppearance(copy: appearance.multiSelectToggleItem)
-    }
-    
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        guard let appearance = appearance as? ActionSheetMultiSelectToggleItemAppearance else { return }
-        let isSelectAll = state == .selectAll
-        subtitle = isSelectAll ? selectAllTitle : deselectAllTitle
-        appearance.subtitleTextColor = isSelectAll ? appearance.selectAllTextColor : appearance.deselectAllTextColor
-        super.applyAppearance(to: cell)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetMultiSelectToggleItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
     
     open override func handleTap(in actionSheet: ActionSheet) {
@@ -81,6 +78,36 @@ open class ActionSheetMultiSelectToggleItem: ActionSheetItem {
     open func updateState(for actionSheet: ActionSheet) {
         let selectItems = actionSheet.items.compactMap { $0 as? ActionSheetMultiSelectItem }
         let items = selectItems.filter { $0.group == group }
+        guard items.count > 0 else { return state = .selectAll }
         state = items.contains { !$0.isSelected } ? .selectAll : .deselectAll
     }
 }
+
+
+// MARK: - 
+
+open class ActionSheetMultiSelectToggleItemCell: ActionSheetItemCell {
+    
+    
+    // MARK: - Appearance Properties
+    
+    @objc public dynamic var deselectAllImage: UIColor?
+    @objc public dynamic var deselectAllSubtitleColor: UIColor?
+    @objc public dynamic var deselectAllTitleColor: UIColor?
+    @objc public dynamic var selectAllImage: UIColor?
+    @objc public dynamic var selectAllSubtitleColor: UIColor?
+    @objc public dynamic var selectAllTitleColor: UIColor?
+    
+    
+    // MARK: - Public Functions
+    
+    open override func refresh() {
+        super.refresh()
+        guard let item = item as? ActionSheetMultiSelectToggleItem else { return }
+        let isSelectAll = item.state == .selectAll
+        item.subtitle = isSelectAll ? item.selectAllTitle : item.deselectAllTitle
+        titleColor = isSelectAll ? selectAllTitleColor : deselectAllTitleColor
+        subtitleColor = isSelectAll ? selectAllSubtitleColor : deselectAllSubtitleColor
+        super.refresh()
+    }
+}

+ 84 - 17
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetSelectItem.swift

@@ -29,6 +29,15 @@ import UIKit
 open class ActionSheetSelectItem: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        super.applyAppearance(appearance)
+        self.appearance = ActionSheetSelectItemAppearance(copy: appearance.selectItem)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(
@@ -53,33 +62,91 @@ open class ActionSheetSelectItem: ActionSheetItem {
     // MARK: - Properties
     
     open var group: String
-    
     open var isSelected: Bool
     
-    open var selectAppearance: ActionSheetSelectItemAppearance? {
-        return appearance as? ActionSheetSelectItemAppearance
+    
+    // MARK: - Functions
+    
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetSelectItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
     
+    open override func handleTap(in actionSheet: ActionSheet) {
+        super.handleTap(in: actionSheet)
+        isSelected = !isSelected
+    }
+}
+
+
+// MARK: -
+
+open class ActionSheetSelectItemCell: ActionSheetItemCell {
+    
+    
+    // MARK: - Appearance Properties
+    
+    @objc public dynamic var selectedIcon: UIImage?
+    @objc public dynamic var selectedIconColor: UIColor?
+    @objc public dynamic var selectedSubtitleColor: UIColor?
+    @objc public dynamic var selectedSubtitleFont: UIFont?
+    @objc public dynamic var selectedTitleColor: UIColor?
+    @objc public dynamic var selectedTitleFont: UIFont?
+    @objc public dynamic var selectedTintColor: UIColor?
+    @objc public dynamic var unselectedIcon: UIImage?
+    @objc public dynamic var unselectedIconColor: UIColor?
+    
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        self.appearance = ActionSheetSelectItemAppearance(copy: appearance.selectItem)
+    open override func refresh() {
+        super.refresh()
+        guard let item = item as? ActionSheetSelectItem else { return }
+        applyAccessoryView(for: item)
+        applyAccessoryViewColor(for: item)
+        applySubtitleColor(for: item)
+        applySubtitleFont(for: item)
+        applyTintColor(for: item)
+        applyTitleColor(for: item)
+        applyTitleFont(for: item)
     }
+}
+
+
+private extension ActionSheetSelectItemCell {
     
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        guard let appearance = selectAppearance else { return }
-        let accessoryImage = isSelected ? appearance.selectedIcon : appearance.unselectedIcon
-        cell.accessoryView = UIImageView(image: accessoryImage)
-        cell.accessoryView?.tintColor = isSelected ? appearance.selectedIconTintColor : appearance.tintColor
-        cell.tintColor = isSelected ? appearance.selectedTintColor : appearance.tintColor
-        cell.textLabel?.textColor = isSelected ? appearance.selectedTextColor : appearance.textColor
+    func applyAccessoryView(for item: ActionSheetSelectItem) {
+        guard let image = item.isSelected ? selectedIcon : unselectedIcon else { return }
+        accessoryView = UIImageView(image: image)
     }
     
-    open override func handleTap(in actionSheet: ActionSheet) {
-        super.handleTap(in: actionSheet)
-        isSelected = !isSelected
+    func applyAccessoryViewColor(for item: ActionSheetSelectItem) {
+        guard let color = item.isSelected ? selectedIconColor : unselectedIconColor else { return }
+        accessoryView?.tintColor = color
+    }
+    
+    func applySubtitleColor(for item: ActionSheetSelectItem) {
+        guard let color = item.isSelected ? selectedSubtitleColor : subtitleColor else { return }
+        detailTextLabel?.textColor = color
+    }
+    
+    func applySubtitleFont(for item: ActionSheetSelectItem) {
+        guard let font = item.isSelected ? selectedSubtitleFont : subtitleFont else { return }
+        detailTextLabel?.font = font
+    }
+    
+    func applyTintColor(for item: ActionSheetSelectItem) {
+        let defaultTint = type(of: self).appearance().tintColor
+        guard let color = item.isSelected ? selectedTintColor : defaultTint else { return }
+        tintColor = color
+    }
+    
+    func applyTitleColor(for item: ActionSheetSelectItem) {
+        guard let color = item.isSelected ? selectedTitleColor : titleColor else { return }
+        textLabel?.textColor = color
+    }
+    
+    func applyTitleFont(for item: ActionSheetSelectItem) {
+        guard let font = item.isSelected ? selectedTitleFont : titleFont else { return }
+        textLabel?.font = font
     }
 }

+ 15 - 2
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetSingleSelectItem.swift

@@ -23,12 +23,20 @@ import UIKit
 open class ActionSheetSingleSelectItem: ActionSheetSelectItem {
     
     
-     // MARK: - Functions
-    
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
     open override func applyAppearance(_ appearance: ActionSheetAppearance) {
         super.applyAppearance(appearance)
         self.appearance = ActionSheetSingleSelectItemAppearance(copy: appearance.singleSelectItem)
     }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
+    // MARK: - Functions
+    
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetSingleSelectItemCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
+    }
     
     open override func handleTap(in actionSheet: ActionSheet) {
         super.handleTap(in: actionSheet)
@@ -38,3 +46,8 @@ open class ActionSheetSingleSelectItem: ActionSheetSelectItem {
         isSelected = true
     }
 }
+
+
+// MARK: -
+
+open class ActionSheetSingleSelectItemCell: ActionSheetSelectItemCell {}

+ 15 - 7
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetSectionMargin.swift

@@ -19,6 +19,14 @@ import UIKit
 open class ActionSheetSectionMargin: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = ActionSheetSectionMarginAppearance(copy: appearance.sectionMargin)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init() {
@@ -28,12 +36,12 @@ open class ActionSheetSectionMargin: ActionSheetItem {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = ActionSheetSectionMarginAppearance(copy: appearance.sectionMargin)
-    }
-    
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        cell.selectionStyle = .none
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetSectionMarginCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
 }
+
+
+// MARK: -
+
+open class ActionSheetSectionMarginCell: ActionSheetItemCell {}

+ 15 - 8
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetSectionTitle.swift

@@ -23,22 +23,29 @@ import UIKit
 open class ActionSheetSectionTitle: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = ActionSheetSectionTitleAppearance(copy: appearance.sectionTitle)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String, subtitle: String? = nil) {
         super.init(title: title, subtitle: subtitle, tapBehavior: .none)
-        cellStyle = .value1
     }
     
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = ActionSheetSectionTitleAppearance(copy: appearance.sectionTitle)
-    }
-    
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        cell.selectionStyle = .none
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetSectionTitleCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
 }
+
+
+// MARK: -
+
+open class ActionSheetSectionTitleCell: ActionSheetItemCell {}

+ 19 - 6
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Titles/ActionSheetTitle.swift

@@ -19,6 +19,14 @@ import UIKit
 open class ActionSheetTitle: ActionSheetItem {
     
     
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    @available(*, deprecated, message: "applyAppearance will be removed in 1.4.0. Use the new appearance model instead.")
+    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
+        self.appearance = ActionSheetTitleAppearance(copy: appearance.title)
+    }
+    // MARK: - Deprecated - Remove in 1.4.0 ****************
+    
+    
     // MARK: - Initialization
     
     public init(title: String) {
@@ -28,13 +36,18 @@ open class ActionSheetTitle: ActionSheetItem {
     
     // MARK: - Functions
     
-    open override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        self.appearance = ActionSheetTitleAppearance(copy: appearance.title)
+    open override func cell(for tableView: UITableView) -> ActionSheetItemCell {
+        return ActionSheetTitleCell(style: cellStyle, reuseIdentifier: cellReuseIdentifier)
     }
+}
+
+
+// MARK: -
+
+open class ActionSheetTitleCell: ActionSheetItemCell {
     
-    open override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        cell.selectionStyle = .none
-        cell.textLabel?.textAlignment = .center
+    open override func refresh() {
+        super.refresh()
+        textLabel?.textAlignment = .center
     }
 }

+ 6 - 16
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPopoverPresenter.swift

@@ -8,8 +8,8 @@
 
 /*
  
- This presenter will present action sheets as popovers, just
- as a regular UIAlertController is displayed on the iPad.
+ This presenter presents action sheets in a popover, just as
+ regular UIAlertControllers are displayed on an iPad.
  
  Since popovers have an arrow that should use the same color
  as the rest of the popover view, this presenter will remove
@@ -28,8 +28,8 @@ open class ActionSheetPopoverPresenter: NSObject, ActionSheetPresenter {
     open var events = ActionSheetPresenterEvents()
     open var isDismissableWithTapOnBackground = true
     
-    private var actionSheet: ActionSheet?
-    private weak var popover: UIPopoverPresentationController?
+    var actionSheet: ActionSheet?
+    weak var popover: UIPopoverPresentationController?
     
     
     // MARK: - ActionSheetPresenter
@@ -59,16 +59,7 @@ open class ActionSheetPopoverPresenter: NSObject, ActionSheetPresenter {
         guard let sheet = actionSheet else { return }
         sheet.headerViewContainer?.isHidden = true
         sheet.buttonsTableView?.isHidden = true
-        refreshPopoverAppearance(for: sheet)
-    }
-    
-    
-    // MARK: - Protected Functions
-    
-    open func refreshPopoverAppearance(for sheet: ActionSheet) {
-        let width = sheet.appearance.popover.width
-        let height = sheet.itemsHeight
-        sheet.preferredContentSize = CGSize(width: width, height: height)
+        sheet.preferredContentSize.height = sheet.itemsHeight
         popover?.backgroundColor = sheet.itemsTableView?.backgroundColor
     }
 }
@@ -92,7 +83,6 @@ extension ActionSheetPopoverPresenter: UIPopoverPresentationControllerDelegate {
 extension ActionSheetPopoverPresenter {
     
     func popover(for sheet: ActionSheet, in vc: UIViewController) -> UIPopoverPresentationController? {
-        sheet.modalPresentationStyle = .popover
         let popover = sheet.popoverPresentationController
         popover?.delegate = self
         return popover
@@ -100,9 +90,9 @@ extension ActionSheetPopoverPresenter {
     
     func setupSheetForPresentation(_ sheet: ActionSheet) {
         self.actionSheet = sheet
-        sheet.headerView = nil
         sheet.items = popoverItems(for: sheet)
         sheet.buttons = []
+        sheet.modalPresentationStyle = .popover
     }
 }
 

+ 6 - 9
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPresenter.swift

@@ -8,16 +8,13 @@
 
 /*
  
- Action sheet presenters are used by Sheeeeeeeeet to present
- action sheets in different ways, e.g. with a default bottom
- slide, showing a popover from the tapped view etc.
+ Action sheet presenters are used to present and dismiss any
+ action sheet in different ways, for instance with a default
+ slide-in, showing the sheet in a popover etc.
  
- When implementing this protocol, `present(in:from:)` is the
- standard way to present an action sheet, while `dismiss` is
- the standard way to dismiss it.
- 
- `isDismissableWithTapOnBackground` is used to specify if an
- action sheet can be dismissed by tapping on the background.
+ Instead of a delegate, the presenter protocol uses an event
+ property that has events that you can subscribe to, by just
+ setting the action blocks in the event struct.
  
  */
 

+ 14 - 7
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetStandardPresenter.swift

@@ -8,8 +8,9 @@
 
 /*
  
- This presenter will present action sheets as regular iPhone
- action sheets, from the bottom of the screen.
+ This presenter presents action sheets as regular iOS action
+ sheets, which are presented with a slide-in from the bottom
+ of the screen.
  
  */
 
@@ -28,7 +29,9 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
     public var events = ActionSheetPresenterEvents()
     public var isDismissableWithTapOnBackground = true
     
-    private var actionSheet: ActionSheet?
+    var actionSheet: ActionSheet?
+    var animationDelay: TimeInterval = 0
+    var animationDuration: TimeInterval = 0.3
     
     
     // MARK: - ActionSheetPresenter
@@ -53,6 +56,7 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
     open func present(sheet: ActionSheet, in vc: UIViewController, completion: @escaping () -> ()) {
         actionSheet = sheet
         addActionSheetView(from: sheet, to: vc.view)
+        addBackgroundViewTapAction(to: sheet.backgroundView)
         presentBackgroundView()
         presentActionSheet(completion: completion)
     }
@@ -71,7 +75,6 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
     open func addActionSheetView(from sheet: ActionSheet, to view: UIView) {
         sheet.view.frame = view.frame
         view.addSubview(sheet.view)
-        addBackgroundViewTapAction(to: sheet.backgroundView)
     }
 
     open func addBackgroundViewTapAction(to view: UIView?) {
@@ -86,7 +89,12 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
     }
     
     open func animate(_ animation: @escaping () -> (), completion: (() -> ())?) {
-        UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut], animations: animation) { _ in completion?() }
+        guard animationDuration >= 0 else { return }
+        UIView.animate(
+            withDuration: animationDuration,
+            delay: animationDelay,
+            options: [.curveEaseOut],
+            animations: animation) { _ in completion?() }
     }
     
     open func presentActionSheet(completion: @escaping () -> ()) {
@@ -106,8 +114,7 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
 
     open func removeActionSheet(completion: @escaping () -> ()) {
         guard let view = actionSheet?.stackView else { return }
-        let frame = view.frame
-        let animation = { view.frame.origin.y += frame.height + 100 }
+        let animation = { view.frame.origin.y += view.frame.height + 100 }
         animate(animation) { completion() }
     }
 

+ 0 - 34
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemCell.swift

@@ -1,34 +0,0 @@
-import Foundation
-
-open class ActionSheetCollectionItemCell: ActionSheetItemCell {
-    
-    
-    // MARK: - Properties
-    
-    static let itemCellIdentifier = ActionSheetCollectionItemCell.className
-    
-    static let nib = ActionSheetCollectionItemCell.defaultNib
-    
-    
-    // MARK: - Outlets
-    
-    @IBOutlet weak var collectionView: UICollectionView! {
-        didSet {
-            let flow = UICollectionViewFlowLayout()
-            flow.scrollDirection = .horizontal
-            collectionView.collectionViewLayout = flow
-        }
-    }
-    
-    
-    // MARK: - Functions
-    
-    func setup(withNib nib: UINib, owner: UICollectionViewDataSource & UICollectionViewDelegate) {
-        let id = ActionSheetCollectionItemCell.itemCellIdentifier
-        collectionView.contentInset = .zero
-        collectionView.register(nib, forCellWithReuseIdentifier: id)
-        collectionView.dataSource = owner
-        collectionView.delegate = owner
-        collectionView.reloadData()
-    }
-}

+ 0 - 46
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemCell.xib

@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
-    <dependencies>
-        <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <objects>
-        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
-        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="196" id="moX-qQ-Gyn" customClass="ActionSheetCollectionItemCell" customModule="Sheeeeeeeeet" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="452" height="196"/>
-            <autoresizingMask key="autoresizingMask"/>
-            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="moX-qQ-Gyn" id="qre-fe-O26">
-                <rect key="frame" x="0.0" y="0.0" width="452" height="195.5"/>
-                <autoresizingMask key="autoresizingMask"/>
-                <subviews>
-                    <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="eSV-yW-MpO">
-                        <rect key="frame" x="0.0" y="0.0" width="452" height="195.5"/>
-                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                        <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="UCY-qV-avs">
-                            <size key="itemSize" width="50" height="50"/>
-                            <size key="headerReferenceSize" width="0.0" height="0.0"/>
-                            <size key="footerReferenceSize" width="0.0" height="0.0"/>
-                            <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
-                        </collectionViewFlowLayout>
-                    </collectionView>
-                </subviews>
-                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                <constraints>
-                    <constraint firstAttribute="bottom" secondItem="eSV-yW-MpO" secondAttribute="bottom" id="Dnr-ur-bHp"/>
-                    <constraint firstAttribute="trailing" secondItem="eSV-yW-MpO" secondAttribute="trailing" id="Hbt-ZB-Gy8"/>
-                    <constraint firstItem="eSV-yW-MpO" firstAttribute="top" secondItem="qre-fe-O26" secondAttribute="top" id="OuY-7J-7R1"/>
-                    <constraint firstItem="eSV-yW-MpO" firstAttribute="leading" secondItem="qre-fe-O26" secondAttribute="leading" id="h5Z-mf-v8W"/>
-                </constraints>
-            </tableViewCellContentView>
-            <connections>
-                <outlet property="collectionView" destination="eSV-yW-MpO" id="s1m-Z2-HwO"/>
-            </connections>
-            <point key="canvasLocation" x="-28" y="-15"/>
-        </tableViewCell>
-    </objects>
-</document>

+ 0 - 27
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCollectionItemContentCell.swift

@@ -1,27 +0,0 @@
-//
-//  ActionSheetCollectionItemContentCell.swift
-//  Sheeeeeeeeet
-//
-//  Created by Jonas Ullström (ullstrm) on 2018-03-01.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-/*
- 
- This protocol must be implemented by any cell that is to be
- used together with an `ActionSheetCollectionItem`.
- 
- */
-
-import UIKit
-
-public protocol ActionSheetCollectionItemContentCell where Self: UICollectionViewCell {
-    
-    static var nib: UINib { get }
-    static var defaultSize: CGSize { get }
-    static var leftInset: CGFloat { get }
-    static var rightInset: CGFloat { get }
-    static var topInset: CGFloat { get }
-    static var bottomInset: CGFloat { get }
-    static var itemSpacing: CGFloat { get }
-}

+ 0 - 22
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetCustomItemCell.swift

@@ -1,22 +0,0 @@
-//
-//  ActionSheetCustomItemCell.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2018-10-08.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-/*
- 
- This protocol must be implemented by any cell that is to be
- used together with an `ActionSheetCustomItem`.
- 
- */
-
-import UIKit
-
-public protocol ActionSheetCustomItemCell where Self: ActionSheetItemCell {
-    
-    static var nib: UINib { get }
-    static var defaultSize: CGSize { get }
-}

+ 0 - 19
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Views/ActionSheetItemCell.swift

@@ -1,19 +0,0 @@
-//
-//  ActionSheetItemCell.swift
-//  Sheeeeeeeeet
-//
-//  Created by Daniel Saidi on 2017-11-24.
-//  Copyright © 2017 Daniel Saidi. All rights reserved.
-//
-
-/*
- 
- This is the base class for all different cell types in this
- library. It makes it possible to set a global appearance on
- all item cells, using `ActionSheetItemCell.appearance()`.
- 
- */
-
-import UIKit
-
-open class ActionSheetItemCell: UITableViewCell {}

+ 89 - 63
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/AppDelegate+Appearance.swift

@@ -19,68 +19,94 @@ import Sheeeeeeeeet
 extension AppDelegate {
     
     func applyAppearance() {
-        
-        let blue = UIColor(hex: 0x0FA2F5)
-        let green = UIColor(hex: 0x81c03f)
-        let pink = UIColor(hex: 0xec5f72)
-        let purple = UIColor(hex: 0xd9007b)
-        let red = UIColor(hex: 0xff3333)
-        
-        let robotoBlack = "Roboto-Black"
-        let robotoMedium = "Roboto-Medium"
-        let robotoRegular = "Roboto-Regular"
-        
-        let appearance = ActionSheetAppearance.standard
-        
-//        appearance.popover.width = 500
-        
-        appearance.item.font = UIFont(name: robotoRegular, size: 17)
-        appearance.item.textColor = .darkText
-        appearance.item.tintColor = .darkGray
-        appearance.item.subtitleFont = UIFont(name: robotoRegular, size: 14)
-        appearance.item.subtitleTextColor = blue
-        
-//        appearance.separatorColor = .red
-//        appearance.itemsSeparatorColor = .blue
-//        appearance.buttonsSeparatorColor = .green
-        
-        appearance.title.hideSeparator()
-        appearance.title.font = UIFont(name: robotoMedium, size: 15)
-        
-        appearance.sectionTitle.hideSeparator()
-        appearance.sectionTitle.font = UIFont(name: robotoMedium, size: 13)
-        appearance.sectionTitle.height = 20
-        
-        appearance.sectionMargin.height = 20
-        
-        appearance.selectItem.selectedIcon = UIImage(named: "ic_checkmark")
-        appearance.selectItem.unselectedIcon = UIImage(named: "ic_empty")
-        appearance.selectItem.selectedTintColor = blue
-        appearance.selectItem.selectedTextColor = green
-        appearance.selectItem.selectedIconTintColor = purple
-        
-        appearance.singleSelectItem.selectedTintColor = green
-        appearance.singleSelectItem.selectedTextColor = purple
-        appearance.singleSelectItem.selectedIconTintColor = blue
-        
-        appearance.multiSelectItem.selectedTintColor = purple
-        appearance.multiSelectItem.selectedTextColor = blue
-        appearance.multiSelectItem.selectedIconTintColor = green
-        
-        appearance.multiSelectToggleItem.hideSeparator()
-        appearance.multiSelectToggleItem.font = UIFont(name: robotoMedium, size: 13)
-        appearance.multiSelectToggleItem.selectAllTextColor = .lightGray
-        appearance.multiSelectToggleItem.deselectAllTextColor = red
-        
-        appearance.linkItem.linkIcon = UIImage(named: "ic_arrow_right")
-        
-        appearance.okButton.textColor = .darkGray
-        appearance.okButton.font = UIFont(name: robotoBlack, size: 17)
-        
-        appearance.dangerButton.textColor = pink
-        appearance.dangerButton.font = UIFont(name: robotoMedium, size: 17)
-        
-        appearance.cancelButton.textColor = .lightGray
-        appearance.cancelButton.font = UIFont(name: robotoMedium, size: 17)
+        applyViewAppearances()
+        applyColors()
+        applyFonts()
+        applyHeights()
+        applyIcons()
+        applySelectItemAppearances()
+        applySeparatorInsets()
+        applyPopoverWidth()
+    }
+}
+
+
+private extension AppDelegate {
+    
+    func applyViewAppearances() {
+//        ActionSheetBackgroundView.appearance().backgroundColor = .purple
+        ActionSheetHeaderView.appearance().cornerRadius = 10
+        ActionSheetTableView.appearance().cornerRadius = 10
+//        ActionSheetTableView.appearance().separatorLineColor = .purple
+//        ActionSheetItemTableView.appearance().cornerRadius = 20
+//        ActionSheetTableView.appearance(whenContainedInInstancesOf: [MultiSelectActionSheet.self]).cornerRadius = 20
+    }
+    
+    func applyColors() {
+        ActionSheetItemCell.appearance().titleColor = .darkText
+        ActionSheetItemCell.appearance().subtitleColor = .exampleBlue
+        ActionSheetItemCell.appearance().tintColor = .darkText
+//        ActionSheetItemCell.appearance().separatorColor = .red
+//        ActionSheetItemCell.appearance().backgroundColor = red
+//        ActionSheetItemCell.appearance(whenContainedInInstancesOf: [ActionSheetItemTableView.self]).backgroundColor = .purple
+        ActionSheetOkButtonCell.appearance().titleColor = .darkGray
+        ActionSheetCancelButtonCell.appearance().titleColor = .lightGray
+        ActionSheetDangerButtonCell.appearance().titleColor = .examplePink
+    }
+    
+    func applyFonts() {
+        ActionSheetItemCell.appearance().titleFont = .robotoRegular(size: 17)
+        ActionSheetItemCell.appearance().subtitleFont = .robotoRegular(size: 14)
+        ActionSheetLinkItemCell.appearance().titleFont = .robotoRegular(size: 17)
+        ActionSheetMultiSelectToggleItemCell.appearance().titleFont = .robotoMedium(size: 13)
+        ActionSheetSectionTitleCell.appearance().titleFont = .robotoMedium(size: 13)
+        ActionSheetTitleCell.appearance().titleFont = .robotoMedium(size: 15)
+        ActionSheetOkButtonCell.appearance().titleFont = .robotoBlack(size: 17)
+        ActionSheetDangerButtonCell.appearance().titleFont = .robotoMedium(size: 17)
+        ActionSheetCancelButtonCell.appearance().titleFont = .robotoRegular(size: 17)
+    }
+    
+    func applyHeights() {
+        ActionSheetSectionTitle.height = 20
+        ActionSheetSectionMargin.height = 20
+    }
+    
+    func applyIcons() {
+        ActionSheetLinkItemCell.appearance().linkIcon = UIImage(named: "ic_arrow_right")
+    }
+    
+    func applySelectItemAppearances() {
+        ActionSheetSelectItemCell.appearance().selectedIcon = UIImage(named: "ic_checkmark")
+        ActionSheetSelectItemCell.appearance().unselectedIcon = UIImage(named: "ic_empty")
+        ActionSheetSelectItemCell.appearance().selectedTintColor = .exampleBlue
+        ActionSheetSelectItemCell.appearance().selectedTitleColor = .exampleGreen
+        ActionSheetSelectItemCell.appearance().selectedIconColor = .examplePurple
+        
+        ActionSheetSingleSelectItemCell.appearance().selectedTintColor = .exampleGreen
+        ActionSheetSingleSelectItemCell.appearance().selectedTitleFont = .robotoMedium(size: 35)
+        ActionSheetSingleSelectItemCell.appearance().selectedSubtitleFont = .robotoMedium(size: 25)
+        ActionSheetSingleSelectItemCell.appearance().selectedTitleColor = .examplePurple
+        ActionSheetSingleSelectItemCell.appearance().selectedIconColor = .exampleBlue
+        
+        ActionSheetMultiSelectItemCell.appearance().tintColor = UIColor.darkText.withAlphaComponent(0.4)
+        ActionSheetMultiSelectItemCell.appearance().titleColor = UIColor.darkText.withAlphaComponent(0.4)
+        ActionSheetMultiSelectItemCell.appearance().selectedTintColor = .examplePurple
+        ActionSheetMultiSelectItemCell.appearance().selectedTitleColor = .exampleBlue
+        ActionSheetMultiSelectItemCell.appearance().selectedIconColor = .exampleGreen
+        
+        ActionSheetMultiSelectToggleItemCell.appearance().selectAllSubtitleColor = .lightGray
+        ActionSheetMultiSelectToggleItemCell.appearance().deselectAllSubtitleColor = .exampleRed
+    }
+    
+    func applySeparatorInsets() {
+        ActionSheetItemCell.appearance().separatorInset = .zero
+        ActionSheetTitleCell.appearance().separatorInset = .hiddenSeparator
+        ActionSheetSectionTitleCell.appearance().separatorInset = .hiddenSeparator
+        ActionSheetSectionMarginCell.appearance().separatorInset = .hiddenSeparator
+        ActionSheetMultiSelectToggleItemCell.appearance().separatorInset = .hiddenSeparator
+    }
+    
+    func applyPopoverWidth() {
+//        ActionSheet.preferredPopoverWidth = 700
     }
 }

+ 2 - 2
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Info.plist

@@ -15,9 +15,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.1.0</string>
+	<string>1.2.2</string>
 	<key>CFBundleVersion</key>
-	<string>1901101458</string>
+	<string>1</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
 	<key>UIAppFonts</key>

+ 2 - 2
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheet+PresenterTests.swift

@@ -8,7 +8,7 @@ class ActionSheet_PresenterTests: QuickSpec {
         
         describe("default presenter") {
             
-            func getDifferentIdiom() -> UIUserInterfaceIdiom {
+            func getReversedIdiom() -> UIUserInterfaceIdiom {
                 switch UIDevice.current.userInterfaceIdiom {
                 case .phone: return .pad
                 case .pad: return .phone
@@ -26,7 +26,7 @@ class ActionSheet_PresenterTests: QuickSpec {
             }
             
             it("is different from other idioms") {
-                let idiom = getDifferentIdiom()
+                let idiom = getReversedIdiom()
                 let idiomPresenter = idiom.defaultPresenter
                 let defaultPresenter = ActionSheet.defaultPresenter
                 let isSameKind = type(of: defaultPresenter) == type(of: idiomPresenter)

+ 21 - 17
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetItemHandlerTests.swift

@@ -14,8 +14,8 @@ class ActionSheetItemHandlerTests: QuickSpec {
     
     override func spec() {
         
-        func tableView() -> MockTableView {
-            return MockTableView(frame: .zero)
+        func createTableView() -> MockItemTableView {
+            return MockItemTableView(frame: .zero)
         }
         
         var sheet: MockActionSheet!
@@ -32,6 +32,7 @@ class ActionSheetItemHandlerTests: QuickSpec {
             handler = ActionSheetItemHandler(actionSheet: sheet, itemType: .items)
         }
         
+        
         describe("configured with item type") {
             
             beforeEach {
@@ -46,6 +47,7 @@ class ActionSheetItemHandlerTests: QuickSpec {
             }
         }
         
+        
         describe("configured with button type") {
             
             beforeEach {
@@ -59,7 +61,8 @@ class ActionSheetItemHandlerTests: QuickSpec {
             }
         }
         
-        describe("as table view data source") {
+        
+        describe("when used as table view data source") {
             
             it("returns correct item at index") {
                 let path1 = IndexPath(row: 0, section: 0)
@@ -69,54 +72,55 @@ class ActionSheetItemHandlerTests: QuickSpec {
             }
             
             it("has correct section count") {
-                let sections = handler.numberOfSections(in: tableView())
+                let sections = handler.numberOfSections(in: createTableView())
                 expect(sections).to(equal(1))
             }
             
             it("has correct row count") {
-                let rows = handler.tableView(tableView(), numberOfRowsInSection: 0)
+                let rows = handler.tableView(createTableView(), numberOfRowsInSection: 0)
                 expect(rows).to(equal(2))
             }
             
             it("returns correct cell for existing item") {
                 let path = IndexPath(row: 0, section: 0)
-                item1.cell = UITableViewCell(frame: .zero)
-                let result = handler.tableView(tableView(), cellForRowAt: path)
+                item1.cell = ActionSheetItemCell(frame: .zero)
+                let result = handler.tableView(createTableView(), cellForRowAt: path)
                 expect(result).to(be(item1.cell))
             }
             
             it("returns fallback cell for existing item") {
                 let path = IndexPath(row: 1, section: 1)
-                let result = handler.tableView(tableView(), cellForRowAt: path)
+                let result = handler.tableView(createTableView(), cellForRowAt: path)
                 expect(result).toNot(beNil())
             }
             
             it("returns correct height for existing item") {
                 let path = IndexPath(row: 0, section: 0)
-                item1.appearance.height = 123
-                let result = handler.tableView(tableView(), heightForRowAt: path)
+                MockActionSheetItem.height = 123
+                let result = handler.tableView(createTableView(), heightForRowAt: path)
                 expect(result).to(equal(123))
             }
             
             it("returns zero height for existing item") {
                 let path = IndexPath(row: 1, section: 1)
-                let result = handler.tableView(tableView(), heightForRowAt: path)
+                let result = handler.tableView(createTableView(), heightForRowAt: path)
                 expect(result).to(equal(0))
             }
         }
         
-        describe("as table view delegate") {
+        
+        describe("when used as table view delegate") {
             
             it("does not deselect row for invalid path") {
                 let path = IndexPath(row: 1, section: 1)
-                let view = tableView()
+                let view = createTableView()
                 handler.tableView(view, didSelectRowAt: path)
                 expect(view.deselectRowInvokeCount).to(equal(0))
             }
             
             it("deselects row for valid path") {
                 let path = IndexPath(row: 0, section: 0)
-                let view = tableView()
+                let view = createTableView()
                 handler.tableView(view, didSelectRowAt: path)
                 expect(view.deselectRowInvokeCount).to(equal(1))
                 expect(view.deselectRowInvokePaths.count).to(equal(1))
@@ -128,13 +132,13 @@ class ActionSheetItemHandlerTests: QuickSpec {
             it("does not handle tap if missing action sheet") {
                 sheet = nil
                 let path = IndexPath(row: 0, section: 0)
-                handler.tableView(tableView(), didSelectRowAt: path)
+                handler.tableView(createTableView(), didSelectRowAt: path)
                 expect(item1.handleTapInvokeCount).to(equal(0))
             }
             
             it("handles item tap for existing action sheet") {
                 let path = IndexPath(row: 0, section: 0)
-                handler.tableView(tableView(), didSelectRowAt: path)
+                handler.tableView(createTableView(), didSelectRowAt: path)
                 expect(item1.handleTapInvokeCount).to(equal(1))
                 expect(item1.handleTapInvokeActionSheets.count).to(equal(1))
                 expect(item1.handleTapInvokeActionSheets[0]).to(be(sheet))
@@ -142,7 +146,7 @@ class ActionSheetItemHandlerTests: QuickSpec {
 
             it("handles sheet item tap for existing action sheet") {
                 let path = IndexPath(row: 0, section: 0)
-                handler.tableView(tableView(), didSelectRowAt: path)
+                handler.tableView(createTableView(), didSelectRowAt: path)
                 expect(sheet.handleTapInvokeCount).to(equal(1))
                 expect(sheet.handleTapInvokeItems.count).to(equal(1))
                 expect(sheet.handleTapInvokeItems[0]).to(be(item1))

+ 14 - 7
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetMarginTests.swift

@@ -31,16 +31,23 @@ class ActionSheetMarginTests: QuickSpec {
         
         describe("value with minimum fallback in view") {
             
-            func value(for margin: ActionSheetMargin) -> CGFloat? {
+            func value(for margin: ActionSheetMargin, minimum: CGFloat) -> CGFloat {
                 let view = UIView(frame: .zero)
-                return margin.value(in: view, minimum: 10)
+                return margin.value(in: view, minimum: minimum)
             }
             
-            it("returns safe area inset value") {
-                expect(value(for: .top)).to(equal(10))
-                expect(value(for: .left)).to(equal(10))
-                expect(value(for: .right)).to(equal(10))
-                expect(value(for: .bottom)).to(equal(10))
+            it("returns safe area inset if it is greater than minimum value") {
+                expect(value(for: .top, minimum: -1)).to(equal(0))
+                expect(value(for: .left, minimum: -1)).to(equal(0))
+                expect(value(for: .right, minimum: -1)).to(equal(0))
+                expect(value(for: .bottom, minimum: -1)).to(equal(0))
+            }
+            
+            it("returns minimum value if it is greater than safe area insets") {
+                expect(value(for: .top, minimum: 10)).to(equal(10))
+                expect(value(for: .left, minimum: 10)).to(equal(10))
+                expect(value(for: .right, minimum: 10)).to(equal(10))
+                expect(value(for: .bottom, minimum: 10)).to(equal(10))
             }
         }
     }

+ 358 - 300
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetTests.swift

@@ -6,8 +6,6 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
-//  TODO: Improve these tests, since much logic has changed.
-
 import Quick
 import Nimble
 @testable import Sheeeeeeeeet
@@ -16,121 +14,237 @@ class ActionSheetTests: QuickSpec {
     
     override func spec() {
         
-        func createButton(_ title: String) -> ActionSheetButton {
-            return ActionSheetOkButton(title: title)
-        }
-        
-        func createItem(_ title: String) -> ActionSheetItem {
-            return ActionSheetItem(title: title)
-        }
+        var sheet: MockActionSheet!
         
         func createSheet(_ items: [ActionSheetItem] = []) -> MockActionSheet {
             return MockActionSheet(items: items, action: { _, _ in })
         }
         
-        func createTableView() -> ActionSheetTableView {
-            return ActionSheetTableView(frame: .zero)
+        
+        // MARK: - Initialization
+        
+        describe("created instance") {
+            
+            context("default behavior") {
+                
+                it("use default presenter") {
+                    sheet = createSheet()
+                    let isStandard = sheet.presenter is ActionSheetStandardPresenter
+                    let isPopover = sheet.presenter is ActionSheetPopoverPresenter
+                    let isValid = isStandard || isPopover
+                    
+                    expect(isValid).to(beTrue())
+                }
+                
+                it("applies no items and buttons") {
+                    sheet = createSheet()
+                    
+                    expect(sheet.items.count).to(equal(0))
+                    expect(sheet.buttons.count).to(equal(0))
+                }
+                
+            }
+            
+            context("custom properties") {
+                
+                it("uses provided presenter") {
+                    let presenter = ActionSheetPopoverPresenter()
+                    sheet = MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
+                    
+                    expect(sheet.presenter).to(be(presenter))
+                }
+                
+                it("sets up provided items and buttons") {
+                    let items = [ActionSheetItem(title: "foo")]
+                    sheet = createSheet(items)
+                    
+                    expect(sheet.setupItemsInvokeCount).to(equal(1))
+                    expect(sheet.setupItemsInvokeItems[0]).to(be(items))
+                }
+            }
+            
+            it("uses provided action") {
+                var counter = 0
+                sheet = MockActionSheet(items: []) { _, _  in counter += 1 }
+                sheet.selectAction(sheet, ActionSheetItem(title: "foo"))
+                
+                expect(counter).to(equal(1))
+            }
         }
         
         
-        // MARK: - Initialization
+        describe("setup") {
+            
+            beforeEach {
+                sheet = createSheet()
+            }
+            
+            it("applies default preferred popover width") {
+                sheet.setup()
+                
+                expect(sheet.preferredContentSize.width).to(equal(300))
+            }
+            
+            it("applies custom preferred popover width") {
+                sheet.preferredPopoverWidth = 200
+                sheet.setup()
+                
+                expect(sheet.preferredContentSize.width).to(equal(200))
+            }
+        }
+        
         
-        describe("when initialized with parameters") {
+        describe("setup items") {
+            
+            beforeEach {
+                sheet = createSheet()
+            }
             
-            it("applies provided items") {
-                let item1 = createItem("foo")
-                let item2 = createItem("bar")
-                let sheet = createSheet([item1, item2])
+            it("applies empty collection") {
+                sheet.setup(items: [])
                 
-                expect(sheet.items.count).to(equal(2))
-                expect(sheet.items[0]).to(be(item1))
-                expect(sheet.items[1]).to(be(item2))
+                expect(sheet.items.count).to(equal(0))
+                expect(sheet.buttons.count).to(equal(0))
             }
-
-            it("separates provided items and buttons") {
-                let button = createButton("Sheeeeeeeeet")
-                let item1 = createItem("foo")
-                let item2 = createItem("bar")
-                let sheet = createSheet([button, item1, item2])
-
+            
+            it("separates items and buttons") {
+                let item1 = ActionSheetItem(title: "foo")
+                let item2 = ActionSheetItem(title: "bar")
+                let button = ActionSheetOkButton(title: "baz")
+                sheet.setup(items: [button, item1, item2])
+                
                 expect(sheet.items.count).to(equal(2))
                 expect(sheet.items[0]).to(be(item1))
                 expect(sheet.items[1]).to(be(item2))
                 expect(sheet.buttons.count).to(equal(1))
                 expect(sheet.buttons[0]).to(be(button))
             }
-
-            it("applies default presenter if none is provided") {
-                let sheet = createSheet()
-                let isStandard = sheet.presenter is ActionSheetStandardPresenter
-                let isPopover = sheet.presenter is ActionSheetPopoverPresenter
-                let isValid = isStandard || isPopover
+            
+            it("reloads data") {
+                sheet.reloadDataInvokeCount = 0
+                sheet.setup(items: [])
                 
-                expect(isValid).to(beTrue())
+                expect(sheet.reloadDataInvokeCount).to(equal(1))
             }
-
-            it("applies provided presenter") {
-                let presenter = ActionSheetPopoverPresenter()
-                let sheet = MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
+        }
+        
+        
+        describe("loading view") {
+            
+            var itemsTableView: ActionSheetItemTableView!
+            var buttonsTableView: ActionSheetButtonTableView!
+            
+            beforeEach {
+                sheet = createSheet()
+                itemsTableView = ActionSheetItemTableView(frame: .zero)
+                buttonsTableView = ActionSheetButtonTableView(frame: .zero)
+                sheet.itemsTableView = itemsTableView
+                sheet.buttonsTableView = buttonsTableView
+                sheet.viewDidLoad()
+            }
+            
+            it("sets up action sheet") {
+                expect(sheet.setupInvokeCount).to(equal(1))
+            }
+            
+            it("sets up items table view") {
+                expect(itemsTableView.delegate).to(be(sheet.itemHandler))
+                expect(itemsTableView.dataSource).to(be(sheet.itemHandler))
+                expect(itemsTableView.alwaysBounceVertical).to(beFalse())
+                expect(itemsTableView.estimatedRowHeight).to(equal(44))
+                expect(itemsTableView.rowHeight).to(equal(UITableView.automaticDimension))
+                expect(itemsTableView.cellLayoutMarginsFollowReadableWidth).to(beFalse())
+            }
+            
+            it("sets up buttons table view") {
+                expect(buttonsTableView.delegate).to(be(sheet.buttonHandler))
+                expect(buttonsTableView.dataSource).to(be(sheet.buttonHandler))
+                expect(itemsTableView.alwaysBounceVertical).to(beFalse())
+                expect(buttonsTableView.estimatedRowHeight).to(equal(44))
+                expect(buttonsTableView.rowHeight).to(equal(UITableView.automaticDimension))
+                expect(buttonsTableView.cellLayoutMarginsFollowReadableWidth).to(beFalse())
+            }
+        }
+        
+        
+        describe("laying out subviews") {
+            
+            it("refreshes sheet") {
+                sheet = createSheet()
+                sheet.viewDidLayoutSubviews()
                 
-                expect(sheet.presenter).to(be(presenter))
+                expect(sheet.refreshInvokeCount).to(equal(1))
             }
-
-            it("applies provided action") {
-                var counter = 0
-                let sheet = MockActionSheet(items: []) { _, _  in counter += 1 }
-                sheet.selectAction(sheet, createItem("foo"))
+        }
+        
+        
+        describe("minimum content insets") {
+            
+            it("has correct default value") {
+                sheet = createSheet()
+                let expected = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
                 
-                expect(counter).to(equal(1))
+                expect(sheet.minimumContentInsets).to(equal(expected))
             }
         }
         
         
-        // MARK: - Properties
-
-        describe("appearance") {
+        describe("preferred popover width") {
             
-            it("is initially a copy of standard appearance") {
-                let original = ActionSheetAppearance.standard.popover.width
-                ActionSheetAppearance.standard.popover.width = -1
-                let sheet = createSheet()
-                let appearance = sheet.appearance
-                ActionSheetAppearance.standard.popover.width = original
+            it("has correct default value") {
+                sheet = createSheet()
+                let expected: CGFloat = 300
                 
-                expect(appearance.popover.width).to(equal(-1))
+                expect(sheet.preferredPopoverWidth).to(equal(expected))
             }
         }
         
         
-        // MARK: - Item Properties
+        describe("section margins") {
+            
+            it("has correct default value") {
+                sheet = createSheet()
+                let expected: CGFloat = 15
+                
+                expect(sheet.sectionMargins).to(equal(expected))
+            }
+        }
+        
         
         describe("items height") {
             
-            it("is sum of all item appearances") {
-                let item1 = createItem("foo")
-                let item2 = createItem("bar")
-                let item3 = createButton("baz")
-                item1.appearance.height = 100
-                item2.appearance.height = 110
-                item3.appearance.height = 120
-                let sheet = createSheet([item1, item2, item3])
-                
-                expect(sheet.itemsHeight).to(equal(210))
+            beforeEach {
+                ActionSheetItem.height = 100
+                ActionSheetSingleSelectItem.height = 110
+                ActionSheetMultiSelectItem.height = 120
+                ActionSheetOkButton.height = 120
+            }
+            
+            it("is sum of all items") {
+                let item1 = ActionSheetItem(title: "foo")
+                let item2 = ActionSheetSingleSelectItem(title: "bar", isSelected: true)
+                let item3 = ActionSheetMultiSelectItem(title: "baz", isSelected: false)
+                let button = ActionSheetOkButton(title: "ok")
+                sheet = createSheet([item1, item2, item3, button])
+                
+                expect(sheet.itemsHeight).to(equal(330))
             }
         }
         
+        
         describe("item handler") {
             
             it("has correct item type") {
-                let sheet = createSheet()
+                sheet = createSheet()
+                
                 expect(sheet.itemHandler.itemType).to(equal(.items))
             }
             
             it("has correct items") {
-                let item1 = createItem("foo")
-                let item2 = createItem("bar")
-                let item3 = createButton("baz")
-                let sheet = createSheet([item1, item2, item3])
+                let item1 = ActionSheetItem(title: "foo")
+                let item2 = ActionSheetItem(title: "bar")
+                let button = ActionSheetOkButton(title: "ok")
+                sheet = createSheet([item1, item2, button])
                 
                 expect(sheet.itemHandler.items.count).to(equal(2))
                 expect(sheet.itemHandler.items[0]).to(be(item1))
@@ -138,93 +252,64 @@ class ActionSheetTests: QuickSpec {
             }
         }
         
-        describe("item table view") {
+        
+        describe("items height") {
             
-            it("is correctly setup when view is loaded") {
-                let sheet = createSheet()
-                let view = createTableView()
-                sheet.itemsTableView = view
-                sheet.viewDidLoad()
-                
-                expect(view.delegate).to(be(sheet.itemHandler))
-                expect(view.dataSource).to(be(sheet.itemHandler))
-                expect(view.estimatedRowHeight).to(equal(44))
-                expect(view.rowHeight).to(equal(UITableView.automaticDimension))
-                expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
+            beforeEach {
+                ActionSheetItem.height = 100
+                ActionSheetOkButton.height = 110
+                ActionSheetDangerButton.height = 120
+                ActionSheetCancelButton.height = 130
             }
-        }
-        
-        
-        // MARK: - Button Properties
-        
-        describe("buttons height") {
             
-            it("is sum of all button appearances") {
-                let item1 = createItem("foo")
-                let item2 = createButton("bar")
-                let item3 = createButton("baz")
-                item1.appearance.height = 100
-                item2.appearance.height = 110
-                item3.appearance.height = 120
-                let sheet = createSheet([item1, item2, item3])
-                
-                expect(sheet.buttonsHeight).to(equal(230))
+            it("is sum of all items") {
+                let item = ActionSheetItem(title: "foo")
+                let button1 = ActionSheetOkButton(title: "ok")
+                let button2 = ActionSheetDangerButton(title: "ok")
+                let button3 = ActionSheetCancelButton(title: "ok")
+                sheet = createSheet([item, button1, button2, button3])
+                
+                expect(sheet.buttonsHeight).to(equal(360))
             }
         }
         
-        describe("button handler") {
+        
+        describe("item handler") {
             
             it("has correct item type") {
-                let sheet = createSheet()
+                sheet = createSheet()
+                
                 expect(sheet.buttonHandler.itemType).to(equal(.buttons))
             }
             
             it("has correct items") {
-                let item1 = createItem("foo")
-                let item2 = createButton("bar")
-                let item3 = createButton("baz")
-                let sheet = createSheet([item1, item2, item3])
+                let item = ActionSheetItem(title: "foo")
+                let button1 = ActionSheetOkButton(title: "ok")
+                let button2 = ActionSheetOkButton(title: "ok")
+                sheet = createSheet([item, button1, button2])
                 
                 expect(sheet.buttonHandler.items.count).to(equal(2))
-                expect(sheet.buttonHandler.items[0]).to(be(item2))
-                expect(sheet.buttonHandler.items[1]).to(be(item3))
+                expect(sheet.buttonHandler.items[0]).to(be(button1))
+                expect(sheet.buttonHandler.items[1]).to(be(button2))
             }
         }
         
-        describe("button table view") {
-            
-            it("is correctly setup when view is loaded") {
-                let sheet = createSheet()
-                let view = createTableView()
-                sheet.buttonsTableView = view
-                sheet.viewDidLoad()
-                
-                expect(view.delegate).to(be(sheet.buttonHandler))
-                expect(view.dataSource).to(be(sheet.buttonHandler))
-                expect(view.estimatedRowHeight).to(equal(44))
-                expect(view.rowHeight).to(equal(UITableView.automaticDimension))
-                expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
-            }
-        }
-        
-        
-        // MARK: - Presentation Functions
         
         context("presentation") {
             
             var presenter: MockActionSheetPresenter!
             
-            func createSheet() -> MockActionSheet {
+            beforeEach {
                 presenter = MockActionSheetPresenter()
-                return MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
+                sheet = createSheet()
+                sheet.presenter = presenter
             }
             
             describe("when dismissed") {
                 
-                it("dismisses itself by calling presenter") {
+                it("it calls presenter") {
                     var counter = 0
                     let completion = { counter += 1 }
-                    let sheet = createSheet()
                     sheet.dismiss(completion: completion)
                     presenter.dismissInvokeCompletions[0]()
                     
@@ -236,16 +321,14 @@ class ActionSheetTests: QuickSpec {
             describe("when presented from view") {
                 
                 it("refreshes itself") {
-                    let sheet = createSheet()
                     sheet.present(in: UIViewController(), from: UIView())
                     
                     expect(sheet.refreshInvokeCount).to(equal(1))
                 }
                 
-                it("presents itself by calling presenter") {
+                it("calls presenter") {
                     var counter = 0
                     let completion = { counter += 1 }
-                    let sheet = createSheet()
                     let vc = UIViewController()
                     let view = UIView()
                     sheet.present(in: vc, from: view, completion: completion)
@@ -261,16 +344,14 @@ class ActionSheetTests: QuickSpec {
             describe("when presented from bar button item") {
                 
                 it("refreshes itself") {
-                    let sheet = createSheet()
                     sheet.present(in: UIViewController(), from: UIBarButtonItem())
                     
                     expect(sheet.refreshInvokeCount).to(equal(1))
                 }
                 
-                it("presents itself by calling presenter") {
+                it("calls presenter") {
                     var counter = 0
                     let completion = { counter += 1 }
-                    let sheet = createSheet()
                     let vc = UIViewController()
                     let item = UIBarButtonItem()
                     sheet.present(in: vc, from: item, completion: completion)
@@ -285,191 +366,153 @@ class ActionSheetTests: QuickSpec {
         }
         
         
-        // MARK: - Refresh Functions
-        
-        describe("when refreshing") {
+        describe("refreshing") {
             
-            var sheet: MockActionSheet!
-            var headerViewContainer: UIView!
-            var itemsView: ActionSheetTableView!
-            var buttonsView: ActionSheetTableView!
+            var presenter: MockActionSheetPresenter!
             var stackView: UIStackView!
             
             beforeEach {
+                presenter = MockActionSheetPresenter()
+                stackView = UIStackView()
                 sheet = createSheet()
-                sheet.appearance.groupMargins = 123
-                sheet.appearance.cornerRadius = 90
-                headerViewContainer = UIView(frame: .zero)
-                itemsView = createTableView()
-                buttonsView = createTableView()
-                stackView = UIStackView(frame: .zero)
-                sheet.headerViewContainer = headerViewContainer
-                sheet.itemsTableView = itemsView
-                sheet.buttonsTableView = buttonsView
                 sheet.stackView = stackView
+                sheet.presenter = presenter
+                sheet.refresh()
             }
             
-            context("sheet") {
-                
-                it("applies round corners") {
-                    sheet.refresh()
-                    
-                    expect(headerViewContainer.layer.cornerRadius).to(equal(90))
-                    expect(itemsView.layer.cornerRadius).to(equal(90))
-                    expect(buttonsView.layer.cornerRadius).to(equal(90))
-                }
-                
-                it("applies stack view spacing") {
-                    sheet.refresh()
-                    
-                    expect(sheet.stackView?.spacing).to(equal(123))
-                }
-                
-                it("asks presenter to refresh sheet") {
-                    let presenter = MockActionSheetPresenter()
-                    let sheet = MockActionSheet(items: [], presenter: presenter) { (_, _) in }
-                    sheet.refresh()
-                    
-                    expect(presenter.refreshActionSheetInvokeCount).to(equal(1))
-                }
+            it("refreshes header") {
+                expect(sheet.refreshHeaderInvokeCount).to(equal(1))
             }
             
-            context("header") {
-                
-                it("refreshes header visibility") {
-                    sheet.refresh()
-                    expect(sheet.refreshHeaderInvokeCount).to(equal(1))
-                }
-                
-                it("adds header view to header container") {
-                    let header = UIView(frame: .zero)
-                    sheet.headerView = header
-                    expect(header.constraints.count).to(equal(0))
-                    sheet.refresh()
-                    expect(headerViewContainer.subviews.count).to(equal(1))
-                    expect(headerViewContainer.subviews[0]).to(be(header))
-                    expect(header.translatesAutoresizingMaskIntoConstraints).to(beFalse())
-                }
+            it("refreshes items") {
+                expect(sheet.refreshItemsInvokeCount).to(equal(1))
             }
             
-            context("header visibility") {
-                
-                it("hides header container if header view is nil") {
-                    sheet.refreshHeader()
-                    expect(headerViewContainer.isHidden).to(beTrue())
-                }
-                
-                it("shows header container if header view is nil") {
-                    sheet.headerView = UIView(frame: .zero)
-                    sheet.refreshHeader()
-                    expect(headerViewContainer.isHidden).to(beFalse())
-                }
+            it("refreshes buttons") {
+                expect(sheet.refreshButtonsInvokeCount).to(equal(1))
             }
             
-            context("items") {
-                
-                it("applies appearances to all items") {
-                    let item1 = MockActionSheetItem(title: "foo")
-                    let item2 = MockActionSheetItem(title: "foo")
-                    sheet.setup(items: [item1, item2])
-                    sheet.refresh()
-                    
-                    expect(item1.applyAppearanceInvokeCount).to(equal(1))
-                    expect(item2.applyAppearanceInvokeCount).to(equal(1))
-                    expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
-                    expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
-                }
-                
-                it("applies background color") {
-                    sheet.appearance.itemsBackgroundColor = .yellow
-                    let view = createTableView()
-                    sheet.itemsTableView = view
-                    sheet.refresh()
-                    
-                    expect(view.backgroundColor).to(equal(.yellow))
-                }
-                
-                it("applies separator color") {
-                    sheet.appearance.itemsSeparatorColor = .yellow
-                    let view = createTableView()
-                    sheet.itemsTableView = view
-                    sheet.refresh()
-
-                    expect(view.separatorColor).to(equal(.yellow))
-                }
+            it("applies stack view spacing") {
+                expect(stackView.spacing).to(equal(15))
             }
             
-            context("buttons") {
-                
-                it("refreshes buttons visibility") {
-                    sheet.refresh()
-                    expect(sheet.refreshButtonsInvokeCount).to(equal(1))
-                }
-                
-                it("applies appearances to all buttons") {
-                    let item1 = MockActionSheetButton(title: "foo", value: true)
-                    let item2 = MockActionSheetButton(title: "foo", value: true)
-                    sheet.setup(items: [item1, item2])
-                    sheet.refresh()
-                    
-                    expect(item1.applyAppearanceInvokeCount).to(equal(1))
-                    expect(item2.applyAppearanceInvokeCount).to(equal(1))
-                    expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
-                    expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
-                }
-                
-                it("applies background color") {
-                    sheet.appearance.buttonsBackgroundColor = .yellow
-                    let view = createTableView()
-                    sheet.buttonsTableView = view
-                    sheet.refresh()
-                    
-                    expect(view.backgroundColor).to(equal(.yellow))
-                }
-                
-                it("applies separator color") {
-                    sheet.appearance.buttonsSeparatorColor = .yellow
-                    let view = createTableView()
-                    sheet.buttonsTableView = view
-                    sheet.refresh()
-                    
-                    expect(view.separatorColor).to(equal(.yellow))
-                }
+            it("calls presenter to refresh itself") {
+                expect(presenter.refreshActionSheetInvokeCount).to(equal(1))
+            }
+        }
+        
+        
+        describe("refreshing header") {
+            
+            var container: ActionSheetHeaderView!
+            var height: NSLayoutConstraint!
+            
+            beforeEach {
+                container = ActionSheetHeaderView()
+                height = NSLayoutConstraint()
+                sheet = createSheet()
+                sheet.headerViewContainer = container
+                sheet.headerViewContainerHeight = height
             }
             
-            context("button visibility") {
+            it("refreshes correctly if header view is nil") {
+                sheet.refreshHeader()
                 
-                it("hides buttons if sheet has no buttons") {
-                    sheet.refreshButtons()
-                    expect(buttonsView.isHidden).to(beTrue())
-                }
+                expect(container.isHidden).to(beTrue())
+                expect(container.subviews.count).to(equal(0))
+                expect(height.constant).to(equal(0))
+            }
+            
+            it("refreshes correctly if header view is set") {
+                let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
+                sheet.headerView = view
+                sheet.refreshHeader()
+                
+                expect(container.isHidden).to(beFalse())
+                expect(container.subviews.count).to(equal(1))
+                expect(container.subviews[0]).to(be(view))
+                expect(height.constant).to(equal(200))
+            }
+        }
+        
+        
+        describe("refreshing items") {
+            
+            var height: NSLayoutConstraint!
+            
+            beforeEach {
+                height = NSLayoutConstraint()
+                sheet = createSheet()
+                sheet.itemsTableViewHeight = height
+                ActionSheetItem.height = 12
+                ActionSheetOkButton.height = 13
+            }
+            
+            it("refreshes correctly if no items are set") {
+                sheet.refreshItems()
                 
-                it("shows buttons if sheet has buttons") {
-                    sheet.setup(items: [MockActionSheetButton(title: "foo", value: true)])
-                    sheet.refreshButtons()
-                    expect(buttonsView.isHidden).to(beFalse())
-                }
+                expect(height.constant).to(equal(0))
+            }
+            
+            it("refreshes correctly if items are set") {
+                let item1 = ActionSheetItem(title: "foo")
+                let item2 = ActionSheetItem(title: "foo")
+                let button = ActionSheetOkButton(title: "foo")
+                sheet.setup(items: [item1, item2, button])
+                sheet.refreshItems()
+                
+                expect(height.constant).to(equal(24))
             }
         }
         
         
-        // MARK: - Protected Functions
+        describe("refreshing buttons") {
+            
+            var height: NSLayoutConstraint!
+            
+            beforeEach {
+                height = NSLayoutConstraint()
+                sheet = createSheet()
+                sheet.buttonsTableViewHeight = height
+                ActionSheetItem.height = 12
+                ActionSheetOkButton.height = 13
+            }
+            
+            it("refreshes correctly if no items are set") {
+                sheet.refreshButtons()
+                
+                expect(height.constant).to(equal(0))
+            }
+            
+            it("refreshes correctly if items are set") {
+                let item = ActionSheetItem(title: "foo")
+                let button1 = ActionSheetOkButton(title: "foo")
+                let button2 = ActionSheetOkButton(title: "foo")
+                sheet.setup(items: [item, button1, button2])
+                sheet.refreshButtons()
+                
+                expect(height.constant).to(equal(26))
+            }
+        }
+        
         
         describe("handling tap on item") {
             
-            it("reloads data") {
-                let sheet = createSheet()
+            beforeEach {
+                sheet = createSheet()
                 sheet.reloadDataInvokeCount = 0
-                sheet.handleTap(on: createItem(""))
+            }
+            
+            it("reloads data") {
+                sheet.handleTap(on: ActionSheetItem(title: ""))
                 
                 expect(sheet.reloadDataInvokeCount).to(equal(1))
             }
             
             it("calls select action without dismiss if item has none tap action") {
                 var count = 0
-                let sheet = MockActionSheet(items: []) { (_, _) in count += 1 }
-                let item = createItem("")
-                item.tapBehavior = .none
+                sheet = MockActionSheet { (_, _) in count += 1 }
+                let item = ActionSheetItem(title: "", tapBehavior: .none)
                 sheet.handleTap(on: item)
                 
                 expect(count).to(equal(1))
@@ -478,34 +521,49 @@ class ActionSheetTests: QuickSpec {
             
             it("calls select action after dismiss if item has dismiss tap action") {
                 var count = 0
-                let sheet = MockActionSheet(items: []) { (_, _) in count += 1 }
-                let item = createItem("")
-                item.tapBehavior = .dismiss
+                sheet = MockActionSheet { (_, _) in count += 1 }
+                let item = ActionSheetItem(title: "", tapBehavior: .dismiss)
                 sheet.handleTap(on: item)
-//                expect(count).toEventually(equal(1), time)        TODO
-//                expect(sheet.dismissInvokeCount).to(equal(1))     TODO
+                
+                expect(count).to(equal(1))
+                expect(sheet.dismissInvokeCount).to(equal(1))
             }
         }
         
+        
         describe("margin at position") {
             
-            it("uses apperance if no superview value exists") {
+            beforeEach {
+                sheet = createSheet()
+            }
+            
+            it("ignores custom edge margins with smaller value than the default ones") {
                 let sheet = createSheet()
-                sheet.appearance.contentInset = 80
+                sheet.minimumContentInsets = UIEdgeInsets(top: -1, left: -1, bottom: -1, right: -1)
                 
-                expect(sheet.margin(at: .top)).to(equal(80))
-                expect(sheet.margin(at: .left)).to(equal(80))
-                expect(sheet.margin(at: .right)).to(equal(80))
-                expect(sheet.margin(at: .bottom)).to(equal(80))
+                expect(sheet.margin(at: .top)).to(equal(sheet.view.safeAreaInsets.top))
+                expect(sheet.margin(at: .left)).to(equal(sheet.view.safeAreaInsets.left))
+                expect(sheet.margin(at: .right)).to(equal(sheet.view.safeAreaInsets.right))
+                expect(sheet.margin(at: .bottom)).to(equal(sheet.view.safeAreaInsets.bottom))
+            }
+
+            it("uses custom edge margins with greated value than the default ones") {
+                let sheet = createSheet()
+                sheet.minimumContentInsets = UIEdgeInsets(top: 111, left: 222, bottom: 333, right: 444)
+                
+                expect(sheet.margin(at: .top)).to(equal(111))
+                expect(sheet.margin(at: .left)).to(equal(222))
+                expect(sheet.margin(at: .bottom)).to(equal(333))
+                expect(sheet.margin(at: .right)).to(equal(444))
             }
         }
         
         describe("reloading data") {
             
             it("reloads both table views") {
-                let view1 = MockTableView(frame: .zero)
-                let view2 = MockTableView(frame: .zero)
-                let sheet = createSheet()
+                let view1 = MockItemTableView(frame: .zero)
+                let view2 = MockButtonTableView(frame: .zero)
+                sheet = createSheet()
                 sheet.itemsTableView = view1
                 sheet.buttonsTableView = view2
                 sheet.reloadData()

+ 22 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/MockActionSheet.swift

@@ -11,6 +11,17 @@ class MockActionSheet: ActionSheet {
     var refreshItemsInvokeCount = 0
     var refreshHeaderInvokeCount = 0
     var reloadDataInvokeCount = 0
+    var setupInvokeCount = 0
+    var setupItemsInvokeCount = 0
+    var setupItemsInvokeItems = [[ActionSheetItem]]()
+    
+    
+    private var _presentingViewController: UIViewController?
+    override var presentingViewController: UIViewController? {
+        get { return _presentingViewController }
+        set { _presentingViewController = newValue }
+    }
+    
     
     override func dismiss(completion: @escaping () -> ()) {
         super.dismiss { completion() }
@@ -47,4 +58,15 @@ class MockActionSheet: ActionSheet {
         super.reloadData()
         reloadDataInvokeCount += 1
     }
+    
+    override func setup() {
+        super.setup()
+        setupInvokeCount += 1
+    }
+    
+    override func setup(items: [ActionSheetItem]) {
+        super.setup(items: items)
+        setupItemsInvokeCount += 1
+        setupItemsInvokeItems.append(items)
+    }
 }

+ 4 - 4
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Extensions/NSObject+ClassNameTests.swift

@@ -20,15 +20,15 @@ class NSObject_ClassNameTests: QuickSpec {
                 expect(ActionSheet.className).to(equal("ActionSheet"))
             }
             
-            it("is valid for inherited type") {
-                expect(TestSheet.className).to(equal("TestSheet"))
-            }
-            
             it("is valid for base type instance") {
                 let obj = ActionSheet(items: []) { _, _ in }
                 expect(obj.className).to(equal("ActionSheet"))
             }
             
+            it("is valid for inherited type") {
+                expect(TestSheet.className).to(equal("TestSheet"))
+            }
+            
             it("is valid for inherited type instance") {
                 let obj = TestSheet(items: []) { _, _ in }
                 expect(obj.className).to(equal("TestSheet"))

+ 2 - 2
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Info.plist

@@ -15,8 +15,8 @@
 	<key>CFBundlePackageType</key>
 	<string>BNDL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>1.1.0</string>
+	<string>1.2.2</string>
 	<key>CFBundleVersion</key>
-	<string>1901101458</string>
+	<string>1901161647</string>
 </dict>
 </plist>

+ 90 - 135
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/ActionSheetItemTests.swift

@@ -8,194 +8,149 @@
 
 import Quick
 import Nimble
-import Sheeeeeeeeet
+@testable import Sheeeeeeeeet
 
 class ActionSheetItemTests: QuickSpec {
     
-    func prepareStandardAppearance() {
-        let appearance = ActionSheetAppearance.standard.item
-        appearance.backgroundColor = .red
-        appearance.font = UIFont.systemFont(ofSize: 31)
-        appearance.height = 314
-        appearance.separatorInsets = UIEdgeInsets(top: 1, left: 20, bottom: 3, right: 40)
-        appearance.textColor = .green
-        appearance.tintColor = .blue
-        appearance.subtitleFont = UIFont.systemFont(ofSize: 34)
-        appearance.subtitleTextColor = .yellow
-    }
-    
-    func restoreStandardAppearance() {
-        let appearance = ActionSheetAppearance.standard.item
-        appearance.backgroundColor = nil
-        appearance.font = nil
-        appearance.height = 50
-        appearance.separatorInsets = .zero
-        appearance.textColor = nil
-        appearance.tintColor = nil
-        appearance.subtitleFont = nil
-        appearance.subtitleTextColor = nil
-    }
-    
-    func compare(
-        _ appearance1: ActionSheetItemAppearance,
-        _ appearance2: ActionSheetItemAppearance,
-        textColor: UIColor? = nil) -> Bool {
-        let textColor = textColor ?? appearance2.textColor
-        return appearance1.backgroundColor == appearance2.backgroundColor
-            && appearance1.font == appearance2.font
-            && appearance1.height == appearance2.height
-            && appearance1.separatorInsets == appearance2.separatorInsets
-            && appearance1.textColor == textColor
-            && appearance1.tintColor == appearance2.tintColor
-            && appearance1.subtitleFont == appearance2.subtitleFont
-            && appearance1.subtitleTextColor == appearance2.subtitleTextColor
-    }
-    
-    func compare(
-        _ cell: UITableViewCell,
-        item: ActionSheetItem,
-        appearance: ActionSheetItemAppearance,
-        textColor: UIColor? = nil,
-        textAlignment: NSTextAlignment = .left) -> Bool {
-        let compareColor = textColor ?? appearance.textColor
-        return cell.imageView?.image == item.image
-            && cell.selectionStyle == .default
-            //&& cell.separatorInset == appearance.item.separatorInsets))
-            && cell.tintColor == appearance.tintColor
-            && cell.textLabel?.text == item.title
-            && cell.textLabel?.textAlignment == textAlignment
-            && cell.textLabel?.textColor == compareColor
-            && cell.textLabel?.font == appearance.font
-            && cell.detailTextLabel?.text == item.subtitle
-            && cell.detailTextLabel?.textColor == appearance.subtitleTextColor
-            && cell.detailTextLabel?.font == appearance.subtitleFont
-    }
-    
-    
     override func spec() {
         
         func createItem(subtitle: String? = nil) -> MockActionSheetItem {
             return MockActionSheetItem(title: "foo", subtitle: subtitle, value: true, image: UIImage())
-            
         }
         
         func createItem(_ tapBehavior: ActionSheetItem.TapBehavior) -> MockActionSheetItem {
             return MockActionSheetItem(title: "foo", subtitle: "bar", value: true, image: UIImage(), tapBehavior: tapBehavior)
         }
         
-        beforeEach {
-            self.prepareStandardAppearance()
-        }
-        
-        afterEach {
-            self.restoreStandardAppearance()
-        }
-        
-        describe("when created") {
+        describe("created instance") {
+            
+            it("applies default values") {
+                let item = ActionSheetItem(title: "foo")
+                
+                expect(item.title).to(equal("foo"))
+                expect(item.subtitle).to(beNil())
+                expect(item.value).to(beNil())
+                expect(item.image).to(beNil())
+                expect(item.tapBehavior).to(equal(.dismiss))
+                expect(item.cellStyle).to(equal(.default))
+            }
             
             it("applies provided values") {
-                let item = createItem(.none)
+                let image = UIImage()
+                let item = ActionSheetItem(title: "foo", subtitle: "bar", value: true, image: image, tapBehavior: .none)
+                
                 expect(item.title).to(equal("foo"))
                 expect(item.subtitle).to(equal("bar"))
                 expect(item.value as? Bool).to(equal(true))
-                expect(item.image).toNot(beNil())
+                expect(item.image).to(be(image))
                 expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
-            }
-            
-            it("uses dismiss tap behavior by default") {
-                let item = createItem()
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
-            }
-            
-            it("copies standard item appearance initially") {
-                let item = createItem()
-                let standard = ActionSheetAppearance.standard.item
-                let isEqual = self.compare(item.appearance, standard)
-                expect(isEqual).to(beTrue())
+                expect(item.cellStyle).to(equal(.value1))
             }
         }
         
+        
         describe("cell reuse identifier") {
             
             it("is class name") {
                 let item = createItem()
+                
                 expect(item.cellReuseIdentifier).to(equal("MockActionSheetItem"))
             }
         }
         
-        describe("cell style") {
+        
+        describe("height") {
             
-            it("is default if no subtitle is set") {
-                let item = createItem(subtitle: nil)
-                expect(item.cellStyle).to(equal(.default))
+            let preset = CustomItem.height
+            
+            afterEach {
+                CustomItem.height = preset
             }
             
-            it("is value1 if subtitle is set") {
-                let item = createItem(subtitle: "bar")
-                expect(item.cellStyle).to(equal(.value1))
+            it("uses standard height if no custom value is registered") {
+                expect(ActionSheetItem.height).to(equal(50))
             }
-        }
-        
-        describe("custom appearance") {
             
-            it("is nil by default") {
-                let item = createItem()
-                expect(item.customAppearance).to(beNil())
+            it("only uses custom height for registered type") {
+                CustomItem.height = 123
+                
+                expect(CustomItem.height).to(equal(123))
+                expect(CustomItem(title: "").height).to(equal(123))
+                expect(ActionSheetItem.height).to(equal(50))
             }
         }
         
-        describe("applying appearance") {
-            
-            it("applies standard copy if no custom appearance is set") {
-                let item = createItem()
-                item.applyAppearance(ActionSheetAppearance.standard)
-                expect(self.compare(item.appearance, ActionSheetAppearance.standard.item)).to(beTrue())
-            }
+        
+        describe("resolving cell") {
             
-            it("applies custom appearance if set") {
+            it("returns correct cell") {
                 let item = createItem()
-                let standard = ActionSheetAppearance.standard
-                let custom = ActionSheetAppearance(copy: standard)
-                custom.item.backgroundColor = .yellow
-                item.customAppearance = custom.item
-                item.applyAppearance(standard)
-                expect(item.appearance).to(be(custom.item))
+                let cell = item.cell(for: UITableView(frame: .zero))
+                
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
+    }
+}
+
+
+class ActionSheetItemCellTests: QuickSpec {
+    
+    override func spec() {
         
-        describe("applying appearance to cell") {
-            
-            it("applies correct style") {
-                let item = createItem()
-                let appearance = ActionSheetAppearance.standard
-                let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
-                item.applyAppearance(appearance)
-                item.applyAppearance(to: cell)
-                expect(self.compare(cell, item: item, appearance: appearance.item)).to(beTrue())
-            }
+        var cell: ActionSheetItemCell!
+        var item: ActionSheetItem! {
+            didSet { cell = item.cell(for: UITableView()) }
         }
         
+        beforeEach {
+            item = ActionSheetItem(title: "foo")
+        }
         
         
-        describe("resolving cell") {
+        describe("moving to window") {
             
-            func tableView() -> UITableView {
-                return UITableView(frame: .zero)
+            it("refreshes cell") {
+                cell.refresh(with: item)
+                cell.textLabel?.text = ""
+                cell.didMoveToWindow()
+                
+                expect(cell.textLabel?.text).to(equal("foo"))
             }
+        }
+        
+        
+        describe("refreshing") {
             
-            it("always returns a cell even if table view fails to dequeue") {
-                let item = createItem()
-                let cell = item.cell(for: tableView())
-                expect(cell).toNot(beNil())
+            it("aborts if cell has no item reference") {
+                cell.refresh()
+                
+                expect(cell.textLabel?.text).to(beNil())
             }
             
-            it("applies appearance to cell") {
-                let item = createItem()
-                let cell = item.cell(for: tableView())
-                expect(item.applyAppearanceInvokeCount).to(equal(1))
-                expect(item.applyAppearanceInvokeCells.count).to(equal(1))
-                expect(item.applyAppearanceInvokeCells[0]).to(be(cell))
+            it("refreshes if cell has item reference") {
+                let image = UIImage()
+                item = ActionSheetItem(title: "foo", subtitle: "bar", value: "baz", image: image)
+                cell.titleColor = .yellow
+                cell.titleFont = .boldSystemFont(ofSize: 1)
+                cell.subtitleColor = .brown
+                cell.subtitleFont = .boldSystemFont(ofSize: 2)
+                
+                cell.refresh(with: item)
+                
+                expect(cell.imageView?.image).to(be(image))
+                expect(cell.selectionStyle).to(equal(.default))
+                expect(cell.textLabel?.font).toNot(beNil())
+                expect(cell.textLabel?.font).to(be(cell.titleFont))
+                expect(cell.textLabel?.text).to(equal("foo"))
+                expect(cell.textLabel?.textAlignment).to(equal(.left))
+                expect(cell.detailTextLabel?.font).toNot(beNil())
+                expect(cell.detailTextLabel?.font).to(be(cell.subtitleFont))
+                expect(cell.detailTextLabel?.text).to(equal("bar"))
             }
         }
     }
 }
+
+
+private class CustomItem: ActionSheetItem {}

+ 41 - 38
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetButtonTests.swift

@@ -10,73 +10,76 @@ import Quick
 import Nimble
 import Sheeeeeeeeet
 
-class ActionSheetButtonTests: ActionSheetItemTests {
+class ActionSheetButtonTests: QuickSpec {
     
     override func spec() {
         
         var item: ActionSheetButton!
         
-        beforeEach {
-            item = ActionSheetButton(title: "foo", type: .ok)
-            self.prepareStandardAppearance()
-        }
-        
-        afterEach {
-            self.restoreStandardAppearance()
-        }
         
         describe("when created with value") {
             
-            beforeEach {
+            it("is correctly setup") {
                 item = ActionSheetButton(title: "foo", value: "bar")
-            }
-            
-            it("applies provided values") {
+                
                 expect(item.title).to(equal("foo"))
                 expect(item.value as? String).to(equal("bar"))
-            }
-            
-            it("is correctly setup") {
                 expect(item.isOkButton).to(beFalse())
             }
         }
         
-        describe("when created with type") {
+        
+        describe("when created with button type") {
             
-            it("applies provided values") {
+            it("is correctly setup") {
+                item = ActionSheetButton(title: "foo", type: .ok)
+                
                 expect(item.title).to(equal("foo"))
+                expect(item.value as? ActionSheetButton.ButtonType).to(equal(.ok))
             }
+        }
+        
+        
+        describe("cell") {
             
-            it("is correctly setup") {
-                expect(item.value as? ActionSheetButton.ButtonType).to(equal(.ok))
-                expect(item.isOkButton).to(beTrue())
+            it("is of correct type") {
+                item = ActionSheetButton(title: "foo", type: .ok)
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetButtonCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
         
-        describe("applying appearance") {
+        
+        describe("button type") {
             
-            it("applies standard copy if no custom appearance is set") {
-                item.applyAppearance(ActionSheetAppearance.standard)
-                expect(self.compare(item.appearance, ActionSheetAppearance.standard.okButton)).to(beTrue())
+            func createItem(type: ActionSheetButton.ButtonType) -> ActionSheetButton {
+                return ActionSheetButton(title: "foo", type: type)
             }
             
-            it("applies custom appearance if set") {
-                let standard = ActionSheetAppearance.standard
-                let custom = ActionSheetAppearance(copy: standard)
-                item.customAppearance = custom.okButton
-                item.applyAppearance(standard)
-                expect(item.appearance).to(be(custom.okButton))
+            it("is correct for each button type") {
+                expect(createItem(type: .cancel).isOkButton).to(beFalse())
+                expect(createItem(type: .cancel).isCancelButton).to(beTrue())
+                expect(createItem(type: .ok).isOkButton).to(beTrue())
+                expect(createItem(type: .ok).isCancelButton).to(beFalse())
             }
         }
+    }
+}
+
+
+class ActionSheetButtonCellTests: QuickSpec {
+    
+    override func spec() {
         
-        describe("applying appearance to cell") {
+        describe("refreshing") {
             
-            it("applies correct style") {
-                let appearance = ActionSheetAppearance.standard
-                let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
-                item.applyAppearance(appearance)
-                item.applyAppearance(to: cell)
-                expect(self.compare(cell, item: item, appearance: appearance.okButton, textAlignment: .center)).to(beTrue())
+            it("center aligns text label") {
+                let item = ActionSheetButton(title: "", value: nil)
+                let cell = item.cell(for: UITableView())
+                cell.refresh()
+                expect(cell.textLabel?.textAlignment).to(equal(.center))
             }
         }
     }

+ 16 - 20
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetCancelButtonTests.swift

@@ -10,37 +10,33 @@ import Quick
 import Nimble
 import Sheeeeeeeeet
 
-class ActionSheetCancelButtonTests: ActionSheetItemTests {
+class ActionSheetCancelButtonTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetCancelButton(title: "foo")
+        var item: ActionSheetCancelButton!
         
-        describe("when created") {
-            
-            it("applies provided values") {
-                expect(item.title).to(equal("foo"))
-            }
+        beforeEach {
+            item = ActionSheetCancelButton(title: "cancel")
+        }
+        
+        
+        describe("created instance") {
             
             it("is correctly setup") {
+                expect(item.title).to(equal("cancel"))
                 expect(item.value as? ActionSheetButton.ButtonType).to(equal(.cancel))
-                expect(item.isCancelButton).to(beTrue())
             }
         }
         
-        describe("applying appearance") {
-            
-            it("applies standard copy if no custom appearance is set") {
-                item.applyAppearance(ActionSheetAppearance.standard)
-                expect(self.compare(item.appearance, ActionSheetAppearance.standard.cancelButton)).to(beTrue())
-            }
+        
+        describe("cell") {
             
-            it("applies custom appearance if set") {
-                let standard = ActionSheetAppearance.standard
-                let custom = ActionSheetAppearance(copy: standard)
-                item.customAppearance = custom.cancelButton
-                item.applyAppearance(standard)
-                expect(item.appearance).to(be(custom.cancelButton))
+            it("is of correct type") {
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetCancelButtonCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 16 - 20
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetDangerButtonTests.swift

@@ -10,37 +10,33 @@ import Quick
 import Nimble
 import Sheeeeeeeeet
 
-class ActionSheetDangerButtonTests: ActionSheetItemTests {
+class ActionSheetDangerButtonTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetDangerButton(title: "foo")
+        var item: ActionSheetDangerButton!
         
-        describe("when created") {
-            
-            it("applies provided values") {
-                expect(item.title).to(equal("foo"))
-            }
+        beforeEach {
+            item = ActionSheetDangerButton(title: "danger")
+        }
+        
+        
+        describe("created instance") {
             
             it("is correctly setup") {
+                expect(item.title).to(equal("danger"))
                 expect(item.value as? ActionSheetButton.ButtonType).to(equal(.ok))
-                expect(item.isOkButton).to(beTrue())
             }
         }
         
-        describe("applying appearance") {
-            
-            it("applies standard copy if no custom appearance is set") {
-                item.applyAppearance(ActionSheetAppearance.standard)
-                expect(self.compare(item.appearance, ActionSheetAppearance.standard.dangerButton, textColor: .red)).to(beTrue())
-            }
+        
+        describe("cell") {
             
-            it("applies custom appearance if set") {
-                let standard = ActionSheetAppearance.standard
-                let custom = ActionSheetAppearance(copy: standard)
-                item.customAppearance = custom.dangerButton
-                item.applyAppearance(standard)
-                expect(item.appearance).to(be(custom.dangerButton))
+            it("is of correct type") {
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetDangerButtonCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 16 - 20
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetOkButtonTests.swift

@@ -10,37 +10,33 @@ import Quick
 import Nimble
 import Sheeeeeeeeet
 
-class ActionSheetOkButtonTests: ActionSheetItemTests {
+class ActionSheetOkButtonTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetOkButton(title: "foo")
+        var item: ActionSheetOkButton!
         
-        describe("when created") {
-            
-            it("applies provided values") {
-                expect(item.title).to(equal("foo"))
-            }
+        beforeEach {
+            item = ActionSheetOkButton(title: "ok")
+        }
+        
+        
+        describe("created instance") {
             
             it("is correctly setup") {
+                expect(item.title).to(equal("ok"))
                 expect(item.value as? ActionSheetButton.ButtonType).to(equal(.ok))
-                expect(item.isOkButton).to(beTrue())
             }
         }
         
-        describe("applying appearance") {
-            
-            it("applies standard copy if no custom appearance is set") {
-                item.applyAppearance(ActionSheetAppearance.standard)
-                expect(self.compare(item.appearance, ActionSheetAppearance.standard.okButton)).to(beTrue())
-            }
+        
+        describe("cell") {
             
-            it("applies custom appearance if set") {
-                let standard = ActionSheetAppearance.standard
-                let custom = ActionSheetAppearance(copy: standard)
-                item.customAppearance = custom.okButton
-                item.applyAppearance(standard)
-                expect(item.appearance).to(be(custom.okButton))
+            it("is of correct type") {
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetOkButtonCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 24 - 10
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetLinkItemTests.swift

@@ -14,21 +14,35 @@ class ActionSheetLinkItemTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetLinkItem(title: "foo", value: true, image: UIImage())
-        
-        describe("when created") {
+        describe("cell") {
             
-            it("applies provided values") {
-                expect(item.title).to(equal("foo"))
-                expect(item.value as? Bool).to(equal(true))
-                expect(item.image).toNot(beNil())
+            it("is of correct type") {
+                let item = ActionSheetLinkItem(title: "foo")
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetLinkItemCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
+    }
+}
+
+
+class ActionSheetLinkItemCellTests: QuickSpec {
+    
+    override func spec() {
         
-        describe("tap behavior") {
+        describe("refreshing") {
             
-            it("is dismiss") {
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
+            it("applies accessory view with link icon") {
+                let item = ActionSheetLinkItem(title: "foo")
+                let cell = item.cell(for: UITableView()) as? ActionSheetLinkItemCell
+                cell?.linkIcon = UIImage()
+                cell?.refresh()
+                let imageView = cell?.accessoryView as? UIImageView
+                
+                expect(imageView?.image).toNot(beNil())
+                expect(imageView?.image).to(be(cell?.linkIcon))
             }
         }
     }

+ 46 - 15
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectItemTests.swift

@@ -14,31 +14,62 @@ class ActionSheetMultiSelectItemTests: QuickSpec {
     
     override func spec() {
         
-        func getItem(isSelected: Bool, group: String = "") -> ActionSheetMultiSelectItem {
-            return ActionSheetMultiSelectItem(title: "foo", isSelected: isSelected, group: group, value: true, image: UIImage())
-        }
-        
-        describe("when created") {
+        describe("instance") {
+            
+            it("can be created with default values") {
+                let item = ActionSheetMultiSelectItem(title: "foo", isSelected: false)
+                expect(item.title).to(equal("foo"))
+                expect(item.isSelected).to(beFalse())
+                expect(item.group).to(equal(""))
+                expect(item.value).to(beNil())
+                expect(item.image).to(beNil())
+                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
+            }
             
-            it("applies provided values") {
-                let item = getItem(isSelected: true, group: "my group")
+            it("can be created with custom values") {
+                let item = ActionSheetMultiSelectItem(title: "foo", isSelected: true, group: "group", value: true, image: UIImage())
                 expect(item.title).to(equal("foo"))
-                expect(item.group).to(equal("my group"))
+                expect(item.isSelected).to(beTrue())
+                expect(item.group).to(equal("group"))
                 expect(item.value as? Bool).to(equal(true))
                 expect(item.image).toNot(beNil())
+                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
             }
+        }
+        
+        
+        describe("cell") {
             
-            it("applies provided selection state") {
-                expect(getItem(isSelected: true).isSelected).to(beTrue())
-                expect(getItem(isSelected: false).isSelected).to(beFalse())
+            it("is of correct type") {
+                let item = ActionSheetMultiSelectItem(title: "foo", isSelected: false)
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetMultiSelectItemCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
         
-        describe("tap behavior") {
+        
+        describe("handling tap") {
             
-            it("is none") {
-                let item = getItem(isSelected: true)
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
+            it("updates toggle item in the same group") {
+                let item1 = ActionSheetMultiSelectItem(title: "foo", isSelected: false, group: "group 1")
+                let item2 = ActionSheetMultiSelectItem(title: "bar", isSelected: false, group: "group 2")
+                let item3 = ActionSheetMultiSelectItem(title: "baz", isSelected: false, group: "group 1")
+                let toggle1 = ActionSheetMultiSelectToggleItem(title: "toggle 1", state: .selectAll, group: "group 1", selectAllTitle: "", deselectAllTitle: "")
+                let toggle2 = ActionSheetMultiSelectToggleItem(title: "toggle 2", state: .selectAll, group: "group 2", selectAllTitle: "", deselectAllTitle: "")
+                let items = [item1, item2, item3, toggle1, toggle2]
+                let sheet = ActionSheet(items: items) { (_, _) in }
+                
+                item1.handleTap(in: sheet)
+                expect(toggle1.state).to(equal(.selectAll))
+                expect(toggle2.state).to(equal(.selectAll))
+                item2.handleTap(in: sheet)
+                expect(toggle1.state).to(equal(.selectAll))
+                expect(toggle2.state).to(equal(.deselectAll))
+                item3.handleTap(in: sheet)
+                expect(toggle1.state).to(equal(.deselectAll))
+                expect(toggle2.state).to(equal(.deselectAll))
             }
         }
     }

+ 59 - 11
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectToggleItemTests.swift

@@ -14,32 +14,80 @@ class ActionSheetMultiSelectToggleItemTests: QuickSpec {
     
     override func spec() {
         
-        func getItem(group: String = "") -> ActionSheetMultiSelectToggleItem {
+        func createItem(group: String) -> ActionSheetMultiSelectToggleItem {
             return ActionSheetMultiSelectToggleItem(title: "foo", state: .selectAll, group: group, selectAllTitle: "select all", deselectAllTitle: "deselect all")
         }
         
-        describe("when created") {
+        describe("instance") {
             
-            it("applies provided values") {
-                let item = getItem(group: "my group")
+            it("can be created with custom values") {
+                let item = createItem(group: "group")
                 expect(item.title).to(equal("foo"))
-                expect(item.group).to(equal("my group"))
+                expect(item.state).to(equal(.selectAll))
+                expect(item.group).to(equal("group"))
+                expect(item.value).to(beNil())
                 expect(item.selectAllTitle).to(equal("select all"))
                 expect(item.deselectAllTitle).to(equal("deselect all"))
+                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
             }
         }
         
-        describe("cell style") {
+    
+        describe("cell") {
             
-            it("is value1") {
-                expect(getItem().cellStyle).to(equal(UITableViewCell.CellStyle.value1))
+            it("is of correct type") {
+                let item = createItem(group: "group")
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetMultiSelectToggleItemCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
         
-        describe("tap behavior") {
+        
+        describe("handling tap") {
+            
+            var sheet: ActionSheet!
+            var item1: ActionSheetMultiSelectItem!
+            var item2: ActionSheetMultiSelectItem!
+            var item3: ActionSheetMultiSelectItem!
+            var toggle1: ActionSheetMultiSelectToggleItem!
+            var toggle2: ActionSheetMultiSelectToggleItem!
+            
+            beforeEach {
+                item1 = ActionSheetMultiSelectItem(title: "foo", isSelected: false, group: "group 1")
+                item2 = ActionSheetMultiSelectItem(title: "bar", isSelected: false, group: "group 2")
+                item3 = ActionSheetMultiSelectItem(title: "baz", isSelected: false, group: "group 1")
+                toggle1 = createItem(group: "group 1")
+                toggle2 = createItem(group: "group 3")
+                sheet = ActionSheet(items: [item1, item2, item3, toggle1, toggle2]) { (_, _) in }
+            }
+            
+            it("resets state if no matching items exist in sheet") {
+                toggle2.state = .deselectAll
+                toggle2.handleTap(in: sheet)
+                expect(toggle2.state).to(equal(.selectAll))
+            }
             
-            it("is none") {
-                expect(getItem().tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
+            it("toggles select items in the same group") {
+                toggle1.handleTap(in: sheet)
+                expect(item1.isSelected).to(beTrue())
+                expect(item2.isSelected).to(beFalse())
+                expect(item3.isSelected).to(beTrue())
+                expect(toggle1.state).to(equal(.deselectAll))
+                expect(toggle2.state).to(equal(.selectAll))
+                toggle1.handleTap(in: sheet)
+                expect(item1.isSelected).to(beFalse())
+                expect(item2.isSelected).to(beFalse())
+                expect(item3.isSelected).to(beFalse())
+                expect(toggle1.state).to(equal(.selectAll))
+                expect(toggle2.state).to(equal(.selectAll))
+                toggle2.handleTap(in: sheet)
+                expect(item1.isSelected).to(beFalse())
+                expect(item2.isSelected).to(beFalse())
+                expect(item3.isSelected).to(beFalse())
+                expect(toggle1.state).to(equal(.selectAll))
+                expect(toggle2.state).to(equal(.selectAll))
             }
         }
     }

+ 83 - 38
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSelectItemTests.swift

@@ -8,71 +8,116 @@
 
 import Quick
 import Nimble
-import Sheeeeeeeeet
+@testable import Sheeeeeeeeet
 
 class ActionSheetSelectItemTests: QuickSpec {
     
     override func spec() {
         
-        func getItem(isSelected: Bool = false) -> ActionSheetSelectItem {
-            return ActionSheetSelectItem(title: "foo", isSelected: isSelected, value: true, image: UIImage())
-        }
         
-        describe("when created") {
+        describe("instance") {
+            
+            it("can be created with default values") {
+                let item = ActionSheetSelectItem(title: "foo", isSelected: false)
+                expect(item.title).to(equal("foo"))
+                expect(item.isSelected).to(beFalse())
+                expect(item.group).to(equal(""))
+                expect(item.value).to(beNil())
+                expect(item.image).to(beNil())
+                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
+            }
             
-            it("applies provided values") {
-                let item = ActionSheetSelectItem(title: "foo", isSelected: true, group: "my group", value: true, image: UIImage(), tapBehavior: .none)
+            it("can be created with custom values") {
+                let item = ActionSheetSelectItem(title: "foo", isSelected: true, group: "group", value: true, image: UIImage())
                 expect(item.title).to(equal("foo"))
                 expect(item.isSelected).to(beTrue())
-                expect(item.group).to(equal("my group"))
+                expect(item.group).to(equal("group"))
                 expect(item.value as? Bool).to(equal(true))
                 expect(item.image).toNot(beNil())
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
+                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
             }
+        }
+        
+        
+        describe("cell") {
             
-            it("applies provided selection state") {
-                expect(getItem(isSelected: true).isSelected).to(beTrue())
-                expect(getItem(isSelected: false).isSelected).to(beFalse())
+            it("is of correct type") {
+                let item = ActionSheetSelectItem(title: "foo", isSelected: false)
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetSelectItemCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
         
-        describe("tap behavior") {
+        
+        describe("handling tap") {
             
-            it("is dismiss by default") {
-                let item = getItem()
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
+            it("toggles selected state") {
+                let item = ActionSheetSelectItem(title: "foo", isSelected: false)
+                let sheet = ActionSheet { _, _ in }
+                item.handleTap(in: sheet)
+                expect(item.isSelected).to(beTrue())
+                item.handleTap(in: sheet)
+                expect(item.isSelected).to(beFalse())
             }
         }
+    }
+}
+
+
+class ActionSheetSelectItemCellTests: QuickSpec {
+    
+    override func spec() {
         
-        describe("when tapped") {
+        describe("refreshing") {
             
-            var sheet: ActionSheet!
+            var item: ActionSheetSelectItem!
+            var cell: ActionSheetSelectItemCell!
             
             beforeEach {
-                sheet = ActionSheet(items: [
-                    getItem(isSelected: true),
-                    getItem(isSelected: false)
-                    ], action: { _, _ in })
+                let label = UILabel()
+                item = ActionSheetSelectItem(title: "foo", isSelected: false)
+                cell = item.cell(for: UITableView()) as? ActionSheetSelectItemCell
+                cell.tintColor = UIColor.purple.withAlphaComponent(0.1)
+                cell.titleColor = UIColor.yellow.withAlphaComponent(0.1)
+                cell.titleFont = .systemFont(ofSize: 11)
+                cell.subtitleColor = UIColor.red.withAlphaComponent(0.1)
+                cell.subtitleFont = .systemFont(ofSize: 12)
+                cell.selectedIcon = UIImage()
+                cell.selectedIconColor = .green
+                cell.selectedTintColor = .purple
+                cell.selectedTitleColor = .yellow
+                cell.selectedTitleFont = .systemFont(ofSize: 13)
+                cell.selectedSubtitleColor = .red
+                cell.selectedSubtitleFont = .systemFont(ofSize: 14)
+                cell.unselectedIcon = UIImage()
+                cell.unselectedIconColor = UIColor.green.withAlphaComponent(0.1)
+                cell.refresh(with: item)
             }
             
-            it("selects unselected item") {
-                let item = getItem(isSelected: false)
-                item.handleTap(in: sheet)
-                expect(item.isSelected).to(beTrue())
+            it("refreshes correctly for selected item") {
+                item.isSelected = true
+                cell.refresh()
+                expect((cell.accessoryView as? UIImageView)?.image).to(be(cell.selectedIcon))
+                expect(cell.accessoryView?.tintColor).to(be(cell.selectedIconColor))
+                expect(cell.tintColor).to(be(cell.selectedTintColor))
+                expect(cell.textLabel?.textColor).to(be(cell.selectedTitleColor))
+                expect(cell.textLabel?.font).to(be(cell.selectedTitleFont))
+//                expect(cell.detailTextLabel?.textColor).to(be(cell.selectedSubtitleColor))
+//                expect(cell.detailTextLabel?.font).to(be(cell.selectedSubtitleFont))
             }
             
-            it("deselects selected item") {
-                let item = getItem(isSelected: true)
-                item.handleTap(in: sheet)
-                expect(item.isSelected).to(beFalse())
-            }
-            
-            it("does not affect other sheet items") {
-                let item = getItem(isSelected: true)
-                item.handleTap(in: sheet)
-                let items = sheet.items.compactMap { $0 as? ActionSheetSelectItem }
-                expect(items.first!.isSelected).to(beTrue())
-                expect(items.last!.isSelected).to(beFalse())
+            it("refreshes correctly for unselected item") {
+                item.isSelected = false
+                cell.refresh()
+                expect((cell.accessoryView as? UIImageView)?.image).to(be(cell.unselectedIcon))
+                expect(cell.accessoryView?.tintColor).to(be(cell.unselectedIconColor))
+                expect(cell.tintColor).to(be(cell.tintColor))
+                expect(cell.textLabel?.textColor).to(be(cell.titleColor))
+                expect(cell.textLabel?.font).to(be(cell.titleFont))
+//                expect(cell.detailTextLabel?.textColor).to(be(cell.subtitleColor))
+//                expect(cell.detailTextLabel?.font).to(be(cell.subtitleFont))
             }
         }
     }

+ 29 - 63
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSingleSelectItemTests.swift

@@ -14,73 +14,39 @@ class ActionSheetSingleSelectItemTests: QuickSpec {
     
     override func spec() {
         
-        func getItem(isSelected: Bool = false, group: String = "") -> ActionSheetSingleSelectItem {
-            return ActionSheetSingleSelectItem(title: "foo", isSelected: isSelected, group: group, value: true, image: UIImage())
-        }
-        
-        describe("when created") {
-            
-            it("applies provided values") {
-                let item = ActionSheetSingleSelectItem(title: "foo", isSelected: true, group: "my group", value: true, image: UIImage(), tapBehavior: .none)
-                expect(item.title).to(equal("foo"))
-                expect(item.isSelected).to(beTrue())
-                expect(item.group).to(equal("my group"))
-                expect(item.value as? Bool).to(equal(true))
-                expect(item.image).toNot(beNil())
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
-            }
-            
-            it("applies provided selection state") {
-                expect(getItem(isSelected: true).isSelected).to(beTrue())
-                expect(getItem(isSelected: false).isSelected).to(beFalse())
+        describe("cell") {
+            
+            it("is of correct type") {
+                let item = ActionSheetSingleSelectItem(title: "foo", isSelected: false)
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetSingleSelectItemCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
         
-        describe("tap behavior") {
-            
-            it("is dismiss by default") {
-                let item = getItem()
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.dismiss))
-            }
-        }
         
-        describe("when tapped") {
-            
-            var sheet: ActionSheet!
-            
-            beforeEach {
-                sheet = ActionSheet(items: [
-                    getItem(isSelected: true, group: "foo"),
-                    getItem(isSelected: false, group: "foo"),
-                    getItem(isSelected: true, group: "bar"),
-                    getItem(isSelected: false, group: "bar"),
-                    getItem(isSelected: true, group: "baz"),
-                    getItem(isSelected: false, group: "baz")
-                    ], action: { _, _ in })
-            }
-            
-            it("selects unselected item") {
-                let item = getItem(isSelected: false)
-                item.handleTap(in: sheet)
-                expect(item.isSelected).to(beTrue())
-            }
-            
-            it("does not deselect selected item") {
-                let item = getItem(isSelected: true)
-                item.handleTap(in: sheet)
-                expect(item.isSelected).to(beTrue())
-            }
-            
-            it("does not affect sheet items in other groups") {
-                let item = getItem(isSelected: false, group: "baz")
-                item.handleTap(in: sheet)
-                let items = sheet.items.compactMap { $0 as? ActionSheetSingleSelectItem }
-                expect(items[0].isSelected).to(beTrue())
-                expect(items[1].isSelected).to(beFalse())
-                expect(items[2].isSelected).to(beTrue())
-                expect(items[3].isSelected).to(beFalse())
-                expect(items[4].isSelected).to(beFalse())
-                expect(items[5].isSelected).to(beFalse())
+        describe("handling tap") {
+            
+            it("deselects other single select items in the same group") {
+                let item1 = ActionSheetSingleSelectItem(title: "foo", isSelected: false, group: "group 1")
+                let item2 = ActionSheetSingleSelectItem(title: "bar", isSelected: false, group: "group 2")
+                let item3 = ActionSheetSingleSelectItem(title: "baz", isSelected: false, group: "group 1")
+                let items = [item1, item2, item3]
+                let sheet = ActionSheet(items: items) { (_, _) in }
+                
+                item1.handleTap(in: sheet)
+                expect(item1.isSelected).to(beTrue())
+                expect(item2.isSelected).to(beFalse())
+                expect(item3.isSelected).to(beFalse())
+                item2.handleTap(in: sheet)
+                expect(item1.isSelected).to(beTrue())
+                expect(item2.isSelected).to(beTrue())
+                expect(item3.isSelected).to(beFalse())
+                item3.handleTap(in: sheet)
+                expect(item1.isSelected).to(beFalse())
+                expect(item2.isSelected).to(beTrue())
+                expect(item3.isSelected).to(beTrue())
             }
         }
     }

+ 2 - 17
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Mocks/MockActionSheetItem.swift

@@ -10,25 +10,10 @@ import Sheeeeeeeeet
 
 class MockActionSheetItem: ActionSheetItem {
     
-    var applyAppearanceInvokeCount = 0
-    var applyAppearanceInvokeAppearances = [ActionSheetAppearance]()
-    var applyAppearanceInvokeCells = [UITableViewCell]()
     var handleTapInvokeCount = 0
     var handleTapInvokeActionSheets = [ActionSheet]()
     
-    var cell: UITableViewCell?
-    
-    override func applyAppearance(_ appearance: ActionSheetAppearance) {
-        super.applyAppearance(appearance)
-        applyAppearanceInvokeCount += 1
-        applyAppearanceInvokeAppearances.append(appearance)
-    }
-    
-    override func applyAppearance(to cell: UITableViewCell) {
-        super.applyAppearance(to: cell)
-        applyAppearanceInvokeCount += 1
-        applyAppearanceInvokeCells.append(cell)
-    }
+    var cell: ActionSheetItemCell?
     
     override func handleTap(in actionSheet: ActionSheet) {
         super.handleTap(in: actionSheet)
@@ -36,7 +21,7 @@ class MockActionSheetItem: ActionSheetItem {
         handleTapInvokeActionSheets.append(actionSheet)
     }
     
-    override func cell(for tableView: UITableView) -> UITableViewCell {
+    override func cell(for tableView: UITableView) -> ActionSheetItemCell {
         return cell ?? super.cell(for: tableView)
     }
 }

+ 15 - 11
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionMarginTests.swift

@@ -14,27 +14,31 @@ class ActionSheetSectionMarginTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetSectionMargin()
+        var item: ActionSheetSectionMargin!
         
-        describe("when created") {
+        beforeEach {
+            item = ActionSheetSectionMargin()
+        }
+        
+        
+        describe("instance") {
             
-            it("applies provided values") {
+            it("is correctly configured") {
                 expect(item.title).to(equal(""))
                 expect(item.value).to(beNil())
                 expect(item.image).to(beNil())
-            }
-            
-            it("applies non-provided values") {
                 expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
             }
         }
         
-        describe("applying appearance to cell") {
+        
+        describe("cell") {
             
-            it("is correctly configures cell") {
-                let cell = UITableViewCell()
-                item.applyAppearance(to: cell)
-                expect(cell.selectionStyle).to(equal(UITableViewCell.SelectionStyle.none))
+            it("is of correct type") {
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetSectionMarginCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 13 - 14
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionTitleTests.swift

@@ -14,29 +14,28 @@ class ActionSheetSectionTitleTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetSectionTitle(title: "foo", subtitle: "bar")
+        var item: ActionSheetSectionTitle!
         
-        describe("when created") {
+        describe("instance") {
             
-            it("applies provided values") {
+            it("is correctly configured") {
+                item = ActionSheetSectionTitle(title: "foo", subtitle: "bar")
+                
                 expect(item.title).to(equal("foo"))
                 expect(item.subtitle).to(equal("bar"))
-                expect(item.value).to(beNil())
-                expect(item.image).to(beNil())
-            }
-            
-            it("applies non-provided values") {
-                expect(item.cellStyle).to(equal(UITableViewCell.CellStyle.value1))
                 expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
             }
         }
         
-        describe("applying appearance to cell") {
+        
+        describe("cell") {
             
-            it("is correctly configures cell") {
-                let cell = UITableViewCell()
-                item.applyAppearance(to: cell)
-                expect(cell.selectionStyle).to(equal(UITableViewCell.SelectionStyle.none))
+            it("is of correct type") {
+                item = ActionSheetSectionTitle(title: "foo")
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetSectionTitleCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 13 - 15
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetTitleTests.swift

@@ -14,29 +14,27 @@ class ActionSheetTitleTests: QuickSpec {
     
     override func spec() {
         
-        let item = ActionSheetTitle(title: "foo")
+        var item: ActionSheetTitle!
         
-        describe("when created") {
+        describe("instance") {
             
-            it("applies provided values") {
+            it("is correctly configured") {
+                item = ActionSheetTitle(title: "foo")
+                
                 expect(item.title).to(equal("foo"))
-                expect(item.value).to(beNil())
-                expect(item.image).to(beNil())
-                expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
-            }
-            
-            it("applies non-provided values") {
                 expect(item.tapBehavior).to(equal(ActionSheetItem.TapBehavior.none))
             }
         }
         
-        describe("applying appearance to cell") {
+        
+        describe("cell") {
             
-            it("is correctly configures cell") {
-                let cell = UITableViewCell()
-                item.applyAppearance(to: cell)
-                expect(cell.selectionStyle).to(equal(UITableViewCell.SelectionStyle.none))
-                expect(cell.textLabel?.textAlignment).to(equal(NSTextAlignment.center))
+            it("is of correct type") {
+                item = ActionSheetTitle(title: "foo")
+                let cell = item.cell(for: UITableView())
+                
+                expect(cell is ActionSheetTitleCell).to(beTrue())
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
     }

+ 175 - 4
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetPopoverPresenterTests.swift

@@ -15,8 +15,11 @@ class ActionSheetPopoverPresenterTests: QuickSpec {
     override func spec() {
         
         var presenter: ActionSheetPopoverPresenter!
-        var sheet: ActionSheet!
+        var sheet: MockActionSheet!
         var headerView: UIView!
+        var headerViewContainer: ActionSheetHeaderView!
+        var itemView: ActionSheetItemTableView!
+        var buttonView: ActionSheetButtonTableView!
         
         beforeEach {
             let items: [ActionSheetItem] = [
@@ -25,12 +28,23 @@ class ActionSheetPopoverPresenterTests: QuickSpec {
                 ActionSheetOkButton(title: "ok"),
                 ActionSheetMultiSelectItem(title: "item 2", isSelected: true)
             ]
+            
             headerView = UIView(frame: .zero)
-            sheet = ActionSheet(items: items) { (_, _) in }
+            headerViewContainer = ActionSheetHeaderView(frame: .zero)
+            itemView = ActionSheetItemTableView(frame: .zero)
+            buttonView = ActionSheetButtonTableView(frame: .zero)
+            
+            sheet = MockActionSheet(items: items) { (_, _) in }
+            sheet.headerViewContainer = headerViewContainer
             sheet.headerView = headerView
+            sheet.itemsTableView = itemView
+            sheet.buttonsTableView = buttonView
+            
             presenter = ActionSheetPopoverPresenter()
+            presenter.actionSheet = sheet
         }
         
+        
         describe("background tap dismissal") {
             
             it("is enabled by default") {
@@ -38,14 +52,171 @@ class ActionSheetPopoverPresenterTests: QuickSpec {
             }
         }
         
+        
+        describe("dismissing") {
+            
+            it("completes dismissal directly if action sheet has no presenting view controller") {
+                var count = 0
+                presenter.dismiss { count += 1 }
+                
+                expect(count).to(equal(1))
+                expect(presenter.actionSheet).to(beNil())
+            }
+            
+            it("completes dismissal after presenting view controller has finished dismissing") {
+                var count = 0
+                let vc = MockViewController()
+                sheet.presentingViewController = vc
+                presenter.dismiss { count += 1 }
+                
+                expect(vc.dismissInvokeCount).to(equal(1))
+                expect(vc.dismissInvokeAnimateds).to(equal([true]))
+                expect(vc.dismissInvokeCompletions.count).to(equal(1))
+                expect(count).to(equal(0))
+                expect(presenter.actionSheet).toNot(beNil())
+                
+                vc.completeDismissal()
+                
+                expect(count).to(equal(1))
+                expect(presenter.actionSheet).to(beNil())
+            }
+        }
+        
+        
+        describe("presenting action sheet from view") {
+            
+            var vc: MockViewController!
+            var view: UIView!
+            var completion: (() -> ())!
+            
+            beforeEach {
+                vc = MockViewController()
+                view = UIView(frame: CGRect(x: 1, y: 2, width: 3, height: 4))
+                completion = {}
+                presenter.present(sheet: sheet, in: vc, from: view, completion: completion)
+            }
+            
+            it("sets up sheet for popover presentation") {
+                expect(sheet.items.count).to(equal(3))
+                expect(sheet.buttons.count).to(equal(0))
+                expect(sheet.modalPresentationStyle).to(equal(.popover))
+            }
+            
+            it("sets up popover presentation controller") {
+                expect(presenter.popover?.delegate).to(be(presenter))
+                expect(presenter.popover?.sourceView).to(be(view))
+                expect(presenter.popover?.sourceRect).to(equal(view.bounds))
+            }
+            
+            it("performs presentation") {
+                expect(vc.presentInvokeCount).to(equal(1))
+                expect(vc.presentInvokeVcs).to(equal([sheet]))
+                expect(vc.presentInvokeAnimateds).to(equal([true]))
+                expect(vc.presentInvokeCompletions.count).to(equal(1))
+            }
+        }
+        
+        
+        describe("presenting action sheet from bar button item") {
+            
+            var vc: MockViewController!
+            var item: UIBarButtonItem!
+            var completion: (() -> ())!
+            
+            beforeEach {
+                vc = MockViewController()
+                item = UIBarButtonItem(customView: UIView(frame: .zero))
+                completion = {}
+                presenter.present(sheet: sheet, in: vc, from: item, completion: completion)
+            }
+            
+            it("sets up sheet for popover presentation") {
+                expect(sheet.items.count).to(equal(3))
+                expect(sheet.buttons.count).to(equal(0))
+                expect(sheet.modalPresentationStyle).to(equal(.popover))
+            }
+            
+            it("sets up popover presentation controller") {
+                expect(presenter.popover?.delegate).to(be(presenter))
+                expect(presenter.popover?.barButtonItem).to(be(item))
+                expect(presenter.popover?.sourceRect).to(equal(.zero))
+            }
+            
+            it("performs presentation") {
+                expect(vc.presentInvokeCount).to(equal(1))
+                expect(vc.presentInvokeVcs).to(equal([sheet]))
+                expect(vc.presentInvokeAnimateds).to(equal([true]))
+                expect(vc.presentInvokeCompletions.count).to(equal(1))
+            }
+        }
+        
+        
+        describe("refreshing action sheet") {
+            
+            beforeEach {
+                sheet.itemsTableView?.backgroundColor = .red
+                presenter.present(sheet: sheet, in: UIViewController(), from: UIView()) {}
+                presenter.refreshActionSheet()
+            }
+            
+            it("hides unused views") {
+                expect(sheet.buttonsTableView?.isHidden).to(beTrue())
+                expect(sheet.headerViewContainer?.isHidden).to(beTrue())
+            }
+            
+            it("resizes popover") {
+                expect(sheet.preferredContentSize.height).to(equal(150))
+            }
+            
+            it("applies color to popover arrow") {
+                expect(presenter.popover?.backgroundColor).to(equal(.red))
+            }
+        }
+        
+        
+        describe("popover should dismiss") {
+            
+            var popover: UIPopoverPresentationController!
+            var presenting: MockViewController!
+            var dismissEventCount: Int!
+            
+            beforeEach {
+                popover = UIPopoverPresentationController(presentedViewController: UIViewController(), presenting: nil)
+                presenting = MockViewController()
+                sheet.presentingViewController = presenting
+                dismissEventCount = 0
+                presenter.events.didDismissWithBackgroundTap = { dismissEventCount += 1 }
+            }
+            
+            it("aborts and returns false if background tap is disabled") {
+                presenter.isDismissableWithTapOnBackground = false
+                let result = presenter.popoverPresentationControllerShouldDismissPopover(popover)
+                
+                expect(result).to(beFalse())
+                expect(dismissEventCount).to(equal(0))
+                expect(presenting.dismissInvokeCount).to(equal(0))
+            }
+            
+            it("completes and returns false if background tap is enabled") {
+                presenter.isDismissableWithTapOnBackground = true
+                let result = presenter.popoverPresentationControllerShouldDismissPopover(popover)
+                
+                expect(result).to(beFalse())
+                expect(dismissEventCount).to(equal(1))
+                expect(presenting.dismissInvokeCount).to(equal(1))
+            }
+            
+        }
+        
+        
         describe("setting up sheet for popover presentation") {
             
             beforeEach {
                 presenter.setupSheetForPresentation(sheet)
             }
             
-            it("removes header view") {
-                expect(sheet.headerView).to(beNil())
+            it("sets popover style") {
+                expect(sheet.modalPresentationStyle).to(equal(.popover))
             }
             
             it("moves non-cancel buttons last into items group") {

+ 81 - 1
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetStandardPresenterTests.swift

@@ -8,23 +8,103 @@
 
 import Quick
 import Nimble
-import Sheeeeeeeeet
+@testable import Sheeeeeeeeet
 
 class ActionSheetStandardPresenterTests: QuickSpec {
     
     override func spec() {
         
         var presenter: ActionSheetStandardPresenter!
+        var sheet: MockActionSheet!
+        var backgroundView: ActionSheetBackgroundView!
+        var stackView: UIStackView!
         
         beforeEach {
+            backgroundView = ActionSheetBackgroundView(frame: .zero)
+            stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: 0, height: 100))
+            
+            sheet = MockActionSheet(items: []) { _, _ in }
+            sheet.backgroundView = backgroundView
+            sheet.stackView = stackView
+            
             presenter = ActionSheetStandardPresenter()
+            presenter.animationDuration = 0
+            presenter.actionSheet = sheet
         }
         
+        
         describe("background tap dismissal") {
             
             it("is enabled by default") {
                 expect(presenter.isDismissableWithTapOnBackground).to(beTrue())
             }
         }
+        
+        
+        describe("dismissing action sheet") {
+            
+            var counter: Int!
+            
+            beforeEach {
+                counter = 0
+                presenter.dismiss { counter += 1 }
+            }
+            
+            it("calls completion directly") {
+                expect(counter).to(equal(1))
+            }
+            
+            it("removes background view") {
+                expect(backgroundView.alpha).to(equal(0))
+            }
+            
+            it("removes action sheet") {
+                expect(stackView.frame.origin.y).to(equal(200))
+                expect(sheet.view.superview).to(beNil())
+                expect(presenter.actionSheet).toEventually(beNil())
+            }
+        }
+        
+        
+        describe("presenting action sheet") {
+            
+            var vc: MockViewController!
+            var counter: Int!
+            
+            beforeEach {
+                vc = MockViewController()
+                vc.view.frame = CGRect(x: 1, y: 2, width: 3, height: 4)
+                counter = 0
+                presenter.present(sheet: sheet, in: vc) { counter += 1 }
+            }
+            
+            it("sets action sheet") {
+                expect(presenter.actionSheet).to(be(sheet))
+            }
+            
+            it("adds actio sheet to vc view") {
+                expect(sheet.view.frame).to(equal(vc.view.frame))
+                expect(vc.view.subviews).to(contain(sheet.view))
+            }
+            
+            it("adds tap gesture to background view") {
+                expect(sheet.backgroundView?.isUserInteractionEnabled).to(beTrue())
+                //expect(backgroundView.gestureRecognizers?.count).to(equal(1))
+            }
+            
+            it("presents background view") {
+                presenter.animationDuration = -1
+                presenter.present(sheet: sheet, in: vc) {}
+                expect(sheet.backgroundView?.alpha).to(equal(0))
+                presenter.animationDuration = 0
+                presenter.present(sheet: sheet, in: vc) {}
+                expect(sheet.backgroundView?.alpha).to(equal(1))
+            }
+            
+            it("presents stack view") {
+                presenter.present(sheet: sheet, in: vc) {}
+                expect(sheet.stackView?.frame.origin.y).to(equal(277))
+            }
+        }
     }
 }

+ 20 - 1
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Views/MockTableView.swift

@@ -9,7 +9,7 @@
 import UIKit
 import Sheeeeeeeeet
 
-class MockTableView: ActionSheetTableView {
+class MockItemTableView: ActionSheetItemTableView {
 
     var deselectRowInvokeCount = 0
     var deselectRowInvokePaths = [IndexPath]()
@@ -27,3 +27,22 @@ class MockTableView: ActionSheetTableView {
         reloadDataInvokeCount += 1
     }
 }
+
+class MockButtonTableView: ActionSheetButtonTableView {
+    
+    var deselectRowInvokeCount = 0
+    var deselectRowInvokePaths = [IndexPath]()
+    var deselectRowInvokeAnimated = [Bool]()
+    var reloadDataInvokeCount = 0
+    
+    override func deselectRow(at indexPath: IndexPath, animated: Bool) {
+        deselectRowInvokeCount += 1
+        deselectRowInvokePaths.append(indexPath)
+        deselectRowInvokeAnimated.append(animated)
+    }
+    
+    override func reloadData() {
+        super.reloadData()
+        reloadDataInvokeCount += 1
+    }
+}

+ 7 - 0
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -504,6 +504,7 @@ class NCCreateScanDocument : NSObject, ImageScannerControllerDelegate {
         let fileName = CCUtility.createFileName("scan.png", fileDate: Date(), fileType: PHAssetMediaType.image, keyFileName: k_keyFileNameMask, keyFileNameType: k_keyFileNameType, keyFileNameOriginal: k_keyFileNameOriginal)!
         let fileNamePath = CCUtility.getDirectoryScan() + "/" + fileName
         
+        /* V 1.0
         if (results.doesUserPreferEnhancedImage && results.enhancedImage != nil) {
             do {
                 try results.enhancedImage!.pngData()?.write(to: NSURL.fileURL(withPath: fileNamePath), options: .atomic)
@@ -513,6 +514,12 @@ class NCCreateScanDocument : NSObject, ImageScannerControllerDelegate {
                 try results.scannedImage.pngData()?.write(to: NSURL.fileURL(withPath: fileNamePath), options: .atomic)
             } catch { }
         }
+        */
+        
+        // 0.9.1
+        do {
+            try results.scannedImage.pngData()?.write(to: NSURL.fileURL(withPath: fileNamePath), options: .atomic)
+        } catch { }
         
         scanner.dismiss(animated: true, completion: {
             if (self.openScan) {