Bläddra i källkod

Merge remote-tracking branch 'origin/develop'

# Conflicts:
#	Nextcloud.xcodeproj/project.pbxproj
#	iOSClient/AppDelegate.m
#	iOSClient/Brand/File_Provider_Extension.plist
#	iOSClient/Brand/Notification_Service_Extension.plist
#	iOSClient/Brand/Share.plist
#	iOSClient/Brand/iOSClient.plist
#	iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings
#	iOSClient/Supporting Files/de.lproj/Localizable.strings
#	iOSClient/Supporting Files/el.lproj/Localizable.strings
#	iOSClient/Supporting Files/eu.lproj/Localizable.strings
#	iOSClient/Supporting Files/fr.lproj/Localizable.strings
#	iOSClient/Supporting Files/hu.lproj/Localizable.strings
#	iOSClient/Supporting Files/nl.lproj/Localizable.strings
#	iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings
#	iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings
marinofaggiana 6 år sedan
förälder
incheckning
fc41a91298
100 ändrade filer med 2118 tillägg och 2306 borttagningar
  1. 3 2
      Cartfile
  2. 4 3
      Cartfile.resolved
  3. 1 1
      Carthage/Checkouts/Sheeeeeeeeet/.travis.yml
  4. 1 2
      Carthage/Checkouts/Sheeeeeeeeet/Fastlane/Fastfile
  5. 108 7
      Carthage/Checkouts/Sheeeeeeeeet/RELEASE_NOTES.md
  6. 1 1
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet.podspec
  7. 107 135
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.swift
  8. 5 5
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.xib
  9. 12 2
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetItemHandler.swift
  10. 5 6
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheetMargin.swift
  11. 0 151
      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 51
      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 36
      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. 16 3
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectItem.swift
  42. 42 15
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetMultiSelectToggleItem.swift
  43. 86 16
      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 21
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPopoverPresenter.swift
  49. 6 9
      Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Presenters/ActionSheetPresenter.swift
  50. 14 9
      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. 0 35
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/ActionSheet+Items.swift
  57. 0 81
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/CollectionActionSheet.swift
  58. 0 41
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/CustomActionSheet.swift
  59. 0 46
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/DestructiveActionSheet.swift
  60. 0 37
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/HeaderActionSheet.swift
  61. 0 34
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/LinkActionSheet.swift
  62. 0 50
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/MultiSelectActionSheet.swift
  63. 0 41
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/SectionActionSheet.swift
  64. 0 49
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/SingleSelectActionSheet.swift
  65. 0 34
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/StandardActionSheet.swift
  66. 96 64
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/AppDelegate+Appearance.swift
  67. 7 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Cells/MyCollectionViewCell.swift
  68. 7 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Cells/MyCustomViewCell.swift
  69. 6 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Extensions/UIColor+Hex.swift
  70. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Info.plist
  71. 7 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/FoodOption+ActionSheetItems.swift
  72. 6 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/FoodOption.swift
  73. 6 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/TableViewOption.swift
  74. 7 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController+Alerts.swift
  75. 8 0
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController+TableView.swift
  76. 4 4
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController.swift
  77. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheet+PresenterTests.swift
  78. 21 17
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetItemHandlerTests.swift
  79. 14 7
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetMarginTests.swift
  80. 344 318
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetTests.swift
  81. 36 8
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/MockActionSheet.swift
  82. 4 4
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Extensions/NSObject+ClassNameTests.swift
  83. 2 2
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Info.plist
  84. 93 133
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/ActionSheetItemTests.swift
  85. 41 38
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetButtonTests.swift
  86. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetCancelButtonTests.swift
  87. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetDangerButtonTests.swift
  88. 16 20
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Buttons/ActionSheetOkButtonTests.swift
  89. 24 10
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetLinkItemTests.swift
  90. 46 15
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectItemTests.swift
  91. 59 11
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetMultiSelectToggleItemTests.swift
  92. 83 38
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSelectItemTests.swift
  93. 29 63
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Items/ActionSheetSingleSelectItemTests.swift
  94. 2 17
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Mocks/MockActionSheetItem.swift
  95. 15 11
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionMarginTests.swift
  96. 13 14
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetSectionTitleTests.swift
  97. 13 15
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/Titles/ActionSheetTitleTests.swift
  98. 175 6
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetPopoverPresenterTests.swift
  99. 81 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetStandardPresenterTests.swift
  100. 20 1
      Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Views/MockTableView.swift

+ 3 - 2
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"
@@ -10,4 +10,5 @@ github "ChangbaDevs/KTVHTTPCache" ~> 1.1.7
 github "jdg/MBProgressHUD" ~> 1.1.0
 github "realm/realm-cocoa"
 github "SVGKit/SVGKit" "2.x"
-github "WeTransfer/WeScan" >= 0.9
+github "WeTransfer/WeScan" == 0.9.1
+github "malcommac/SwiftRichString"

+ 4 - 3
Cartfile.resolved

@@ -4,12 +4,13 @@ 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.0.2"
+github "danielsaidi/Sheeeeeeeeet" "1.2.2"
 github "dzenbot/DZNEmptyDataSet" "v1.8.1"
 github "ealeksandrov/EAIntroView" "2.12.0"
 github "ealeksandrov/EARestrictedScrollView" "1.1.0"
 github "jdg/MBProgressHUD" "1.1.0"
 github "kishikawakatsumi/UICKeyChainStore" "v2.1.2"
-github "realm/realm-cocoa" "v3.12.0"
+github "malcommac/SwiftRichString" "2.1.0"
+github "realm/realm-cocoa" "v3.13.1"
 github "sgr-ksmt/PDFGenerator" "2.1.1"
-github "tilltue/TLPhotoPicker" "1.7.8"
+github "tilltue/TLPhotoPicker" "1.8.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
 
   

+ 108 - 7
Carthage/Checkouts/Sheeeeeeeeet/RELEASE_NOTES.md

@@ -1,20 +1,121 @@
 # 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
+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**
+
+@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
+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:**
+
+* 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
+integrity of the item and button separation logic.
+
+* The code no longer contains any `didSet` events, since these events called the
+same functionality many times. Call `refresh` if you change any outlets manually
+from now on.
+
+* Since the `didSet` events have been removed, `refreshHeaderVisibility` is only
+called once and has therefore been moved into `refreshHeader`.
+
+* Since the `didSet` events have been removed, `refreshButtonsVisibility` is now
+only called once and has therefore been moved into `refreshButtons`.
+
+* A small delay in `handleTap(on:)`, that should not be needed, has been removed.
+Let me know if it causes any side-effects.
+
+
+
+## 1.0.3
+
+This version removes a debug print that I used to ensure that action sheets were
+properly deinitialized after being dismissed.
+
+
+
 ## 1.0.2
 
-This version adds new background color properties to the action sheet appearance,
-to make it possible to set the background color of the table views.
+This version adds new background color properties to the action sheet appearance
+class. They can be used to set the background color of an entire sheet.
 
 This version fixes a bug, where the background color behind an action sheet went
 black when the action sheet was presented in a split view.
 
 
+
 ## 1.0.1
 
 This version fixes a bug, where the presenters incorrectly updated the scrolling
 behavior of the action sheet when rotating the device.
 
 
+
 ## 1.0.0
 
 Sheeeeeeeeet 1.0.0 is finally here, with many internal changes and some external.
@@ -26,14 +127,14 @@ instead of manual calculations, which means that popover scrolling etc. works by
 how the constraints are setup, instead of relying on manual calculations.
 
 This should result in much more robust action sheets, but it requires testing on
-a wide range of devices and orientations before it can be released as a 1.0.
+a wide range of devices and orientations, so please let me know if there are any
+issues with this approach.
 
 `IMPORTANT` The button item values have changed. Insted of `true` and `nil` they
 now have a strong `ButtonType` value. You can still create custom buttons with a
 custom value, though. You can also use the new `isOkButton` and `isCancelButton`
 extensions to quickly see if a user tapped "OK" or "Cancel".
 
-
 ### Breaking changes
 
 Since the presentation logic has been rewritten from scratch, you have to adjust
@@ -50,7 +151,6 @@ so changing your code to the new standard should be easy.
 * `ActionSheetItem.handleTap(in:)` no longer has a `cell` parameter
 * `ActionSheetStandardPresenter` is renamed to `ActionSheetStandardPresenter`
 
-
 ### New features
 
 * `ActionSheetAppearance` has new properties, which adds new way to style sheets.
@@ -58,7 +158,6 @@ so changing your code to the new standard should be easy.
 to `ActionSheetItem`. They can be used to quickly check if a cancel or ok button
 was tapped, instead of having to check if the item can be cast to a button type. 
 
-
 ### Bug fixes
 
 * The big presentation adjustments solves the scrolling issues that occured with
@@ -66,7 +165,6 @@ popovers and many items.
 * The `hideSeparator()` function is adjusted to behave correctly when the device
 is rotated.
 
-
 ### Deprecated logic
 
 Instead of deprecating presentation-related properties and functions that are no
@@ -334,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.0.2'
+  s.version          = '1.2.2'
   s.summary          = 'Sheeeeeeeeet is a Swift library for custom iOS action sheets.'
 
   s.description      = <<-DESC

+ 107 - 135
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/ActionSheet/ActionSheet.swift

@@ -12,48 +12,60 @@
  use it to create action sheets and present them in any view
  controller, from any source view or bar button item.
  
- To create an action sheet, just call the initializer with a
- list of items and buttons and a block that should be called
- whenever an item is selected.
  
+ ## Creating action sheet instances
  
- ## Items
+ You create instances of this class by providing `init(...)`
+ with the items to present and an action to call whenever an
+ item is selected. If you must have an action sheet instance
+ before you can setup its items (this may happen when you're
+ subclassing), you can setup the items afterwards by calling
+ the `setup(items:)` function.
  
- You provide an action sheet with a collection of items when
- you create it. The sheet will automatically split the items
- into items and buttons. You can also create an action sheet
- with an empty item collection, then call `setup(items:)` at
- a later time. This is sometimes required if you must create
- the action sheet before you can create the items.
  
+ ## Subclassing
  
- ## Presentation
+ This class can be subclassed, which is a good practice when
+ you want to use your own models in a controlled way. If you
+ have a podcast app, you could have a `SleepTimerActionSheet`
+ that automatically sets up its `SleepTimerTime` options and
+ streamlines how you work with a sleep timer.
  
- You can inject a custom presenter if you want to change how
- the sheet is presented and dismissed. The default presenter
- for iPhone devices is `ActionSheetStandardPresenter`, while
- iPad devices get `ActionSheetPopoverPresenter` instead.
  
+ ## Appearance
  
- ## Subclassing
+ 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.
  
- `ActionSheet` can be subclassed, which may be nice whenever
- you want to use your own domain model. For instance, if you
- want to present a list of `Food` items, you should create a
- `FoodActionSheet` sheet, then populate it with `Food` items.
- The selected value will then be of the type `Food`. You can
- either override the initializers or the `setup` function to
- change how you populate the sheet with items.
+ 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.
  
- ## Appearance
  
- Sheeeeeeeeet's action sheet appearance if easily customized.
- To change the global appearance for every sheet in your app,
- just modify `ActionSheetAppearance.standard`. To change the
- appearance of a single action sheet, modify the `appearance`
- property. To change the appearance of a single item, modify
- its `customAppearance` property.
+ ## Presentation
+ 
+ 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) use `ActionSheetPopoverPresenter`.
  
  
  ## Handling item selections
@@ -61,14 +73,14 @@
  The `selectAction` is triggered when a user taps an item in
  the action sheet. It provides you with the action sheet and
  the selected item. It is very important to use `[weak self]`
- in this block to avoid memory leaks.
+ in these action closures, to avoid memory leaks.
  
  
  ## Handling item taps
  
  Action sheets receive a call to `handleTap(on:)` every time
- an item is tapped. You can override it when you create your
- own action sheet subclasses, but you probably shouldn't.
+ an item is tapped. You can override it if you, for instance,
+ want to perform any animations before calling `super`.
  
  */
 
@@ -77,32 +89,40 @@ import UIKit
 open class ActionSheet: UIViewController {
     
     
+    // 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 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
     
     public init(
-        items: [ActionSheetItem],
+        items: [ActionSheetItem] = [],
         presenter: ActionSheetPresenter = ActionSheet.defaultPresenter,
         action: @escaping SelectAction) {
         self.presenter = presenter
         selectAction = action
-        super.init(nibName: ActionSheet.className, bundle: Bundle(for: ActionSheet.self))
+        super.init(nibName: ActionSheet.className, bundle: ActionSheet.bundle)
         setup(items: items)
-        setup()
     }
     
     public required init?(coder aDecoder: NSCoder) {
         presenter = ActionSheet.defaultPresenter
         selectAction = { _, _ in print("itemSelectAction is not set") }
         super.init(coder: aDecoder)
-        setup()
     }
     
-    deinit { print("\(type(of: self)) deinit") }
-    
     
     // MARK: - Setup
     
-    open func setup() {}
+    open func setup() {
+        preferredContentSize.width = preferredPopoverWidth
+    }
     
     open func setup(items: [ActionSheetItem]) {
         self.items = items.filter { !($0 is ActionSheetButton) }
@@ -110,14 +130,16 @@ open class ActionSheet: UIViewController {
         reloadData()
     }
     
-    @available(*, deprecated, message: "setupItemsAndButtons(with:) is deprecated. Use setup(items:) instead")
-    open func setupItemsAndButtons(with items: [ActionSheetItem]) {
-        setup(items: items)
-    }
-    
     
     // MARK: - View Controller Lifecycle
     
+    open override func viewDidLoad() {
+        super.viewDidLoad()
+        setup()
+        setup(itemsTableView, with: itemHandler)
+        setup(buttonsTableView, with: buttonHandler)
+    }
+    
     open override func viewDidLayoutSubviews() {
         super.viewDidLayoutSubviews()
         refresh()
@@ -129,19 +151,29 @@ open class ActionSheet: UIViewController {
     public typealias SelectAction = (ActionSheet, ActionSheetItem) -> ()
     
     
-    // MARK: - Properties
+    // MARK: - Init properties
     
-    open var appearance = ActionSheetAppearance(copy: .standard)
-    
-    public let presenter: ActionSheetPresenter
-
+    public var presenter: ActionSheetPresenter
     public var selectAction: SelectAction
     
-    @available(*, deprecated, message: "itemSelectAction is deprecated. Use selectAction instead")
-    open var itemSelectAction: SelectAction { return selectAction }
     
+    // MARK: - Appearance
+    
+    public var minimumContentInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
+    public var preferredPopoverWidth: CGFloat = 300
+    public var sectionMargins: CGFloat = 15
     
-    // MARK: - Margin Outlets
+    
+    // MARK: - Outlets
+    
+    @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?
@@ -149,64 +181,28 @@ open class ActionSheet: UIViewController {
     @IBOutlet weak var bottomMargin: NSLayoutConstraint?
     
     
-    // MARK: - View Outlets
-    
-    @IBOutlet weak var backgroundView: UIView?
-    @IBOutlet weak var stackView: UIStackView?
-    
-    
     // MARK: - Header Properties
     
-    open var headerView: UIView? {
-        didSet { refresh() }
-    }
-    
-    @IBOutlet weak var headerViewContainer: UIView? {
-        didSet {
-            headerViewContainer?.backgroundColor = .clear
-            refreshHeaderVisibility()
-        }
-    }
-    
-    @IBOutlet weak var headerViewContainerHeight: NSLayoutConstraint! {
-        didSet { refreshHeaderVisibility() }
-    }
+    open var headerView: UIView?
     
     
     // MARK: - Item Properties
     
-    public var items = [ActionSheetItem]()
+    public internal(set) var items = [ActionSheetItem]()
     
     public var itemsHeight: CGFloat { return totalHeight(for: items) }
     
     public lazy var itemHandler = ActionSheetItemHandler(actionSheet: self, itemType: .items)
     
-    @IBOutlet weak var itemsTableView: ActionSheetTableView? {
-        didSet { setup(itemsTableView, with: itemHandler) }
-    }
-    
-    @IBOutlet weak var itemsTableViewHeight: NSLayoutConstraint?
-    
     
     // MARK: - Button Properties
     
-    public var buttons = [ActionSheetButton]()
+    public internal(set) var buttons = [ActionSheetButton]()
     
     public var buttonsHeight: CGFloat { return totalHeight(for: buttons) }
     
     public lazy var buttonHandler = ActionSheetItemHandler(actionSheet: self, itemType: .buttons)
     
-    @IBOutlet weak var buttonsTableView: ActionSheetTableView? {
-        didSet {
-            setup(buttonsTableView, with: buttonHandler)
-            refreshButtonsVisibility()
-        }
-    }
-    
-    @IBOutlet weak var buttonsTableViewHeight: NSLayoutConstraint? {
-        didSet { refreshButtonsVisibility() }
-    }
-    
     
     // MARK: - Presentation Functions
     
@@ -219,52 +215,40 @@ open class ActionSheet: UIViewController {
         presenter.present(sheet: self, in: vc.rootViewController, from: view, completion: completion)
     }
 
-    open func present(in vc: UIViewController, from barButtonItem: UIBarButtonItem, completion: @escaping () -> () = {}) {
+    open func present(in vc: UIViewController, from item: UIBarButtonItem, completion: @escaping () -> () = {}) {
         refresh()
-        presenter.present(sheet: self, in: vc.rootViewController, from: barButtonItem, completion: completion)
+        presenter.present(sheet: self, in: vc.rootViewController, from: item, completion: completion)
     }
 
     
     // MARK: - Refresh Functions
     
     open func refresh() {
-        applyRoundCorners()
+        applyLegacyAppearance()
         refreshHeader()
         refreshItems()
         refreshButtons()
-        stackView?.spacing = appearance.groupMargins
+        stackView?.spacing = sectionMargins
         presenter.refreshActionSheet()
     }
     
     open func refreshHeader() {
-        refreshHeaderVisibility()
         let height = headerView?.frame.height ?? 0
         headerViewContainerHeight?.constant = height
+        headerViewContainer?.isHidden = headerView == nil
         guard let view = headerView else { return }
         headerViewContainer?.addSubviewToFill(view)
     }
     
-    open func refreshHeaderVisibility() {
-        headerViewContainer?.isHidden = headerView == nil
-    }
-    
     open func refreshItems() {
-        items.forEach { $0.applyAppearance(appearance) }
-        itemsTableView?.backgroundColor = appearance.itemsBackgroundColor
-        itemsTableView?.separatorColor = appearance.itemsSeparatorColor
+        items.forEach { $0.applyAppearance(appearance) }    // TODO: Deprecated - Remove in 1.4.0
         itemsTableViewHeight?.constant = itemsHeight
     }
     
     open func refreshButtons() {
-        refreshButtonsVisibility()
-        buttons.forEach { $0.applyAppearance(appearance) }
-        buttonsTableView?.backgroundColor = appearance.buttonsBackgroundColor
-        buttonsTableView?.separatorColor = appearance.buttonsSeparatorColor
-        buttonsTableViewHeight?.constant = buttonsHeight
-    }
-    
-    open func refreshButtonsVisibility() {
         buttonsTableView?.isHidden = buttons.count == 0
+        buttons.forEach { $0.applyAppearance(appearance) }  // TODO: Deprecated - Remove in 1.4.0
+        buttonsTableViewHeight?.constant = buttonsHeight
     }
     
     
@@ -272,15 +256,18 @@ open class ActionSheet: UIViewController {
     
     open func handleTap(on item: ActionSheetItem) {
         reloadData()
-        guard item.tapBehavior == .dismiss else { return selectAction(self, item) }
-        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
-            self.dismiss { self.selectAction(self, item) }
-        }
+        if item.tapBehavior != .dismiss { return selectAction(self, item) }
+        self.dismiss { self.selectAction(self, item) }
     }
     
     open func margin(at margin: ActionSheetMargin) -> CGFloat {
-        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() {
@@ -294,32 +281,17 @@ 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
         tableView?.alwaysBounceVertical = false
-        setupAppearance(for: tableView)
-    }
-    
-    func setupAppearance(for tableView: UITableView?) {
         tableView?.estimatedRowHeight = 44
         tableView?.rowHeight = UITableView.automaticDimension
         tableView?.cellLayoutMarginsFollowReadableWidth = false
     }
     
     func totalHeight(for items: [ActionSheetItem]) -> CGFloat {
-        return items.reduce(0) { $0 + $1.appearance.height }
+        let height = items.reduce(0) { $0 + $1.height }
+        return height
     }
 }

+ 5 - 5
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" red="0.45104343879999997" green="1" blue="0.29922929650000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <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>

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

@@ -6,6 +6,13 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ This class is used as data source and delegate for the item
+ and button table views of the action sheet class.
+ 
+ */
+
 import UIKit
 
 open class ActionSheetItemHandler: NSObject {
@@ -61,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 - 151
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Appearance/ActionSheetAppearance.swift

@@ -1,151 +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
-        itemsBackgroundColor = copy.itemsBackgroundColor ?? backgroundColor
-        buttonsSeparatorColor = copy.buttonsSeparatorColor ?? backgroundColor
-        
-        separatorColor = copy.separatorColor
-        itemsSeparatorColor = copy.itemsSeparatorColor ?? separatorColor
-        buttonsSeparatorColor = copy.buttonsSeparatorColor ?? separatorColor
-        
-        item = ActionSheetItemAppearance(copy: copy.item)
-        popover = ActionSheetPopoverAppearance(copy: copy.popover)
-        
-        cancelButton = ActionSheetCancelButtonAppearance(copy: copy.cancelButton)
-        dangerButton = ActionSheetDangerButtonAppearance(copy: copy.dangerButton)
-        okButton = ActionSheetOkButtonAppearance(copy: copy.okButton)
-        
-        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)
-        
-        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 item: ActionSheetItemAppearance = {
-        return ActionSheetItemAppearance()
-    }()
-    
-    public lazy var popover: ActionSheetPopoverAppearance = {
-        return ActionSheetPopoverAppearance(width: 300)
-    }()
-    
-    
-    // MARK: - Buttons
-    
-    public lazy var cancelButton: ActionSheetCancelButtonAppearance = {
-        return ActionSheetCancelButtonAppearance(copy: item)
-    }()
-    
-    public lazy var dangerButton: ActionSheetDangerButtonAppearance = {
-        return ActionSheetDangerButtonAppearance(copy: item)
-    }()
-    
-    public lazy var okButton: ActionSheetOkButtonAppearance = {
-        return ActionSheetOkButtonAppearance(copy: item)
-    }()
-    
-    
-    // MARK: - Items
-    
-    public lazy var collectionItem: ActionSheetCollectionItemAppearance = {
-        return ActionSheetCollectionItemAppearance(copy: item)
-    }()
-    
-    public lazy var customItem: ActionSheetCustomItemAppearance = {
-        return ActionSheetCustomItemAppearance(copy: item)
-    }()
-    
-    public lazy var linkItem: ActionSheetLinkItemAppearance = {
-        return ActionSheetLinkItemAppearance(copy: item)
-    }()
-    
-    public lazy var multiSelectItem: ActionSheetMultiSelectItemAppearance = {
-        return ActionSheetMultiSelectItemAppearance(copy: selectItem)
-    }()
-    
-    public lazy var multiSelectToggleItem: ActionSheetMultiSelectToggleItemAppearance = {
-        return ActionSheetMultiSelectToggleItemAppearance(copy: item)
-    }()
-    
-    public lazy var selectItem: ActionSheetSelectItemAppearance = {
-        return ActionSheetSelectItemAppearance(copy: item)
-    }()
-    
-    public lazy var singleSelectItem: ActionSheetSingleSelectItemAppearance = {
-        return ActionSheetSingleSelectItemAppearance(copy: selectItem)
-    }()
-    
-    
-    // MARK: - Titles
-    
-    public lazy var sectionMargin: ActionSheetSectionMarginAppearance = {
-        return ActionSheetSectionMarginAppearance(copy: item)
-    }()
-    
-    public lazy var sectionTitle: ActionSheetSectionTitleAppearance = {
-        return ActionSheetSectionTitleAppearance(copy: item)
-    }()
-    
-    public lazy var title: ActionSheetTitleAppearance = {
-        return 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)
-        guard let copy = copy as? ActionSheetLinkItemAppearance else { return }
-        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)
-        guard let copy = copy as? ActionSheetMultiSelectToggleItemAppearance else { return }
-        deselectAllTextColor = copy.deselectAllTextColor
-        selectAllTextColor = copy.selectAllTextColor
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var deselectAllTextColor: UIColor?
-    public var selectAllTextColor: UIColor?
-}

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

@@ -1,51 +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
-        guard let copy = copy as? ActionSheetSelectItemAppearance else { return }
-        selectedIcon = copy.selectedIcon
-        selectedTextColor = copy.selectedTextColor ?? selectedTextColor
-        selectedTintColor = copy.selectedTintColor ?? selectedTintColor
-        selectedIconTintColor = copy.selectedIconTintColor ?? selectedTintColor
-    }
-    
-    
-    // MARK: - Properties
-    
-    public var selectedIcon: UIImage?
-    public var selectedIconTintColor: UIColor?
-    public var selectedTextColor: UIColor?
-    public var selectedTintColor: UIColor?
-}

+ 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: Bundle(for: self))
+        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.0.2</string>
+	<string>1.2.2</string>
 	<key>CFBundleVersion</key>
-	<string>1811242240</string>
+	<string>1</string>
 	<key>NSPrincipalClass</key>
 	<string></string>
 </dict>

+ 116 - 36
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,13 +84,12 @@ 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()
     }
     
@@ -58,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
@@ -66,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)
     }
 }

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

@@ -28,16 +28,27 @@ 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(
         title: String,
+        subtitle: String? = nil,
         isSelected: Bool,
         group: String = "",
         value: Any? = nil,
         image: UIImage? = nil) {
         super.init(
             title: title,
+            subtitle: subtitle,
             isSelected: isSelected,
             group: group,
             value: value,
@@ -48,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) {
@@ -60,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()
+    }
+}

+ 86 - 16
Carthage/Checkouts/Sheeeeeeeeet/Sheeeeeeeeet/Items/Items/ActionSheetSelectItem.swift

@@ -29,10 +29,20 @@ 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(
         title: String,
+        subtitle: String? = nil,
         isSelected: Bool,
         group: String = "",
         value: Any? = nil,
@@ -42,6 +52,7 @@ open class ActionSheetSelectItem: ActionSheetItem {
         self.group = group
         super.init(
             title: title,
+            subtitle: subtitle,
             value: value,
             image: image,
             tapBehavior: tapBehavior)
@@ -51,32 +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 }
-        cell.accessoryView = isSelected ? UIImageView(image: appearance.selectedIcon) : nil
-        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 - 21
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
@@ -23,18 +23,13 @@ import UIKit
 open class ActionSheetPopoverPresenter: NSObject, ActionSheetPresenter {
     
     
-    // MARK: - Initialization
-    
-    deinit { print("\(type(of: self)) deinit") }
-    
-    
     // MARK: - Properties
     
     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
@@ -64,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
     }
 }
@@ -97,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
@@ -105,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 - 9
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.
  
  */
 
@@ -22,15 +23,15 @@ open class ActionSheetStandardPresenter: ActionSheetPresenter {
     
     public init() {}
     
-    deinit { print("\(type(of: self)) deinit") }
-    
     
     // MARK: - Properties
     
     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
@@ -55,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)
     }
@@ -73,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?) {
@@ -88,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 () -> ()) {
@@ -108,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 {}

+ 0 - 35
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/ActionSheet+Items.swift

@@ -1,35 +0,0 @@
-//
-//  ActionSheet+Items.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-/*
- 
- These extensions provides action sheets with functions that
- are shared by all example action sheets.
- 
- */
-
-import Sheeeeeeeeet
-
-extension ActionSheet {
-    
-    static var cancelButton: ActionSheetCancelButton {
-        return ActionSheetCancelButton(title: "Cancel")
-    }
-    
-    static var okButton: ActionSheetOkButton {
-        return ActionSheetOkButton(title: "OK")
-    }
-    
-    static var standardTitle: String {
-        return "What do you want to eat?"
-    }
-    
-    static func titleItem(title: String) -> ActionSheetTitle {
-        return ActionSheetTitle(title: title)
-    }
-}

+ 0 - 81
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/CollectionActionSheet.swift

@@ -1,81 +0,0 @@
-//
-//  CollectionActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-/*
- 
- This action sheet calls `setupItemsAndButtons` after it has
- been initialized, since taps in the collection view have to
- reload the action sheet to update selection display.
- 
- */
-
-import Sheeeeeeeeet
-
-class CollectionActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([MyCollectionViewCell.Item]) -> ()) {
-        let collectionItems = CollectionActionSheet.collectionItems
-        super.init(items: []) { _, item in
-            guard item.isOkButton else { return }
-            action(collectionItems.filter { $0.isSelected })
-        }
-        let items = self.items(for: options, collectionItems: collectionItems)
-        setup(items: items)
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension CollectionActionSheet {
-    
-    static var collectionItems: [MyCollectionViewCell.Item] {
-        var items: [MyCollectionViewCell.Item] = []
-        for i in 0...20 {
-            items.append(MyCollectionViewCell.Item(title: "\(i)", subtitle: "\(i)"))
-        }
-        return items
-    }
-    
-    func items(for options: [FoodOption], collectionItems: [MyCollectionViewCell.Item]) -> [ActionSheetItem] {
-        let title = ActionSheetSectionTitle(title: ActionSheet.standardTitle, subtitle: selectionSubtitle(for: collectionItems))
-        
-        let setupAction = { (cell: MyCollectionViewCell, index: Int) in
-            let item = collectionItems[index]
-            cell.configureWith(item: item)
-        }
-        
-        let selectionAction = { [weak self] (cell: MyCollectionViewCell, index: Int) in
-            let item = collectionItems[index]
-            item.isSelected = !item.isSelected
-            title.subtitle = self?.selectionSubtitle(for: collectionItems)
-            cell.configureWith(item: item)
-            self?.reloadData()
-        }
-        
-        let collectionItem = ActionSheetCollectionItem(
-            itemCellType: MyCollectionViewCell.self,
-            itemCount: collectionItems.count,
-            setupAction: setupAction,
-            selectionAction: selectionAction
-        )
-        
-        return [
-            ActionSheetSectionMargin(),
-            title,
-            ActionSheetSectionMargin(),
-            collectionItem,
-            ActionSheet.okButton,
-            ActionSheet.cancelButton]
-    }
-    
-    func selectionSubtitle(for collectionItems: [MyCollectionViewCell.Item]) -> String {
-        return "Selected items: \(collectionItems.filter { $0.isSelected }.count)"
-    }
-}

+ 0 - 41
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/CustomActionSheet.swift

@@ -1,41 +0,0 @@
-//
-//  CustomActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Daniel Saidi on 2018-10-08.
-//  Copyright © 2018 Daniel Saidi. All rights reserved.
-//
-
-import UIKit
-import Sheeeeeeeeet
-
-class CustomActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], buttonTapAction: @escaping (UIButton) -> ()) {
-        let items = CustomActionSheet.items(for: options, buttonTapAction: buttonTapAction)
-        super.init(items: items) { _, _ in }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-
-// MARK: - Private Functions
-
-private extension CustomActionSheet {
-    
-    static func items(for options: [FoodOption], buttonTapAction: @escaping (UIButton) -> ()) -> [ActionSheetItem] {
-        let customType = MyCustomViewCell.self
-        let customItem = ActionSheetCustomItem(cellType: customType) { cell in
-            cell.buttonTapAction = buttonTapAction
-        }
-        
-        return [
-            ActionSheetTitle(title: "Tap a button"),
-            customItem,
-            cancelButton
-        ]
-    }
-}

+ 0 - 46
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/DestructiveActionSheet.swift

@@ -1,46 +0,0 @@
-//
-//  DestructiveActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-/*
- 
- These extensions provides action sheets with functions that
- are shared by all example action sheets.
- 
- */
-
-import Sheeeeeeeeet
-
-class DestructiveActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = DestructiveActionSheet.items(for: options)
-        super.init(items: items) { sheet, item in
-            guard item.isOkButton else { return }
-            let items = sheet.items.compactMap { $0 as? ActionSheetSelectItem }
-            action(items.filter { $0.isSelected })
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension DestructiveActionSheet {
-    
-    static func items(for options: [FoodOption]) -> [ActionSheetItem] {
-        let titleItem = ActionSheetTitle(title: "Remove Payment Options")
-        let image = UIImage(named: "ic_credit_card")
-        let visaTitle = "Visa **** **** **** 4321"
-        let visa = ActionSheetMultiSelectItem(title: visaTitle, isSelected: false, value: "visa", image: image)
-        let masterTitle = "MasterCard **** **** **** 9876"
-        let master = ActionSheetMultiSelectItem(title: masterTitle, isSelected: false, value: "master", image: image)
-        let removeButton = ActionSheetDangerButton(title: "Remove")
-        return [titleItem, visa, master, cancelButton, removeButton]
-    }
-}

+ 0 - 37
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/HeaderActionSheet.swift

@@ -1,37 +0,0 @@
-//
-//  HeaderActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class HeaderActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = HeaderActionSheet.items(for: options)
-        super.init(items: items) { _, item in
-            if item.value == nil { return }
-            action([item])
-        }
-        let image = UIImage(named: "title-image")
-        headerView = UIImageView(image: image)
-        headerView?.frame.size.height = 150
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension HeaderActionSheet {
-    
-    static func items(for options: [FoodOption]) -> [ActionSheetItem] {
-        var items = options.map { $0.item() }
-        items.insert(titleItem(title: standardTitle), at: 0)
-        items.append(cancelButton)
-        return items
-    }
-}

+ 0 - 34
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/LinkActionSheet.swift

@@ -1,34 +0,0 @@
-//
-//  LinkActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class LinkActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = LinkActionSheet.items(for: options)
-        super.init(items: items) { _, item in
-            if item.value == nil { return }
-            action([item])
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension LinkActionSheet {
-    
-    static func items(for options: [FoodOption]) -> [ActionSheetItem] {
-        var items = options.map { $0.linkItem() }
-        items.insert(titleItem(title: standardTitle), at: 0)
-        items.append(cancelButton)
-        return items
-    }
-}

+ 0 - 50
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/MultiSelectActionSheet.swift

@@ -1,50 +0,0 @@
-//
-//  MultiSelectActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class MultiSelectActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], preselected: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = MultiSelectActionSheet.items(for: options, preselected: preselected)
-        super.init(items: items) { sheet, item in
-            guard item.isOkButton else { return }
-            let selectItems = sheet.items.compactMap { $0 as? ActionSheetSelectItem }
-            let selectedItems = selectItems.filter { $0.isSelected }
-            action(selectedItems)
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension MultiSelectActionSheet {
-    
-    static func items(for options: [FoodOption], preselected: [FoodOption]) -> [ActionSheetItem] {
-        var items = [ActionSheetItem]()
-        items.append(titleItem(title: standardTitle))
-        items.append(contentsOf: itemsGroup(for: options, preselected: .fast, group: "Appetizer"))
-        items.append(ActionSheetSectionMargin())
-        items.append(contentsOf: itemsGroup(for: options, preselected: .homeMade, group: "Main Dish"))
-        items.append(okButton)
-        items.append(cancelButton)
-        return items
-    }
-    
-    static func itemsGroup(for options: [FoodOption], preselected: FoodOption?, group: String) -> [ActionSheetItem] {
-        var items = [ActionSheetItem]()
-        let options = options.filter { $0 != .none && $0 != .fancy }
-        let foodItems = options.map { $0.multiSelectItem(isSelected: $0 == preselected, group: group) }
-        let toggler = ActionSheetMultiSelectToggleItem(title: group, state: .selectAll, group: group, selectAllTitle: "Select all", deselectAllTitle: "Deselect all")
-        items.append(toggler)
-        items.append(contentsOf: foodItems)
-        return items
-    }
-}

+ 0 - 41
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/SectionActionSheet.swift

@@ -1,41 +0,0 @@
-//
-//  SectionActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class SectionActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = SectionActionSheet.items(for: options)
-        super.init(items: items) { _, item in
-            if item.value == nil { return }
-            action([item])
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension SectionActionSheet {
-    
-    static func items(for options: [FoodOption]) -> [ActionSheetItem] {
-        var items = [ActionSheetItem]()
-        items.append(titleItem(title: standardTitle))
-        items.append(ActionSheetSectionTitle(title: "Cheap"))
-        let cheap = options.filter { $0.isCheap }.map { $0.item() }
-        cheap.forEach { items.append($0) }
-        items.append(ActionSheetSectionMargin())
-        items.append(ActionSheetSectionTitle(title: "Expensive"))
-        let expensive = options.filter { !$0.isCheap }.map { $0.item() }
-        expensive.forEach { items.append($0) }
-        items.append(cancelButton)
-        return items
-    }
-}

+ 0 - 49
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/SingleSelectActionSheet.swift

@@ -1,49 +0,0 @@
-//
-//  MultiSelectActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class SingleSelectActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], preselected: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = SingleSelectActionSheet.items(for: options, preselected: preselected)
-        super.init(items: items) { sheet, item in
-            guard item.isOkButton else { return }
-            let selectItems = sheet.items.compactMap { $0 as? ActionSheetSelectItem }
-            let selectedItems = selectItems.filter { $0.isSelected }
-            action(selectedItems)
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension SingleSelectActionSheet {
-    
-    static func items(for options: [FoodOption], preselected: [FoodOption]) -> [ActionSheetItem] {
-        var items = [ActionSheetItem]()
-        items.append(titleItem(title: standardTitle))
-        items.append(contentsOf: itemsGroup(for: options, preselected: .fast, group: "Appetizer"))
-        items.append(ActionSheetSectionMargin())
-        items.append(contentsOf: itemsGroup(for: options, preselected: .homeMade, group: "Main Dish"))
-        items.append(okButton)
-        items.append(cancelButton)
-        return items
-    }
-    
-    static func itemsGroup(for options: [FoodOption], preselected: FoodOption?, group: String) -> [ActionSheetItem] {
-        var items = [ActionSheetItem]()
-        let options = options.filter { $0 != .none && $0 != .fancy }
-        let foodItems = options.map { $0.singleSelectItem(isSelected: $0 == preselected, group: group) }
-        items.append(ActionSheetSectionTitle(title: group))
-        items.append(contentsOf: foodItems)
-        return items
-    }
-}

+ 0 - 34
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ActionSheets/StandardActionSheet.swift

@@ -1,34 +0,0 @@
-//
-//  StandardActionSheet.swift
-//  SheeeeeeeeetExample
-//
-//  Created by Jonas Ullström on 2018-03-16.
-//  Copyright © 2018 Jonas Ullström. All rights reserved.
-//
-
-import Sheeeeeeeeet
-
-class StandardActionSheet: ActionSheet {
-    
-    init(options: [FoodOption], action: @escaping ([ActionSheetItem]) -> ()) {
-        let items = StandardActionSheet.items(for: options)
-        super.init(items: items) { _, item in
-            if item.value == nil { return }
-            action([item])
-        }
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-}
-
-private extension StandardActionSheet {
-    
-    static func items(for options: [FoodOption]) -> [ActionSheetItem] {
-        var items = options.map { $0.item() }
-        items.insert(titleItem(title: standardTitle), at: 0)
-        items.append(cancelButton)
-        return items
-    }
-}

+ 96 - 64
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/AppDelegate+Appearance.swift

@@ -6,75 +6,107 @@
 //  Copyright © 2018 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ This extension isolates how the example app applies colors,
+ fonts etc to the example action sheets.
+ 
+ */
+
 import UIKit
 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.selectedTintColor = blue
-        appearance.selectItem.selectedTextColor = green
-        appearance.selectItem.selectedIconTintColor = purple
-        
-        appearance.singleSelectItem.selectedIcon = UIImage(named: "ic_checkmark")
-        appearance.singleSelectItem.selectedTintColor = green
-        appearance.singleSelectItem.selectedTextColor = purple
-        appearance.singleSelectItem.selectedIconTintColor = blue
-        
-        appearance.multiSelectItem.selectedIcon = UIImage(named: "ic_checkmark")
-        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
     }
 }

+ 7 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Cells/MyCollectionViewCell.swift

@@ -6,6 +6,13 @@
 //  Copyright © 2018 Jonas Ullström. All rights reserved.
 //
 
+/*
+ 
+ This cell is used by the example app, to populate the cells
+ of the `CollectionActionSheet` example action sheet.
+ 
+ */
+
 import UIKit
 import Sheeeeeeeeet
 

+ 7 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Cells/MyCustomViewCell.swift

@@ -6,6 +6,13 @@
 //  Copyright © 2018 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ This cell is used by the example app, as the custom view in
+ the `CustomActionSheet` example action sheet.
+ 
+ */
+
 import Sheeeeeeeeet
 
 class MyCustomViewCell: ActionSheetItemCell, ActionSheetCustomItemCell {

+ 6 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Extensions/UIColor+Hex.swift

@@ -6,6 +6,12 @@
 //  Copyright © 2016 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ This extension is used to create colors in the example app.
+ 
+ */
+
 import UIKit
 
 

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

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

+ 7 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/FoodOption+ActionSheetItems.swift

@@ -6,6 +6,13 @@
 //  Copyright © 2018 Jonas Ullström. All rights reserved.
 //
 
+/*
+ 
+ This file contains food option extensions, that can be used
+ to create action sheet items in the example app.
+ 
+ */
+
 import Sheeeeeeeeet
 
 extension FoodOption {

+ 6 - 1
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/FoodOption.swift

@@ -6,7 +6,12 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
-// This enum is only used to populate our example sheets.
+/*
+ 
+ This enum is used to create food action sheet options, that
+ are presented in the example action sheets.
+ 
+ */
 
 import Sheeeeeeeeet
 

+ 6 - 1
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/Options/TableViewOption.swift

@@ -6,7 +6,12 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
-// This enum is only used to populate our example table view.
+/*
+ 
+ This enum is used by the example app, to populate the table
+ view in the main example view controller.
+ 
+ */
 
 import UIKit
 

+ 7 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController+Alerts.swift

@@ -6,6 +6,13 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ These extensions are only used by the example app, to alert
+ which options a user selects in the example action sheets.
+ 
+ */
+
 import UIKit
 import Sheeeeeeeeet
 

+ 8 - 0
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController+TableView.swift

@@ -6,9 +6,17 @@
 //  Copyright © 2017 Daniel Saidi. All rights reserved.
 //
 
+/*
+ 
+ These extensions are only used by the example app, to setup
+ the main view controller's table view handling.
+ 
+ */
+
 import UIKit
 import Sheeeeeeeeet
 
+
 // MARK: - UITableViewDataSource
 
 extension ViewController: UITableViewDataSource {

+ 4 - 4
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetExample/ViewController/ViewController.swift

@@ -9,11 +9,11 @@
 /*
  
  To make the example easier to overview, the view controller
- has been split up into extension files.
+ has been split up into multiple files.
  
- Action sheet appearance is setup by `AppDelegate` using the
- `DemoAppearance` class. You can play around with it to view
- how sheets and items are affected by appearance changes.
+ The action sheet appearance is setup by `AppDelegate`, with
+ the `AppDelegate+Appearance` extension. You can play around
+ with it to see how it affects the example sheets and items.
  
  */
 

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

+ 344 - 318
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/ActionSheetTests.swift

@@ -14,147 +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 })
-                
-                expect(sheet.presenter).to(be(presenter))
+        }
+        
+        
+        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("applies provided action") {
-                var counter = 0
-                let sheet = MockActionSheet(items: []) { _, _  in counter += 1 }
-                sheet.selectAction(sheet, createItem("foo"))
-                
-                expect(counter).to(equal(1))
+            
+            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())
             }
         }
         
         
-        // MARK: - Properties
-
-        describe("appearance") {
+        describe("laying out subviews") {
             
-            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("refreshes sheet") {
+                sheet = createSheet()
+                sheet.viewDidLayoutSubviews()
                 
-                expect(appearance.popover.width).to(equal(-1))
+                expect(sheet.refreshInvokeCount).to(equal(1))
             }
         }
         
         
-        // MARK: - Header Properties
-        
-        describe("header view") {
+        describe("minimum content insets") {
             
-            it("refreshes the sheet when set") {
-                let sheet = createSheet()
-                expect(sheet.refreshInvokeCount).to(equal(0))
-                sheet.headerView = UIView()
+            it("has correct default value") {
+                sheet = createSheet()
+                let expected = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
                 
-                expect(sheet.refreshInvokeCount).to(equal(1))
+                expect(sheet.minimumContentInsets).to(equal(expected))
             }
         }
         
-        describe("header view container") {
+        
+        describe("preferred popover width") {
             
-            it("gets clear background color when set") {
-                let sheet = createSheet()
-                let view = UIView()
-                view.backgroundColor = .red
-                sheet.headerViewContainer = view
+            it("has correct default value") {
+                sheet = createSheet()
+                let expected: CGFloat = 300
                 
-                expect(view.backgroundColor).to(equal(.clear))
+                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])
+            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(210))
+                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))
@@ -162,91 +252,64 @@ class ActionSheetTests: QuickSpec {
             }
         }
         
-        describe("item table view") {
+        
+        describe("items height") {
             
-            it("is correctly setup when set") {
-                let sheet = createSheet()
-                let view = createTableView()
-                sheet.itemsTableView = view
-                
-                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])
+            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(230))
+                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))
-            }
-        }
-        
-        describe("button table view") {
-            
-            it("is correctly setup when set") {
-                let sheet = createSheet()
-                let view = createTableView()
-                sheet.buttonsTableView = view
-                
-                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())
+                expect(sheet.buttonHandler.items[0]).to(be(button1))
+                expect(sheet.buttonHandler.items[1]).to(be(button2))
             }
         }
         
         
-        // 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]()
                     
@@ -258,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)
@@ -283,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)
@@ -307,200 +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.refreshButtonsVisibilityInvokeCount = 0
-                sheet.refreshHeaderVisibilityInvokeCount = 0
+                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.refreshHeaderVisibilityInvokeCount).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.refreshHeaderVisibility()
-                    
-                    expect(headerViewContainer.isHidden).to(beTrue())
-                }
-                
-                it("shows header container if header view is nil") {
-                    sheet.headerView = UIView(frame: .zero)
-                    sheet.refreshHeaderVisibility()
-                    
-                    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))
+            }
+            
+            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("buttons") {
+            it("refreshes correctly if header view is nil") {
+                sheet.refreshHeader()
                 
-                it("refreshes buttons visibility") {
-                    sheet.refresh()
-                    
-                    expect(sheet.refreshButtonsVisibilityInvokeCount).to(equal(1))
-                }
+                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()
                 
-                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))
-                }
+                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("applies background color") {
-                    sheet.appearance.buttonsBackgroundColor = .yellow
-                    let view = createTableView()
-                    sheet.buttonsTableView = view
-                    sheet.refresh()
-                    
-                    expect(view.backgroundColor).to(equal(.yellow))
-                }
+                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()
                 
-                it("applies separator color") {
-                    sheet.appearance.buttonsSeparatorColor = .yellow
-                    let view = createTableView()
-                    sheet.buttonsTableView = view
-                    sheet.refresh()
-                    
-                    expect(view.separatorColor).to(equal(.yellow))
-                }
+                expect(height.constant).to(equal(24))
+            }
+        }
+        
+        
+        describe("refreshing buttons") {
+            
+            var height: NSLayoutConstraint!
+            
+            beforeEach {
+                height = NSLayoutConstraint()
+                sheet = createSheet()
+                sheet.buttonsTableViewHeight = height
+                ActionSheetItem.height = 12
+                ActionSheetOkButton.height = 13
             }
             
-            context("button visibility") {
+            it("refreshes correctly if no items are set") {
+                sheet.refreshButtons()
                 
-                it("hides buttons if sheet has no buttons") {
-                    sheet.refreshButtonsVisibility()
-                    
-                    expect(buttonsView.isHidden).to(beTrue())
-                }
+                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()
                 
-                it("shows buttons if sheet has buttons") {
-                    sheet.setup(items: [MockActionSheetButton(title: "foo", value: true)])
-                    sheet.refreshButtonsVisibility()
-                    
-                    expect(buttonsView.isHidden).to(beFalse())
-                }
+                expect(height.constant).to(equal(26))
             }
         }
         
         
-        // MARK: - Protected Functions
-        
         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))
@@ -509,35 +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()

+ 36 - 8
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/ActionSheet/MockActionSheet.swift

@@ -7,9 +7,21 @@ class MockActionSheet: ActionSheet {
     var handleTapInvokeItems = [ActionSheetItem]()
     var prepareForPresentationInvokeCount = 0
     var refreshInvokeCount = 0
-    var refreshButtonsVisibilityInvokeCount = 0
-    var refreshHeaderVisibilityInvokeCount = 0
+    var refreshButtonsInvokeCount = 0
+    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() }
@@ -27,18 +39,34 @@ class MockActionSheet: ActionSheet {
         refreshInvokeCount += 1
     }
     
-    override func refreshButtonsVisibility() {
-        super.refreshButtonsVisibility()
-        refreshButtonsVisibilityInvokeCount += 1
+    override func refreshButtons() {
+        super.refreshButtons()
+        refreshButtonsInvokeCount += 1
+    }
+    
+    override func refreshItems() {
+        super.refreshItems()
+        refreshItemsInvokeCount += 1
     }
     
-    override func refreshHeaderVisibility() {
-        super.refreshHeaderVisibility()
-        refreshHeaderVisibilityInvokeCount += 1
+    override func refreshHeader() {
+        super.refreshHeader()
+        refreshHeaderInvokeCount += 1
     }
     
     override func reloadData() {
         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.0.2</string>
+	<string>1.2.2</string>
 	<key>CFBundleVersion</key>
-	<string>1811242240</string>
+	<string>1901161647</string>
 </dict>
 </plist>

+ 93 - 133
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Items/ActionSheetItemTests.swift

@@ -8,189 +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() -> MockActionSheetItem {
-            return MockActionSheetItem(title: "foo", subtitle: "bar", value: true, image: UIImage())
-            
+        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") {
-            
-            it("is default") {
-                let item = createItem()
-                expect(item.cellStyle).to(equal(.default))
-            }
-        }
         
-        describe("custom appearance") {
+        describe("height") {
             
-            it("is nil by default") {
-                let item = createItem()
-                expect(item.customAppearance).to(beNil())
+            let preset = CustomItem.height
+            
+            afterEach {
+                CustomItem.height = preset
             }
-        }
-        
-        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())
+            it("uses standard height if no custom value is registered") {
+                expect(ActionSheetItem.height).to(equal(50))
             }
             
-            it("applies custom appearance if set") {
-                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))
+            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 to cell") {
+        
+        describe("resolving cell") {
             
-            it("applies correct style") {
+            it("returns correct cell") {
                 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())
+                let cell = item.cell(for: UITableView(frame: .zero))
+                
+                expect(cell.reuseIdentifier).to(equal(item.cellReuseIdentifier))
             }
         }
+    }
+}
+
+
+class ActionSheetItemCellTests: QuickSpec {
+    
+    override func spec() {
+        
+        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 - 6
Carthage/Checkouts/Sheeeeeeeeet/SheeeeeeeeetTests/Presenters/ActionSheetPopoverPresenterTests.swift

@@ -6,8 +6,6 @@
 //  Copyright © 2018 Daniel Saidi. All rights reserved.
 //
 
-//  TODO: Write more tests
-
 import Quick
 import Nimble
 @testable import Sheeeeeeeeet
@@ -17,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] = [
@@ -27,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") {
@@ -40,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
+    }
+}

Vissa filer visades inte eftersom för många filer har ändrats