Compare commits

...

359 Commits

Author SHA1 Message Date
Ivan Smolin fec9537745 up swift version to 5.7 2024-01-30 23:09:39 +03:00
Ivan Smolin 246c0d06c0 Merge pull request 'remove outdated actions and swift tools version' (#2) from feature/remote_outdated_code into master
Reviewed-on: #2
2024-01-30 22:27:14 +03:00
Ivan Smolin 54bf141aff remove outdated actions and swift tools version 2024-01-30 22:24:27 +03:00
Ivan Smolin 01134b83b4 update source url 2024-01-30 22:13:02 +03:00
Ivan Smolin 3fefa09c3a Merge pull request 'fix warnings' (#1) from fix/xcode_15 into master
Reviewed-on: #1
2024-01-30 22:08:00 +03:00
Ivan Smolin 4886e43d20 fix warnings 2024-01-30 22:01:31 +03:00
Loupehope 41826e18db
Merge pull request #6 from TouchInstinct/feature/actionRows
Replace UITableViewRowAction with UIContextualAction
2020-12-26 15:37:30 +03:00
Vlad caec2dd10e Return deprecated 2020-12-26 15:22:00 +03:00
Vlad efc03d7c22 Add performsFirstActionWithFullSwipe 2020-12-26 11:24:59 +03:00
Vlad 94c40faa63 Code correction 2020-12-26 11:05:20 +03:00
Vlad d87a23587e Add backward capability to editingActions 2020-12-26 10:52:06 +03:00
Vlad 49b3f868f3 Return IPHONEOS_DEPLOYMENT_TARGET 2020-12-26 10:36:32 +03:00
Vlad 37482d3b69 Replace UITableViewRowAction with UIContextualAction 2020-12-26 10:31:38 +03:00
Loupehope 2cc161f0c0
Merge pull request #5 from maxsokolov/master
Merge source master
2020-12-26 10:16:07 +03:00
Max Sokolov 8bf4840d9d bump to 2.11.0 2020-05-04 08:11:57 -04:00
Max Sokolov 3b266fb7c7
Merge pull request #98 from mrtokii/feature/ios13_actions
Add support for iOS 13 actions
2020-05-04 08:06:36 -04:00
mrtokii eb93fe253c Add multiple selection actions & context menu action 2020-05-03 16:25:30 +03:00
Max Sokolov 44c55d2f05
Merge pull request #96 from DmitryFrishbuter/readme-patch
Fix typo in README.md
2019-10-05 15:10:11 -04:00
Dmitry Frishbuter efe99eeb46 Fix typo in README.md 2019-10-05 21:57:35 +06:00
Max Sokolov a0658f0b2e fix travis-ci 2019-09-29 10:03:45 -04:00
Max Sokolov 065cd9ace3 fix travis-ci 2019-09-29 09:56:11 -04:00
Max Sokolov c3652eec6f bump podspec 2019-09-29 09:51:33 -04:00
Max Sokolov 01bf1e01e3 support swift 5.1 and xcode 11 2019-09-29 09:50:40 -04:00
Max Sokolov 45aee4522d
Merge pull request #95 from bellebethcooper/master
Updated Swift tools version and added missing requirements
2019-09-19 16:45:06 -04:00
bellebethcooper f39e7ac602
Updated Swift tools version and added missing requirements 2019-09-07 15:49:07 +10:00
Max Sokolov dc1a924a80 drop iOS 9 checks on ci 2019-06-25 14:45:02 +03:00
Max Sokolov 402757c41f drop iOS 9 checks on ci 2019-06-25 14:36:47 +03:00
Max Sokolov d6520346c3
Merge pull request #94 from dmertsalov/master
Add accessoryButton action support
2019-05-29 09:34:05 +03:00
Denis 959032a03f Add accessoryButton action support 2019-05-27 11:01:35 +03:00
Max Sokolov 00de0cd809
Merge pull request #93 from IntelBohdan/master
added: willDeselect
2019-05-10 19:35:09 +03:00
Bogdan Kurpakov 337175c507 added: willDeselect 2019-05-09 11:10:31 +02:00
Max Sokolov a4bacd2c16 fix travis ci 2019-04-04 23:40:25 +03:00
Max Sokolov 603264a6d1 fix travis ci 2019-04-04 23:31:40 +03:00
Max Sokolov 1d4fdaad0d update readme 2019-04-04 23:27:25 +03:00
Max Sokolov f5ad500ba4 support swift 5.0, bump to 2.9.0 2019-04-04 23:26:15 +03:00
Ivan Zinovyev 1b4a988b35
Merge pull request #4 from TouchInstinct/expandable_states
Expandable states
2019-01-22 13:06:16 +03:00
Ivan Zinovyev f8f2ca6852 Fix indentation 2019-01-22 03:16:40 +03:00
Ivan Zinovyev ff18a4d8b8 Add Expandable States 2019-01-22 03:00:41 +03:00
Ivan Zinovyev d098691621 Merge remote-tracking branch 'maxsokolov/master' into expandable_states 2019-01-22 02:51:16 +03:00
Max Sokolov 8b6319d510 bump to 2.8.1 2019-01-10 18:35:53 +03:00
Max Sokolov b049864758 fix crash on iOS 10.3.1 2019-01-10 18:35:35 +03:00
Ivan Zinovyev 7f349c6944
Merge pull request #2 from TouchInstinct/feature/expandable
Feature/expandable
2019-01-09 19:13:15 +03:00
Ivan Zinovyev a768352b47 Some refactoring 2019-01-09 18:59:29 +03:00
Ivan Zinovyev 65cf31717b Remove deprecated attribute for estimatedHeight 2019-01-09 16:08:25 +03:00
Ivan Zinovyev 9d10bc18bf Remove manual height calculation functionality 2019-01-09 16:05:07 +03:00
Ivan Zinovyev 497b4e009c Make ExpandableCellHeightCalculator optional, not default 2019-01-09 16:04:32 +03:00
Ivan Zinovyev 6ef5ad504e Add initState for Expandable 2018-12-12 19:06:50 +03:00
Ivan Zinovyev e22ec03990 Add layout type 2018-12-12 19:03:16 +03:00
Ivan Zinovyev 5982d5db3a Use ExpandableCellHeightCalculator by default 2018-12-11 12:16:32 +03:00
Ivan Zinovyev 6eaf2cf3a2 Add Expandable protocol to expand/collapse cells 2018-12-10 17:15:43 +03:00
Ivan Zinovyev 921e2b42b3
Merge pull request #1 from iznv/height_for_item
Height for item
2018-11-27 12:30:04 +03:00
Ivan Zinovyev 70e6addcd0 Make AccurateCellHeightCalculator public 2018-11-04 08:31:46 +03:00
Ivan Zinovyev 86f07b8e7c Add AccurateCellHeightCalculator 2018-11-04 08:28:50 +03:00
Ivan Zinovyev 1c92c14a1a Fix for new swift 2018-11-04 08:20:28 +03:00
Ivan Zinovyev 7416271076 Fix type 2018-11-04 08:18:06 +03:00
Ivan Zinovyev 0812293813 Merge remote-tracking branch 'maxsokolov/master' into height_for_item 2018-11-04 08:16:15 +03:00
Max Sokolov f16a5a29c9 fix travis ci 2018-09-30 12:49:21 +03:00
Max Sokolov b379ac6272 fix travis ci 2018-09-30 12:42:46 +03:00
Max Sokolov 0c87f3ee88 update changelog 2018-09-30 12:37:38 +03:00
Max Sokolov ce24369557 bump to swift 4.2, support xcode10 2018-09-30 12:35:48 +03:00
Max Sokolov 85a45c33f2
Merge pull request #86 from petropavel13/swift_4_2
Swift 4.2, Xcode 10
2018-09-19 23:52:23 +03:00
Ivan Smolin 92b723b8ca perform automatic migration with warning suggestions 2018-09-19 12:03:51 +03:00
Ivan Smolin 12883a33de update project to recommended settings 2018-09-19 12:01:11 +03:00
Max Sokolov 1d28233ebe meaningful associatedtype name for ConfigurableCell 2018-06-26 22:37:40 +03:00
Max Sokolov dd8e5d0625
Merge pull request #81 from oxview/master
added: canDelete, editingStyle
2018-05-09 03:51:42 +09:00
into 21aab6256f added: canMoveTo 2018-05-08 16:54:13 +02:00
into cc02531b27 added: canDelete, editingStyle 2018-05-08 13:24:52 +02:00
Ivan Zinovyev b62dea8702 Add default implementation for height(for:) 2018-04-22 01:59:58 +03:00
Ivan Zinovyev 279fdd4854 Add height for item 2018-04-22 01:50:04 +03:00
Max Sokolov 02f77ed699
Merge pull request #80 from desyatov/master
Implement tableView:canMoveRowAt:indexPath method
2018-04-07 04:10:03 +09:00
Alexander Desyatov 74af45f3cd Implement tableView:canMoveRowAt:indexPath method 2018-04-06 20:22:29 +03:00
Max Sokolov f05511cfba
Merge pull request #79 from maxsokolov/bugfixes-and-improvements
Bugfixes and improvements
2018-03-17 16:25:58 +09:00
Max Sokolov de5593f6a9 bump pod version 2018-03-17 00:06:32 +03:00
Max Sokolov a1e1e9d908 add bounds check 2018-03-17 00:03:53 +03:00
Max Sokolov 70d83fc5b0 some codestyle improvements 2018-03-17 00:01:20 +03:00
Max Sokolov 23b51227fd add clear button 2018-03-16 23:54:33 +03:00
Max Sokolov d8ae329118
Merge pull request #77 from mrojas/master
Added support for moving rows
2018-03-13 14:00:07 +09:00
Matias Rojas fd50b732c9 Added support for moving rows 2018-03-13 00:43:00 +03:00
Matias Rojas 709f739ab6 Added support to move rows 2018-03-13 00:16:17 +03:00
Max Sokolov 9fff8c861c
Merge pull request #74 from astrokin/master
add swap rows func. usefull for table row reordering while editing
2018-02-23 12:23:56 +03:00
astrokin da76188afd add swap rows func. usefull for table row reordering while editing 2018-02-23 01:16:39 +03:00
Max Sokolov e3870d5ce8
Merge pull request #73 from sc0rch/master
Add new action .didEndDisplaying
2018-01-18 22:36:06 +03:00
Anton Aleshkevich a7488b1d4f Add new action .didEndDisplaying 2018-01-18 15:39:36 +03:00
Max Sokolov 4712406618 add example with autolayout section header view 2017-12-13 23:12:23 +03:00
Max Sokolov d595a5d2df update travis config 2017-09-24 21:14:58 +03:00
Max Sokolov 8463401b94 update travis config 2017-09-24 21:11:58 +03:00
Max Sokolov 05119aae43 change log update 2017-09-24 21:02:51 +03:00
Max Sokolov f3c7ea9e43 fix readme 2017-09-24 21:00:45 +03:00
Max Sokolov ae24e5c7c0 bump version 2017-09-24 20:58:16 +03:00
Max Sokolov 0c96110ca2 fix test naming 2017-09-24 20:55:37 +03:00
Max Sokolov ad979f4376 fix warnings 2017-09-24 20:54:06 +03:00
Max Sokolov 6f849e4cae bump xcode proj to swift 4.0 2017-09-24 20:52:22 +03:00
Max Sokolov 293caeebdb Merge pull request #65 from jesster2k10/patch-1
Update README.md
2017-07-07 18:14:21 +03:00
Jesse Onolememen cc69951856 Update README.md
fixed formatting
2017-07-07 13:48:05 +01:00
Maxim Sokolov 898191b0fa remove didEndDisplaying
provides problematic behaviour
2017-06-08 16:17:58 +03:00
Max Sokolov bd8ac5a28b Merge pull request #64 from motylevm/master
didEndDisplaying cell action support
2017-06-07 17:46:38 +03:00
Mikhail Motylev 9ef7a7a8bb version up 2017-06-07 17:45:23 +03:00
Mikhail Motylev ce2874c58d didEndDisplay support 2017-06-07 17:36:02 +03:00
motylevm c9bd52797d Merge pull request #1 from maxsokolov/master
Merge from source repo
2017-06-07 17:20:59 +03:00
Max Sokolov 06becd8624 Merge pull request #62 from pegurov/master
Added support for table section index
2017-04-17 12:00:33 +03:00
Pavel Gurov c277b6529c Renamed to sectionsIndexTitlesIndexes, and it is now nil if not used 2017-04-13 14:13:44 +03:00
Pavel Gurov 39b4df6ee9 moved indexTitle to TableSection 2017-04-13 11:37:08 +03:00
Pavel Gurov b49b01ff59 Added support for table section index 2017-04-13 10:52:29 +03:00
Max Sokolov 92b3e3b9a7 bump version 2017-02-28 22:38:52 +03:00
Max Sokolov 1b97d33cf6 Merge pull request #59 from alexs555/sections_update
added replace method to director
2017-02-28 22:37:38 +03:00
Alexey Shpirko 721ba1972a added replca method to director 2017-02-28 22:27:59 +03:00
Max Sokolov ef536b71b3 fix for travis-ci 2016-11-16 19:07:49 +03:00
Max Sokolov d12f3549aa bump changelog 2016-11-16 18:22:06 +03:00
Max Sokolov 155a4d1cb7 bump readme 2016-11-16 18:17:28 +03:00
Max Sokolov 6b25d54bd8 bump podspec 2016-11-16 18:13:36 +03:00
Max Sokolov 9238ab8472 correctly use defaultHeight 2016-11-16 18:13:05 +03:00
Max Sokolov 78b67e4c42 private(set) for rowHeightCalculator 2016-11-16 17:20:43 +03:00
Max Sokolov 3821d4300c correctly set rowHeightCalculator 2016-11-16 17:17:22 +03:00
Max Sokolov 065d8ccae3 bugfixes caused by RowHeightCalculator 2016-11-16 17:00:36 +03:00
Max Sokolov d4fc924676 call prepareForReuse on prototype cell 2016-11-03 19:50:29 +03:00
Max Sokolov 2b96888d12 minor improvements 2016-10-30 14:17:45 +03:00
Max Sokolov 9bbd9c4b69 add remove method 2016-10-29 01:05:58 +03:00
Max Sokolov 44cbb8b7d8 regroup methods 2016-10-29 00:46:47 +03:00
Max Sokolov 48a36e0969 add @discardableResult 2016-10-29 00:43:08 +03:00
Max Sokolov 67003df587 action fix 2016-10-24 17:44:50 +03:00
Max Sokolov 0e1aa0261f bump version 2016-10-24 17:41:36 +03:00
Max Sokolov 3ba9f45ded action fix 2016-10-24 17:40:59 +03:00
Max Sokolov 6abdc8ad8d bump version 2016-10-24 16:15:25 +03:00
Max Sokolov 7132a0a548 automatic cell registration improvement 2016-10-24 16:14:47 +03:00
Max Sokolov 934e7623f9 Merge pull request #56 from maxsokolov/actions_improvements
Support multiple actions with same type
2016-10-23 12:52:09 +03:00
Max Sokolov e74fee3653 bump changelog 2016-10-23 12:46:46 +03:00
Max Sokolov d5d785218a improve custom action init 2016-10-21 01:35:27 +03:00
Max Sokolov 443b8aaa62 fix table header footer section height 2016-10-21 00:52:18 +03:00
Max Sokolov 0f8d0db84e automatic cell registration improvements 2016-10-20 19:33:33 +03:00
Max Sokolov f19b637999 deprecate some methods 2016-10-20 12:13:30 +03:00
Max Sokolov b307b2d809 bump podspec 2016-10-19 01:20:05 +03:00
Max Sokolov ecc90b1db8 bump changelog 2016-10-19 01:19:05 +03:00
Max Sokolov 28f6a60d61 bump readme 2016-10-19 01:13:21 +03:00
Max Sokolov 1eed11abbf remove any action 2016-10-19 01:06:10 +03:00
Max Sokolov 162aee0033 add any action 2016-10-18 20:44:44 +03:00
Max Sokolov 135bc22804 support any actions on row 2016-10-17 19:40:07 +03:00
Max Sokolov f794dea7ee bump readme 2016-10-16 13:06:47 +03:00
Max Sokolov a5d8f0874a support userInfo in custom actions 2016-10-16 12:45:07 +03:00
Max Sokolov 8991138f18 bump readme 2016-10-16 12:35:42 +03:00
Max Sokolov becdc205bb fix 2016-10-16 12:22:40 +03:00
Max Sokolov c3d6f0af60 TableRowActionData -> TableRowActionOptions 2016-10-15 12:16:25 +03:00
Max Sokolov b449e05edc fix 2016-10-15 12:06:48 +03:00
Max Sokolov db6dc40fac make RowHeightCalculator public 2016-10-15 12:05:59 +03:00
Max Sokolov e18c6ef669 fixes 2016-10-15 11:13:54 +03:00
Max Sokolov b74403e2c6 add removeAction method 2016-10-15 11:09:48 +03:00
Max Sokolov 5f6de4703a add id to action 2016-10-15 01:31:05 +03:00
Max Sokolov c040613654 add removeAllActions method 2016-10-15 01:19:48 +03:00
Max Sokolov 0af6956d62 fix 2016-10-15 01:13:41 +03:00
Max Sokolov 3da097f1f1 support multiple actions with same type 2016-10-15 01:12:51 +03:00
Max Sokolov 3de586c4f6 implement RowAction protocol 2016-10-14 21:16:21 +03:00
Max Sokolov 7f65f0c4e0 move protocols to separate file 2016-10-14 20:59:13 +03:00
Max Sokolov 9ae69b71ca add RowAction protocol 2016-10-14 20:56:52 +03:00
Max Sokolov a240acee2d deprecate action method on TableRow, add on instead 2016-10-14 20:35:18 +03:00
Max Sokolov 9e1372252a Merge pull request #52 from motylevm/refactoring
Small refactoring
2016-10-11 17:42:53 +03:00
Mikhail Motylev 5a7a9b9655 refactoring 2016-10-11 11:27:28 +03:00
Max Sokolov 7eca499a59 Merge pull request #47 from maxsokolov/generic_improvements
Generic improvements
2016-10-06 11:37:23 +03:00
Max Sokolov 8de932b1db bump version 2016-10-06 11:22:50 +03:00
Max Sokolov be410f0cc3 bump readme 2016-10-06 02:14:08 +03:00
Max Sokolov 6601b14b7f bump changelog 2016-10-06 02:11:17 +03:00
Max Sokolov da98b9892f add changelog 2016-10-06 02:09:01 +03:00
Max Sokolov 04e62a8bc0 bump podspec 2016-10-06 01:54:28 +03:00
Max Sokolov 9ba8ef4932 bump readme 2016-10-06 01:51:43 +03:00
Max Sokolov 24d3540dd5 fix tests 2016-10-06 01:48:40 +03:00
Max Sokolov 6f441b2f7d reduce first generic parameter 2016-10-06 01:45:39 +03:00
Max Sokolov 38230a9a57 removed unused cast 2016-10-05 01:31:34 +03:00
Max Sokolov 676d4fdf88 bump podspec 2016-10-04 20:11:54 +03:00
Max Sokolov 262d2b83e2 bump readme 2016-10-04 20:11:33 +03:00
Max Sokolov a88043b33a use @discardableResult to suppress warnings 2016-10-04 20:10:09 +03:00
Max Sokolov 5db9726dab add swift version file 2016-10-03 15:59:39 +03:00
Max Sokolov 81fbd2f5e5 Merge pull request #44 from maxsokolov/swift/3.0
Swift 3.0 support
2016-10-03 16:09:50 +04:00
Max Sokolov 0b46f7c52f fix travis config 2016-10-03 15:02:57 +03:00
Max Sokolov 67a41b062f update travis config 2016-10-03 14:01:02 +03:00
Max Sokolov edcbcf60b7 bump podspec 2016-10-03 13:57:21 +03:00
Max Sokolov 6e2b707df7 bump readme 2016-10-03 13:55:23 +03:00
Max Sokolov a25e4c0eb4 remove unused code 2016-10-03 13:54:47 +03:00
Max Sokolov bfc59a0670 bump readme 2016-10-03 13:53:21 +03:00
Max Sokolov 3751eec584 fix tests 2016-10-03 13:51:29 +03:00
Max Sokolov 0916b5f990 fix actions 2016-10-03 13:48:59 +03:00
Max Sokolov 24a7f0589f fix warnings 2016-10-03 13:25:44 +03:00
Max Sokolov 3f88b6d0aa xcode project fixes 2016-10-03 12:22:45 +03:00
Max Sokolov 6c73ef4860 swift 3 syntax fixes 2016-10-03 12:19:05 +03:00
Max Sokolov c09e06f67d Merge pull request #40 from maxsokolov/develop
Significant performance improvements for prototypes
2016-09-16 12:37:20 +04:00
Max Sokolov 6a1650a68b bump podspec and readme 2016-09-15 01:11:06 +03:00
Max Sokolov eebaaf5268 remove useless cell registration 2016-09-15 01:08:52 +03:00
Max Sokolov 29202d4405 height strategy improvements 2016-09-06 19:30:47 +03:00
Max Sokolov 0f93d296ed PrototypeHeightStrategy improvements 2016-09-02 22:56:08 +03:00
Max Sokolov 1be65d18ad PrototypeHeightStrategy improvements 2016-09-02 21:42:34 +03:00
Max Sokolov 70d6cb8d73 TableCellManager -> TableCellRegisterer 2016-09-02 21:16:36 +03:00
Max Sokolov aeae61d48b remove checks in replace method 2016-09-02 21:11:30 +03:00
Max Sokolov 1df6447d73 set estimatedHeight nil by default 2016-09-02 21:09:28 +03:00
Max Sokolov 9218051f10 Merge pull request #39 from maxsokolov/develop
Add replace row at index method
2016-09-01 14:22:56 +04:00
Max Sokolov 52e44dcc6e fix 2016-09-01 13:13:57 +03:00
Max Sokolov 90aebed304 bump readme and podspec 2016-09-01 13:12:25 +03:00
Max Sokolov 40d138eac1 add replace row at index method 2016-09-01 13:11:38 +03:00
Max Sokolov 7a310f6db2 Merge pull request #38 from maxsokolov/develop
Support insert many rows in section
2016-08-30 14:55:36 +04:00
Max Sokolov 9a50eba12e bump readme 2016-08-30 13:46:09 +03:00
Max Sokolov ddced231c3 bump podspec 2016-08-30 13:45:52 +03:00
Max Sokolov 155524f13c support insert many rows in section 2016-08-30 13:45:34 +03:00
Max Sokolov 39eda5a8f3 Merge pull request #36 from maxsokolov/develop
Support editing actions
2016-08-22 19:11:07 +04:00
Max Sokolov 85cf81ecd6 bump podspec 2016-08-22 17:40:05 +03:00
Max Sokolov 99fc12e723 bump readme 2016-08-22 17:39:30 +03:00
Max Sokolov 60003d7557 support editing actions in TableDirector 2016-08-22 17:36:08 +03:00
Max Sokolov 5e5643c342 support editing actions in TableRow 2016-08-22 17:33:28 +03:00
Max Sokolov a7530eb3c7 rename items to rows in TableSection 2016-08-22 17:31:54 +03:00
Max Sokolov 1ae302f7ab Merge pull request #35 from maxsokolov/develop
Bump podspec 1.0.0
2016-08-20 18:03:45 +04:00
Max Sokolov f132560f39 Bump podspec 2016-08-20 16:56:10 +03:00
Max Sokolov a0113c23e0 Merge pull request #34 from maxsokolov/develop
Release 1.0.0
2016-08-20 17:55:18 +04:00
Max Sokolov 520f1f91f0 update readme 2016-08-20 16:47:14 +03:00
Max Sokolov d3773efbcc update readme 2016-08-20 16:44:58 +03:00
Max Sokolov 4dd21a7d2e bump readme 2016-08-20 16:41:10 +03:00
Max Sokolov a9a2496831 make TableCellManager internal 2016-08-20 16:30:39 +03:00
Max Sokolov 7a732cb7c3 bump readme 2016-08-20 16:25:08 +03:00
Max Sokolov d42757fb68 reuseIdentifier improvements 2016-08-20 14:20:29 +03:00
Max Sokolov a9c5d997cb bump readme 2016-08-20 14:03:30 +03:00
Max Sokolov b8084efc08 bump readme 2016-08-20 13:28:38 +03:00
Max Sokolov ab6e88ad65 add examples to readme 2016-08-20 13:19:35 +03:00
Max Sokolov 0c527fd47f fix tests 2016-08-20 13:03:31 +03:00
Max Sokolov 7438856134 set estimatedHeight and defaultHeight as static properties 2016-08-20 13:02:17 +03:00
Max Sokolov cf37c2b5eb Merge pull request #33 from maxsokolov/develop
Update travis-ci config
2016-08-20 13:25:43 +04:00
Max Sokolov f90db7de16 update travis-ci config 2016-08-20 12:19:56 +03:00
Max Sokolov 0186da5452 Merge pull request #31 from maxsokolov/develop
Remove isPrototype
2016-08-19 16:41:39 +04:00
Max Sokolov 09d5d75cef bump podspec and readme 2016-08-19 15:40:54 +03:00
Max Sokolov 8477cea35b remove isPrototype 2016-08-19 15:40:21 +03:00
Max Sokolov aaafb0d28d Merge pull request #30 from maxsokolov/develop
Various improvements
2016-08-19 15:03:32 +04:00
Max Sokolov 4b4d2a616d bump readme and podspec 2016-08-19 14:02:11 +03:00
Max Sokolov b7398a2986 improvements 2016-08-19 14:01:33 +03:00
Max Sokolov 762fd7546b Merge pull request #29 from maxsokolov/develop
Bugfixes
2016-08-19 13:36:47 +04:00
Max Sokolov a5ff6c520b bump readme and podspec 2016-08-19 12:21:03 +03:00
Max Sokolov d3ccd86577 bugfixes and improvements 2016-08-19 12:19:17 +03:00
Max Sokolov f23901d4a1 Merge pull request #28 from maxsokolov/develop
Remove row builder
2016-08-17 13:10:52 +04:00
Max Sokolov 663111500f bump podspec 2016-08-17 12:08:12 +03:00
Max Sokolov 26192a89b8 bump readme 2016-08-17 12:07:12 +03:00
Max Sokolov 2e8b3f3232 implement custom cell height action 2016-08-17 12:02:00 +03:00
Max Sokolov da51e2b7e9 remove row builder 2016-08-17 11:53:08 +03:00
Max Sokolov e23892e33e Merge pull request #24 from maxsokolov/develop
Drop iOS 7 support
2016-07-16 02:54:19 +04:00
Max Sokolov 40653d7a18 drop iOS 7 support 2016-07-16 01:48:56 +03:00
Max Sokolov 2d9bb07795 Merge pull request #23 from maxsokolov/develop
Upgrade action syntax
2016-07-16 02:42:28 +04:00
Max Sokolov c5c180594e bump version 2016-07-16 01:34:36 +03:00
Max Sokolov 0bf04ebae8 add action 2016-07-16 01:33:59 +03:00
Max Sokolov 5dde1c8d70 Merge pull request #21 from maxsokolov/develop
support iOS 7
2016-06-25 00:28:06 +04:00
Max Sokolov 9fbe7c9383 bump podspec 2016-06-24 23:27:17 +03:00
Max Sokolov 080b3f0458 supports iOS 7 2016-06-24 23:01:19 +03:00
Max Sokolov 126720f388 remove tableView from CellHeightCalculatable 2016-06-21 22:46:05 +03:00
Max Sokolov acebd1c3e8 add delete section method 2016-06-19 12:37:12 +03:00
Max Sokolov ffac9fae14 add insert section method 2016-06-19 12:35:12 +03:00
Max Sokolov c31aa997fa Merge pull request #20 from maxsokolov/develop
Provide Examples
2016-06-18 16:46:37 +04:00
Max Sokolov 8de38d8dee update readme 2016-06-18 15:44:57 +03:00
Max Sokolov 740768fdfd add examples 2016-06-18 15:40:02 +03:00
Max Sokolov 1539f39e54 Merge pull request #19 from maxsokolov/develop
Heights Calculation Strategy Support
2016-06-18 07:51:15 +04:00
Max Sokolov 1137b47af1 fix ci 2016-06-18 06:44:34 +03:00
Max Sokolov b68bd46ca8 bump version 2016-06-18 06:23:57 +03:00
Max Sokolov 89e3113a35 fix travis-ci config 2016-06-18 06:23:20 +03:00
Max Sokolov f74d35cc8c update readme 2016-06-18 06:21:24 +03:00
Max Sokolov 7e3d52fb92 add tests 2016-06-18 06:20:29 +03:00
Max Sokolov dd6092f87f fix register cells 2016-06-18 06:03:29 +03:00
Max Sokolov 221efc00ab add register cell method 2016-06-18 05:42:49 +03:00
Max Sokolov 23cceddfe2 functional programming example 2016-06-18 04:31:19 +03:00
Max Sokolov 1bc7301c7e add operators 2016-06-18 04:17:58 +03:00
Max Sokolov 86b93ae78d update readme 2016-06-17 00:01:55 +03:00
Max Sokolov 1266a91446 addAction -> action 2016-06-16 23:59:25 +03:00
Max Sokolov d4efb6a679 improve hash 2016-06-16 22:45:33 +03:00
Max Sokolov 0d7d97db57 update readme 2016-06-16 01:42:24 +03:00
Max Sokolov 018c35ef53 update readme 2016-06-16 01:16:53 +03:00
Max Sokolov 03b67c5178 update readme 2016-06-16 00:54:22 +03:00
Max Sokolov 09e5c4b8a4 update readme 2016-06-15 21:25:28 +03:00
Max Sokolov 23378e2de4 add invalidate 2016-06-15 21:23:41 +03:00
Max Sokolov dd78ffa41e rowItems -> rows 2016-06-15 21:13:34 +03:00
Max Sokolov 1bd049e276 add delete and insert methods 2016-06-15 20:56:17 +03:00
Max Sokolov d01c6c7fba update readme 2016-06-15 20:42:53 +03:00
Max Sokolov a2f8bdde30 add isPrototype property 2016-06-15 20:42:10 +03:00
Max Sokolov 98c481be71 handle separator height 2016-06-15 01:21:12 +03:00
Max Sokolov 9d0c278a34 add shouldUsePrototypeCellHeightCalculation property 2016-06-15 01:05:04 +03:00
Max Sokolov fadb2f4e60 add RowHashable protocol 2016-06-14 23:50:30 +03:00
Max Sokolov cc75e84f58 try height strategy 2016-06-14 20:47:39 +03:00
Max Sokolov 7cc572c7aa update readme 2016-06-14 16:07:49 +03:00
Max Sokolov 3e4f7d81ff remove configure action 2016-06-13 16:34:57 +03:00
Max Sokolov 1ed3db1cb1 update readme 2016-06-13 16:21:16 +03:00
Max Sokolov 8b5ad2fcd1 update readme 2016-06-13 16:07:12 +03:00
Max Sokolov 885977b0ef Merge pull request #17 from maxsokolov/develop
Support Carthage and Swift Package Manager
2016-06-13 14:55:24 +04:00
Max Sokolov c8fca159ee update readme 2016-06-13 13:53:12 +03:00
Max Sokolov 6f3daa337b support Swift Package Manager 2016-06-13 13:51:02 +03:00
Max Sokolov 1e018523bb update readme 2016-06-13 13:12:12 +03:00
Max Sokolov c352099fa2 mark as shared 2016-06-13 13:09:05 +03:00
Max Sokolov 9bf4e72f86 update readme 2016-06-13 01:55:41 +03:00
Max Sokolov 721596131b update readme 2016-06-13 01:39:26 +03:00
Max Sokolov 8509c8d64e bump readme 2016-06-13 01:07:46 +03:00
Max Sokolov 42eec9243e add init to builder 2016-06-12 23:49:06 +03:00
Max Sokolov 533c6e6eb8 add row builder 2016-06-12 23:42:39 +03:00
Max Sokolov 310cec9c12 fix cell action 2016-06-12 21:38:53 +03:00
Max Sokolov 1e4f76e04f remove row builder 2016-06-12 19:01:24 +03:00
Max Sokolov 272d238a2a fix willSelect 2016-06-12 17:01:44 +03:00
Max Sokolov 653948ec08 fix operators 2016-06-12 15:27:50 +03:00
Max Sokolov 699d2aa00d fix row actions 2016-06-12 14:58:38 +03:00
Max Sokolov 166a7421f4 Merge pull request #16 from maxsokolov/develop
Rename to TableKit
2016-06-11 13:35:07 +04:00
Max Sokolov 0463cfdf40 Merge branch 'master' into develop
# Conflicts:
#	README.md
#	TableKitDemo/TableKitDemo.xcodeproj/project.pbxproj
2016-06-11 12:33:22 +03:00
Max Sokolov e3ddb25b22 bump readme 2016-06-11 12:28:46 +03:00
Max Sokolov 87821ac3c4 fix 2016-06-11 00:52:03 +03:00
Max Sokolov 51de86c81a rename to TableKit
Thanks a lot to my greatest friend, Alexander Nikishin
2016-06-11 00:51:10 +03:00
Max Sokolov 96b2a19f1d store actions 2016-06-10 00:32:00 +03:00
Max Sokolov 708f164c96 value actions 2016-06-09 19:34:28 +03:00
Max Sokolov bf01dc638a add row action 2016-06-09 02:27:25 +03:00
Max Sokolov 01dc5dfd21 table row improvements 2016-06-09 01:36:50 +03:00
Max Sokolov 234e0efc85 rethink point 2016-06-09 01:20:52 +03:00
Max Sokolov 44c1a861d8 fix row item generic 2016-06-08 02:08:43 +03:00
Max Sokolov c27a386807 rowitem improvements 2016-06-07 23:40:46 +03:00
Max Sokolov 21f131bb19 add RowConfigurable protocol 2016-06-07 21:41:57 +03:00
Max Sokolov 0c5f24a491 fix 2016-06-07 02:15:37 +03:00
Max Sokolov 6e4ea9caf6 remove base row builder 2016-06-07 02:04:15 +03:00
Max Sokolov 229ef94c0e height strategies improvements 2016-06-07 01:56:41 +03:00
Max Sokolov 66f318bb15 remove prototype row builder 2016-06-06 23:37:00 +03:00
Max Sokolov 7faee8f24e add HeightStrategies 2016-06-06 23:35:20 +03:00
Max Sokolov e121c3fbc6 dynamic approach 2016-06-05 23:58:51 +03:00
Max Sokolov e096889819 fix xcode proj 2016-06-03 00:28:56 +03:00
Max Sokolov b5c7493140 dynamic cells 2016-06-02 19:46:50 +03:00
Max Sokolov 08fb8af74e add TableRowAction 2016-06-02 00:48:10 +03:00
Max Sokolov bdf009b980 update task 2016-05-30 01:02:36 +03:00
Max Sokolov 4bda514277 add operators 2016-05-29 13:58:05 +03:00
Max Sokolov 7f1edda39e fix scroll delegate 2016-05-28 14:34:37 +03:00
Max Sokolov 82fce34c2e doing animations 2016-05-26 02:27:58 +03:00
Max Sokolov 3f54cacab0 estimated height improvements 2016-05-26 01:42:55 +03:00
Max Sokolov 051dba0e97 builders to separate files 2016-05-25 21:54:04 +03:00
Max Sokolov 94925e301b try preheat 2016-05-25 21:47:14 +03:00
Max Sokolov bc503bcb4f fix build 2016-05-25 01:37:41 +03:00
Max Sokolov 92ee2b731c fix autolayout issues 2016-05-25 01:31:22 +03:00
Max Sokolov bba29151ec first try prototype cells 2016-05-24 22:51:04 +03:00
Max Sokolov b87d2b8aec remove Foundation import 2016-05-24 20:22:35 +03:00
Max Sokolov 779bf8dd43 estimatedHeight to CGFloat 2016-05-24 01:30:26 +03:00
Max Sokolov ba8aa05d8b add prototype based cell height row builder 2016-05-24 01:13:18 +03:00
Max Sokolov 0250b08302 add row height 2016-05-23 22:30:17 +03:00
Max Sokolov 348c12e1fc fix travis-ci badge 2016-05-15 19:02:44 +03:00
Max Sokolov 87d959f05f fix travis-ci badge 2016-05-15 19:01:47 +03:00
Max Sokolov 874f767528 Merge pull request #13 from maxsokolov/develop
api improvements
2016-05-15 20:00:18 +04:00
Max Sokolov 05bc9039ea fix readme 2016-05-15 18:21:56 +03:00
Max Sokolov cb9789ccd8 bump readme 2016-05-15 18:18:04 +03:00
Max Sokolov a41222300f bump readme 2016-05-15 18:08:51 +03:00
Max Sokolov ae2707920f bump podspec 2016-05-15 16:11:59 +03:00
Max Sokolov 39d4f8ce4e bump readme 2016-05-15 16:10:38 +03:00
Max Sokolov 08a56c799c bump readme 2016-05-15 15:53:07 +03:00
Max Sokolov 6498e39daa sections now keep reference to director instead of table view 2016-05-15 15:09:27 +03:00
Max Sokolov 0e42c836c1 case improvement 2016-05-08 16:30:08 +03:00
Max Sokolov e6450068a5 fix tests 2016-05-08 13:24:21 +03:00
Max Sokolov 87e7dbd6c4 move back to RowBuilder protocol 2016-05-08 00:39:58 +03:00
Max Sokolov c3fda15cb4 method names refactoring 2016-05-07 23:28:15 +03:00
Max Sokolov 7083462aca add valueAction 2016-05-06 19:03:36 +03:00
Max Sokolov 32a257149e some refactoring 2016-05-02 22:03:52 +03:00
Max Sokolov 03186a2cf5 generic improvements 2016-04-30 00:14:07 +03:00
Max Sokolov ef384cb0f1 code improvements, add examples 2016-04-17 00:27:59 +03:00
Max Sokolov 7d9d6ff60b remove useless code, add gitignore 2016-04-16 22:48:46 +03:00
Max Sokolov fea004861c Merge pull request #12 from maxsokolov/develop
0.4.1
2016-04-13 22:03:01 +04:00
Max Sokolov 9c909b8471 methods improvements 2016-04-13 20:51:23 +03:00
Max Sokolov b71d141cab Merge pull request #11 from maxsokolov/develop
0.4.0
2016-04-13 02:15:42 +04:00
Max Sokolov dd97d7a279 update travis-ci config 2016-04-13 01:11:28 +03:00
Max Sokolov ae34cefe7a update travis-ci config 2016-04-13 00:53:53 +03:00
Max Sokolov b05b5871c5 update travis-ci config 2016-04-13 00:50:54 +03:00
Max Sokolov 6df6d3628c update travis-ci config 2016-04-13 00:43:21 +03:00
Max Sokolov 08ce2d62e4 ConfigurableCell improvements 2016-04-13 00:29:44 +03:00
Max Sokolov f652ba085f Merge pull request #10 from maxsokolov/develop
support swift 2.2
2016-03-22 22:43:26 +04:00
Max Sokolov db8a8740ff update travis config 2016-03-22 21:29:58 +03:00
Max Sokolov f0afd40e5a support swift 2.2 2016-03-22 21:28:23 +03:00
70 changed files with 3255 additions and 1894 deletions

39
.gitignore vendored Executable file
View File

@ -0,0 +1,39 @@
# Mac OS X
.DS_Store
# Xcode
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
.build/
# Carthage
Carthage/Build

1
.swift-version Normal file
View File

@ -0,0 +1 @@
5.7

View File

@ -1,11 +1,24 @@
language: objective-c language: objective-c
osx_image: xcode7.2 osx_image: xcode11
branches: branches:
only: only:
- master - master
before_install: env:
- cd Tablet global:
- LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8
- IOS_SDK=iphonesimulator13.0
- SCHEME_IOS="TableKit"
- PROJECT_FRAMEWORK="TableKit.xcodeproj"
matrix:
- DESTINATION="OS=10.3.1,name=iPhone 5" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=11.1,name=iPhone 6" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=12.0,name=iPhone 7 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=13.0,name=iPhone 11" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
script: script:
- xctool clean build test -project Tablet.xcodeproj -scheme Tablet -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES - set -o pipefail
after_success: - xcodebuild -version
- cd $TRAVIS_BUILD_DIR - xcodebuild -showsdks
- xcodebuild -project "$PROJECT_FRAMEWORK" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO test

55
CHANGELOG.md Normal file
View File

@ -0,0 +1,55 @@
# Change Log
All notable changes to this project will be documented in this file.
## [2.10.0](https://github.com/maxsokolov/TableKit/releases/tag/2.10.0)
Released on 2019-09-29.
- Swift 5.1 support.
## [2.9.0](https://github.com/maxsokolov/TableKit/releases/tag/2.9.0)
Released on 2019-04-04.
- Swift 5.0 support.
## [2.8.0](https://github.com/maxsokolov/TableKit/releases/tag/2.8.0)
Released on 2018-09-30.
- Swift 4.2 support.
## [2.5.0](https://github.com/maxsokolov/TableKit/releases/tag/2.5.0)
Released on 2017-09-24.
- Swift 4.0 support.
## [2.3.0](https://github.com/maxsokolov/TableKit/releases/tag/2.3.0)
Released on 2016-11-16.
- `shouldUsePrototypeCellHeightCalculation` moved to `TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)`
- Prototype cell height calculation bugfixes
## [2.1.0](https://github.com/maxsokolov/TableKit/releases/tag/2.1.0)
Released on 2016-10-19.
- `action` method was deprecated on TableRow. Use `on` instead.
- Support multiple actions with same type on row.
- You could now build your own cell height calculating strategy. See [TablePrototypeCellHeightCalculator](Sources/TablePrototypeCellHeightCalculator.swift).
- Default distance between sections changed to `UITableViewAutomaticDimension`. You can customize it, see [TableSection](Sources/TableSection.swift)
## [2.0.0](https://github.com/maxsokolov/TableKit/releases/tag/2.0.0)
Released on 2016-10-06. Breaking changes in 2.0.0:
<br/>The signatures of `TableRow` and `TableRowAction` classes were changed from
```swift
let action = TableRowAction<String, StringTableViewCell>(.click) { (data) in
}
let row = TableRow<String, StringTableViewCell>(item: "some string", actions: [action])
```
to
```swift
let action = TableRowAction<StringTableViewCell>(.click) { (data) in
}
let row = TableRow<StringTableViewCell>(item: "some string", actions: [action])
```
This is the great improvement that comes from the community. Thanks a lot!
## [1.3.0](https://github.com/maxsokolov/TableKit/releases/tag/1.3.0)
Released on 2016-09-04. Swift 3.0 support.
## [0.1.0](https://github.com/maxsokolov/TableKit/releases/tag/0.1.0)
Released on 2015-11-15. Initial release called Tablet.

View File

@ -0,0 +1,7 @@
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

View File

@ -0,0 +1,70 @@
import UIKit
import TableKit
class AutolayoutCellsController: UIViewController {
@IBOutlet weak var tableView: UITableView! {
didSet {
tableDirector = TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)
}
}
var tableDirector: TableDirector!
func randomString(length: Int) -> String {
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let len = UInt32(letters.length)
var randomString = ""
for _ in 0 ..< length {
let rand = arc4random_uniform(len)
var nextChar = letters.character(at: Int(rand))
randomString += NSString(characters: &nextChar, length: 1) as String
}
return randomString
}
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Autolayout cells"
let header = AutolayoutSectionHeaderView.loadFromNib()
let section = TableSection(headerView: header, footerView: nil)
section.headerHeight = getViewHeight(view: header, width: UIScreen.main.bounds.width)
var rows = 0
while rows <= 20 {
rows += 1
let row = TableRow<AutolayoutTableViewCell>(item: randomString(length: randomInt(min: 20, max: 100)))
section += row
}
tableDirector += section
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Clear", style: .done, target: self, action: #selector(clear))
}
@objc
func clear() {
tableDirector.clear()
tableDirector.reload()
}
func getViewHeight(view: UIView, width: CGFloat) -> CGFloat {
view.frame = CGRect(x: 0, y: 0, width: width, height: view.frame.size.height)
view.setNeedsLayout()
view.layoutIfNeeded()
return view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
}
}

View File

@ -0,0 +1,44 @@
import UIKit
import TableKit
class MainController: UIViewController {
@IBOutlet weak var tableView: UITableView! {
didSet {
tableDirector = TableDirector(tableView: tableView)
}
}
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
title = "TableKit"
let clickAction = TableRowAction<ConfigurableTableViewCell>(.click) { [weak self] (options) in
switch options.indexPath.row {
case 0:
self?.performSegue(withIdentifier: "autolayoutcells", sender: nil)
case 1:
self?.performSegue(withIdentifier: "nibcells", sender: nil)
default:
break
}
}
let printClickAction = TableRowAction<ConfigurableTableViewCell>(.click) { (options) in
print("click", options.indexPath)
}
let rows = [
TableRow<ConfigurableTableViewCell>(item: "Autolayout cells", actions: [clickAction, printClickAction]),
TableRow<ConfigurableTableViewCell>(item: "Nib cells", actions: [clickAction, printClickAction])
]
// automatically creates a section, also could be used like tableDirector.append(rows: rows)
tableDirector += rows
}
}

View File

@ -0,0 +1,26 @@
import UIKit
import TableKit
class NibCellsController: UITableViewController {
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
title = "Nib cells"
tableDirector = TableDirector(tableView: tableView)
let numbers = [1000, 2000, 3000, 4000, 5000]
let rows = numbers.map {
TableRow<NibTableViewCell>(item: $0)
.on(.shouldHighlight) { (_) -> Bool in
return false
}
}
tableDirector.append(rows: rows)
}
}

View File

@ -0,0 +1,24 @@
import UIKit
final class AutolayoutSectionHeaderView: UIView {
@IBOutlet weak var testLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
testLabel.text = "My super super super super super super super super super super super super super super super super super super super long text!"
}
static func loadFromNib() -> AutolayoutSectionHeaderView {
let view = Bundle(for: self)
.loadNibNamed(
String(describing: self),
owner: nil,
options: nil
)?.first
return view as! AutolayoutSectionHeaderView
}
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<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"/>
<view contentMode="scaleToFill" id="rrg-Sy-26U" customClass="AutolayoutSectionHeaderView" customModule="TableKitDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="267" height="68"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7al-ia-5Ud">
<rect key="frame" x="20" y="24" width="227" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="0.67241452084558406" blue="0.62083349393841014" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="7al-ia-5Ud" secondAttribute="bottom" constant="23" id="0Wx-eb-Gf8"/>
<constraint firstAttribute="trailing" secondItem="7al-ia-5Ud" secondAttribute="trailing" constant="20" id="5lG-XV-dd4"/>
<constraint firstItem="7al-ia-5Ud" firstAttribute="top" secondItem="rrg-Sy-26U" secondAttribute="top" constant="24" id="VBP-29-bA8"/>
<constraint firstItem="7al-ia-5Ud" firstAttribute="leading" secondItem="rrg-Sy-26U" secondAttribute="leading" constant="20" id="cAb-gb-fpr"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="testLabel" destination="7al-ia-5Ud" id="sd9-v6-m3z"/>
</connections>
<point key="canvasLocation" x="-188.5" y="-277"/>
</view>
</objects>
</document>

View File

@ -0,0 +1,31 @@
import UIKit
import TableKit
private let LoremIpsumTitle = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
class AutolayoutTableViewCell: UITableViewCell, ConfigurableCell {
typealias T = String
@IBOutlet var titleLabel: UILabel!
@IBOutlet var subtitleLabel: UILabel!
static var estimatedHeight: CGFloat? {
return 150
}
func configure(with string: T) {
titleLabel.text = LoremIpsumTitle
subtitleLabel.text = string
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.layoutIfNeeded()
titleLabel.preferredMaxLayoutWidth = titleLabel.bounds.size.width
subtitleLabel.preferredMaxLayoutWidth = subtitleLabel.bounds.size.width
}
}

View File

@ -0,0 +1,11 @@
import UIKit
import TableKit
class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with text: String) {
accessoryType = .disclosureIndicator
textLabel?.text = text
}
}

View File

@ -0,0 +1,15 @@
import UIKit
import TableKit
class NibTableViewCell: UITableViewCell, ConfigurableCell {
@IBOutlet weak var titleLabel: UILabel!
static var defaultHeight: CGFloat? {
return 100
}
func configure(with number: Int) {
titleLabel.text = "\(number)"
}
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="NibTableViewCell" id="4JI-V9-Bra" customClass="NibTableViewCell" customModule="TableKitDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4JI-V9-Bra" id="Bal-V1-EZ2">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qBe-iP-Q1p">
<rect key="frame" x="8" y="11" width="304" height="21"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="qBe-iP-Q1p" firstAttribute="top" secondItem="Bal-V1-EZ2" secondAttribute="top" constant="11" id="B84-GR-YoJ"/>
<constraint firstAttribute="bottom" secondItem="qBe-iP-Q1p" secondAttribute="bottom" constant="12" id="Cmh-Tj-2Pw"/>
<constraint firstAttribute="trailing" secondItem="qBe-iP-Q1p" secondAttribute="trailing" constant="8" id="ZJP-9s-4ZM"/>
<constraint firstItem="qBe-iP-Q1p" firstAttribute="leading" secondItem="Bal-V1-EZ2" secondAttribute="leading" constant="8" id="wAW-KV-dCJ"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="titleLabel" destination="qBe-iP-Q1p" id="UO4-lc-Kr0"/>
</connections>
<point key="canvasLocation" x="524" y="319"/>
</tableViewCell>
</objects>
</document>

View File

@ -33,6 +33,8 @@
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="dOU-ON-YYD">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="Nb3-Yi-ik8">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="dOU-ON-YYD" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Ngx-L8-cbs">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="hPs-tQ-ZjY" kind="relationship" relationship="rootViewController" id="cVN-5m-Nvf"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1px-T5-UXL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="334" y="287"/>
</scene>
<!--Autolayout Cells Controller-->
<scene sceneID="bgC-Xq-OSw">
<objects>
<viewController id="grv-aL-Qbb" customClass="AutolayoutCellsController" customModule="TableKitDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="COn-EH-LKP"/>
<viewControllerLayoutGuide type="bottom" id="iga-ib-rj1"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="5uw-uC-8lc">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="o67-xJ-fPW">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="AutolayoutTableViewCell" rowHeight="141" id="IBY-tW-SgU" customClass="AutolayoutTableViewCell" customModule="TableKitDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="92" width="600" height="141"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="IBY-tW-SgU" id="UNZ-nz-200">
<rect key="frame" x="0.0" y="0.0" width="600" height="141"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="751" verticalHuggingPriority="751" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" placeholderIntrinsicWidth="172" placeholderIntrinsicHeight="21" text="Title" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YnK-4H-0SS">
<rect key="frame" x="128" y="20" width="452" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="750" placeholderIntrinsicWidth="172" placeholderIntrinsicHeight="18" text="Subtitle" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oPO-9F-UcX">
<rect key="frame" x="128" y="44" width="452" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="il6-0T-Sfb">
<rect key="frame" x="20" y="20" width="100" height="100"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" priority="999" constant="100" id="Ryh-cg-Q69"/>
<constraint firstAttribute="height" priority="999" constant="100" id="cjb-Sw-psw"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="oPO-9F-UcX" secondAttribute="bottom" constant="20" id="3wD-H5-3wL"/>
<constraint firstAttribute="trailing" secondItem="oPO-9F-UcX" secondAttribute="trailing" constant="20" id="4ns-lI-pTV"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="il6-0T-Sfb" secondAttribute="bottom" priority="999" constant="20" id="CQQ-43-LmG"/>
<constraint firstItem="il6-0T-Sfb" firstAttribute="top" secondItem="UNZ-nz-200" secondAttribute="top" constant="20" id="F5Q-jP-iQ0"/>
<constraint firstItem="YnK-4H-0SS" firstAttribute="leading" secondItem="il6-0T-Sfb" secondAttribute="trailing" constant="8" id="Qwz-5H-QFk"/>
<constraint firstItem="oPO-9F-UcX" firstAttribute="leading" secondItem="il6-0T-Sfb" secondAttribute="trailing" constant="8" id="VNa-5R-BEy"/>
<constraint firstItem="il6-0T-Sfb" firstAttribute="leading" secondItem="UNZ-nz-200" secondAttribute="leading" constant="20" id="dIx-cj-H8P"/>
<constraint firstItem="YnK-4H-0SS" firstAttribute="top" secondItem="UNZ-nz-200" secondAttribute="top" constant="20" id="h4u-Nr-cGF"/>
<constraint firstItem="oPO-9F-UcX" firstAttribute="top" secondItem="YnK-4H-0SS" secondAttribute="bottom" constant="3" id="j6U-hX-Z6k"/>
<constraint firstAttribute="trailing" secondItem="YnK-4H-0SS" secondAttribute="trailing" constant="20" id="ulW-hU-AJG"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="subtitleLabel" destination="oPO-9F-UcX" id="RIK-1t-nVt"/>
<outlet property="titleLabel" destination="YnK-4H-0SS" id="ilA-7H-pq7"/>
</connections>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="iga-ib-rj1" firstAttribute="top" secondItem="o67-xJ-fPW" secondAttribute="bottom" id="jrJ-xl-S4k"/>
<constraint firstItem="o67-xJ-fPW" firstAttribute="top" secondItem="5uw-uC-8lc" secondAttribute="top" id="ntC-Lp-77v"/>
<constraint firstAttribute="trailing" secondItem="o67-xJ-fPW" secondAttribute="trailing" id="s2g-E1-S4V"/>
<constraint firstItem="o67-xJ-fPW" firstAttribute="leading" secondItem="5uw-uC-8lc" secondAttribute="leading" id="uWa-n0-GSQ"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="HPV-jJ-NPc"/>
<connections>
<outlet property="tableView" destination="o67-xJ-fPW" id="A8B-MV-tNa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xij-Hw-J33" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1762" y="-59"/>
</scene>
<!--Main Controller-->
<scene sceneID="rB2-pg-ya1">
<objects>
<viewController id="hPs-tQ-ZjY" customClass="MainController" customModule="TableKitDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="bj8-sc-pHV"/>
<viewControllerLayoutGuide type="bottom" id="exD-V1-XKl"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="w1B-sD-vvx">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="lVS-GW-taC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="lVS-GW-taC" firstAttribute="leading" secondItem="w1B-sD-vvx" secondAttribute="leading" id="BML-us-tKv"/>
<constraint firstItem="lVS-GW-taC" firstAttribute="top" secondItem="w1B-sD-vvx" secondAttribute="top" id="KmO-4D-EEl"/>
<constraint firstItem="exD-V1-XKl" firstAttribute="top" secondItem="lVS-GW-taC" secondAttribute="bottom" id="plx-uF-bQd"/>
<constraint firstAttribute="trailing" secondItem="lVS-GW-taC" secondAttribute="trailing" id="uYe-yd-m86"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="i9y-B7-dhJ"/>
<connections>
<outlet property="tableView" destination="lVS-GW-taC" id="C28-ml-ARU"/>
<segue destination="grv-aL-Qbb" kind="show" identifier="autolayoutcells" id="YrM-qz-6Kd"/>
<segue destination="apk-bL-6NO" kind="show" identifier="nibcells" id="gXD-XZ-oBm"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="i96-Zo-Wfg" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1046" y="287"/>
</scene>
<!--Nib Cells Controller-->
<scene sceneID="DHa-3S-OkR">
<objects>
<tableViewController id="apk-bL-6NO" customClass="NibCellsController" customModule="TableKitDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="wDt-Cb-VB2">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="apk-bL-6NO" id="Xhe-U9-wKI"/>
<outlet property="delegate" destination="apk-bL-6NO" id="NqC-Vg-68j"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="oWG-DK-A9C" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1762" y="671"/>
</scene>
</scenes>
</document>

View File

@ -7,21 +7,78 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
5079ADE31FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079ADE21FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift */; };
5079ADE61FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5079ADE51FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib */; };
DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */; };
DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465C1D1569CC00AA83EE /* AutolayoutCellsController.swift */; };
DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */; };
DA5546641D15762000AA83EE /* NibTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5546631D15762000AA83EE /* NibTableViewCell.swift */; };
DA5546661D15765900AA83EE /* NibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5546651D15765900AA83EE /* NibTableViewCell.xib */; };
DA5546681D15771D00AA83EE /* NibCellsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5546671D15771D00AA83EE /* NibCellsController.swift */; };
DA9EA7D91D0EC65B0021F650 /* TableKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9EA7D61D0EC5C60021F650 /* TableKit.framework */; };
DA9EA7DA1D0EC65B0021F650 /* TableKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA9EA7D61D0EC5C60021F650 /* TableKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */; }; DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */; };
DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */; }; DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */; };
DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */; }; DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */; };
DAC2D5D41C9D3118009E9C19 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */; };
DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */; }; DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */; };
DACB71761CC2D63D00432BD3 /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DACB71751CC2D63D00432BD3 /* MainController.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DA9EA7D51D0EC5C60021F650 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = DA9EA7561D0B679A0021F650;
remoteInfo = TableKit;
};
DA9EA7D71D0EC5C60021F650 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = DA9EA7C41D0EC45F0021F650;
remoteInfo = TableKitTests;
};
DA9EA7DB1D0EC65B0021F650 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = DA9EA7551D0B679A0021F650;
remoteInfo = TableKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
DA9EA7DD1D0EC65B0021F650 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
DA9EA7DA1D0EC65B0021F650 /* TableKit.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
DAB7EB271BEF787300D2AD5E /* TabletDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabletDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5079ADE21FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutolayoutSectionHeaderView.swift; sourceTree = "<group>"; };
5079ADE51FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AutolayoutSectionHeaderView.xib; sourceTree = "<group>"; };
DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutolayoutTableViewCell.swift; sourceTree = "<group>"; };
DA55465C1D1569CC00AA83EE /* AutolayoutCellsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutolayoutCellsController.swift; sourceTree = "<group>"; };
DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableTableViewCell.swift; sourceTree = "<group>"; };
DA5546631D15762000AA83EE /* NibTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibTableViewCell.swift; sourceTree = "<group>"; };
DA5546651D15765900AA83EE /* NibTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NibTableViewCell.xib; sourceTree = "<group>"; };
DA5546671D15771D00AA83EE /* NibCellsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibCellsController.swift; sourceTree = "<group>"; };
DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TableKit.xcodeproj; path = ../TableKit.xcodeproj; sourceTree = "<group>"; };
DAB7EB271BEF787300D2AD5E /* TableKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TableKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; }; DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; }; DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DAC2D69D1C9E78B5009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; DAC2D69D1C9E78B5009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DACB71751CC2D63D00432BD3 /* MainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -29,17 +86,36 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DA9EA7D91D0EC65B0021F650 /* TableKit.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
DA539C871CF50B1800368ACB /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
DA9EA7D11D0EC5C50021F650 /* Products */ = {
isa = PBXGroup;
children = (
DA9EA7D61D0EC5C60021F650 /* TableKit.framework */,
DA9EA7D81D0EC5C60021F650 /* TableKitTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
DAB7EB1E1BEF787300D2AD5E = { DAB7EB1E1BEF787300D2AD5E = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */,
DAC2D5C61C9D2FE5009E9C19 /* Classes */, DAC2D5C61C9D2FE5009E9C19 /* Classes */,
DAC2D5CB1C9D3058009E9C19 /* Resources */, DAC2D5CB1C9D3058009E9C19 /* Resources */,
DA539C871CF50B1800368ACB /* Frameworks */,
DAB7EB281BEF787300D2AD5E /* Products */, DAB7EB281BEF787300D2AD5E /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
@ -47,7 +123,7 @@
DAB7EB281BEF787300D2AD5E /* Products */ = { DAB7EB281BEF787300D2AD5E /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DAB7EB271BEF787300D2AD5E /* TabletDemo.app */, DAB7EB271BEF787300D2AD5E /* TableKitDemo.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -64,7 +140,8 @@
DAC2D5C71C9D3005009E9C19 /* Presentation */ = { DAC2D5C71C9D3005009E9C19 /* Presentation */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DAC2D5D11C9D30D8009E9C19 /* Main */, DACB71731CC2D5ED00432BD3 /* Controllers */,
DACB71741CC2D5FD00432BD3 /* Views */,
); );
path = Presentation; path = Presentation;
sourceTree = "<group>"; sourceTree = "<group>";
@ -96,22 +173,6 @@
path = Storyboards; path = Storyboards;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DAC2D5D11C9D30D8009E9C19 /* Main */ = {
isa = PBXGroup;
children = (
DAC2D5D21C9D30E4009E9C19 /* ViewControllers */,
);
path = Main;
sourceTree = "<group>";
};
DAC2D5D21C9D30E4009E9C19 /* ViewControllers */ = {
isa = PBXGroup;
children = (
DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */,
);
path = ViewControllers;
sourceTree = "<group>";
};
DAC2D69A1C9E75BE009E9C19 /* Assets */ = { DAC2D69A1C9E75BE009E9C19 /* Assets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -120,24 +181,49 @@
path = Assets; path = Assets;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DACB71731CC2D5ED00432BD3 /* Controllers */ = {
isa = PBXGroup;
children = (
DACB71751CC2D63D00432BD3 /* MainController.swift */,
DA55465C1D1569CC00AA83EE /* AutolayoutCellsController.swift */,
DA5546671D15771D00AA83EE /* NibCellsController.swift */,
);
path = Controllers;
sourceTree = "<group>";
};
DACB71741CC2D5FD00432BD3 /* Views */ = {
isa = PBXGroup;
children = (
DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */,
DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */,
DA5546631D15762000AA83EE /* NibTableViewCell.swift */,
DA5546651D15765900AA83EE /* NibTableViewCell.xib */,
5079ADE21FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift */,
5079ADE51FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib */,
);
path = Views;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
DAB7EB261BEF787300D2AD5E /* TabletDemo */ = { DAB7EB261BEF787300D2AD5E /* TableKitDemo */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */; buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TableKitDemo" */;
buildPhases = ( buildPhases = (
DAB7EB231BEF787300D2AD5E /* Sources */, DAB7EB231BEF787300D2AD5E /* Sources */,
DAB7EB241BEF787300D2AD5E /* Frameworks */, DAB7EB241BEF787300D2AD5E /* Frameworks */,
DAB7EB251BEF787300D2AD5E /* Resources */, DAB7EB251BEF787300D2AD5E /* Resources */,
DA9EA7DD1D0EC65B0021F650 /* Embed Frameworks */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
DA9EA7DC1D0EC65B0021F650 /* PBXTargetDependency */,
); );
name = TabletDemo; name = TableKitDemo;
productName = TabletDemo; productName = TabletDemo;
productReference = DAB7EB271BEF787300D2AD5E /* TabletDemo.app */; productReference = DAB7EB271BEF787300D2AD5E /* TableKitDemo.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
@ -147,39 +233,66 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0720; LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0700; LastUpgradeCheck = 1000;
ORGANIZATIONNAME = Tablet; ORGANIZATIONNAME = Tablet;
TargetAttributes = { TargetAttributes = {
DAB7EB261BEF787300D2AD5E = { DAB7EB261BEF787300D2AD5E = {
CreatedOnToolsVersion = 7.0.1; CreatedOnToolsVersion = 7.0.1;
DevelopmentTeam = Z48R734SJX; DevelopmentTeam = Z48R734SJX;
LastSwiftMigration = 1000;
}; };
}; };
}; };
buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */; buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TableKitDemo" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
Base, Base,
); );
mainGroup = DAB7EB1E1BEF787300D2AD5E; mainGroup = DAB7EB1E1BEF787300D2AD5E;
productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */; productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */;
projectDirPath = ""; projectDirPath = "";
projectReferences = (
{
ProductGroup = DA9EA7D11D0EC5C50021F650 /* Products */;
ProjectRef = DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */;
},
);
projectRoot = ""; projectRoot = "";
targets = ( targets = (
DAB7EB261BEF787300D2AD5E /* TabletDemo */, DAB7EB261BEF787300D2AD5E /* TableKitDemo */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXReferenceProxy section */
DA9EA7D61D0EC5C60021F650 /* TableKit.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = TableKit.framework;
remoteRef = DA9EA7D51D0EC5C60021F650 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
DA9EA7D81D0EC5C60021F650 /* TableKitTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = TableKitTests.xctest;
remoteRef = DA9EA7D71D0EC5C60021F650 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
DAB7EB251BEF787300D2AD5E /* Resources */ = { DAB7EB251BEF787300D2AD5E /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */, DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */,
5079ADE61FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib in Resources */,
DA5546661D15765900AA83EE /* NibTableViewCell.xib in Resources */,
DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */, DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */,
DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */, DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */,
); );
@ -192,13 +305,27 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAC2D5D41C9D3118009E9C19 /* MainViewController.swift in Sources */, DACB71761CC2D63D00432BD3 /* MainController.swift in Sources */,
DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */,
DA5546681D15771D00AA83EE /* NibCellsController.swift in Sources */,
DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */, DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */,
5079ADE31FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift in Sources */,
DA5546641D15762000AA83EE /* NibTableViewCell.swift in Sources */,
DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */,
DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DA9EA7DC1D0EC65B0021F650 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = TableKit;
targetProxy = DA9EA7DB1D0EC65B0021F650 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
DAB7EB371BEF787300D2AD5E /* Debug */ = { DAB7EB371BEF787300D2AD5E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
@ -208,13 +335,23 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -241,6 +378,7 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
@ -252,13 +390,23 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -277,6 +425,8 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
@ -284,37 +434,41 @@
DAB7EB3A1BEF787300D2AD5E /* Debug */ = { DAB7EB3A1BEF787300D2AD5E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = Resources/Info.plist; INFOPLIST_FILE = Resources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
DAB7EB3B1BEF787300D2AD5E /* Release */ = { DAB7EB3B1BEF787300D2AD5E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = Resources/Info.plist; INFOPLIST_FILE = Resources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_VERSION = 5.0;
}; };
name = Release; name = Release;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */ = { DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TableKitDemo" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
DAB7EB371BEF787300D2AD5E /* Debug */, DAB7EB371BEF787300D2AD5E /* Debug */,
@ -323,7 +477,7 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */ = { DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TableKitDemo" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
DAB7EB3A1BEF787300D2AD5E /* Debug */, DAB7EB3A1BEF787300D2AD5E /* Debug */,

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

19
Package.swift Normal file
View File

@ -0,0 +1,19 @@
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "TableKit",
products: [
.library(
name: "TableKit",
targets: ["TableKit"]),
],
targets: [
.target(
name: "TableKit",
path: "Sources")
]
)

344
README.md
View File

@ -1,194 +1,244 @@
#Tablet # TableKit
<p align="left"> <p align="left">
<a href="https://travis-ci.org/maxsokolov/tablet"><img src="https://travis-ci.org/maxsokolov/tablet.svg" alt="Build Status" /></a> <a href="https://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></a>
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift2-compatible-4BC51D.svg?style=flat" alt="Swift 2 compatible" /></a> <a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_5.1-compatible-4BC51D.svg?style=flat" alt="Swift 5.1 compatible" /></a>
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" /> <a href="https://github.com/Carthage/Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage compatible" /></a>
<a href="https://cocoapods.org/pods/tablet"><img src="https://img.shields.io/badge/pod-0.3.0-blue.svg" alt="CocoaPods compatible" /></a> <a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-2.11.0-blue.svg" alt="CocoaPods compatible" /></a>
<a href="https://raw.githubusercontent.com/maxsokolov/tablet/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a> <img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
<a href="https://raw.githubusercontent.com/maxsokolov/tablekit/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a>
</p> </p>
Tablet is a super lightweight yet powerful generic library that handles a complexity of UITableView's datasource and delegate methods in a Swift environment. Tablet's goal is to provide an easiest way to create complex table views. With Tablet you don't have to write a messy code of `switch` or `if` statements when you deal with bunch of different cells in different sections. TableKit is a super lightweight yet powerful generic library that allows you to build complex table views in a declarative type-safe manner.
It hides a complexity of `UITableViewDataSource` and `UITableViewDelegate` methods behind the scene, so your code will be look clean, easy to read and nice to maintain.
## Features # Features
- [x] Type-safe cells based on generics - [x] Type-safe generic cells
- [x] Functional programming style friendly
- [x] The easiest way to map your models or view models to cells - [x] The easiest way to map your models or view models to cells
- [x] Automatic cell registration*
- [x] Correctly handles autolayout cells with multiline labels - [x] Correctly handles autolayout cells with multiline labels
- [x] Chainable cell actions - [x] Chainable cell actions (select/deselect etc.)
- [x] Support cells created from code, xib, or storyboard - [x] Support cells created from code, xib, or storyboard
- [x] Automatic xib/classes registration - [x] Support different cells height calculation strategies
- [x] Support portrait and landscape orientations
- [x] No need to subclass - [x] No need to subclass
- [x] Extensibility - [x] Extensibility
- [x] Tests
That's almost all you need in your controller to build a bunch of cells in a section 😘: # Getting Started
An [example app](Demo) is included demonstrating TableKit's functionality.
## Basic usage
Create your rows:
```swift ```swift
TableConfigurableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3", "4", "5"]) import TableKit
let row1 = TableRow<StringTableViewCell>(item: "1")
let row2 = TableRow<IntTableViewCell>(item: 2)
let row3 = TableRow<UserTableViewCell>(item: User(name: "John Doe", rating: 5))
``` ```
Tablet respects cells reusability feature and built with performace in mind. See the Usage section to learn more. Put rows into section:
## Requirements
- iOS 8.0+
- Xcode 7.0+
## Installation
### CocoaPods
To integrate Tablet into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Tablet'
```
Then, run the following command:
```bash
$ pod install
```
## Usage
### Very basic
You may want to setup a very basic table view, without any custom cells. In that case simply use the `TableRowBuilder`.
```swift ```swift
import Tablet let section = TableSection(rows: [row1, row2, row3])
let rowBuilder = TableRowBuilder<User, UITableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { data -> Void in
data.cell?.textLabel?.text = data.item.username
data.cell?.detailTextLabel?.text = data.item.isActive ? "Active" : "Inactive"
}
let sectionBuilder = TableSectionBuilder(headerTitle: "Users", rows: [rowBuilder])
director = TableDirector(tableView: tableView)
director.appendSections(sectionBuilder)
``` ```
And setup your table:
### Type-safe configurable cells
Let's say you want to put your cell configuration logic into cell itself. Say you want to pass your view model (or even model) to your cell.
You could easily do this using the `TableConfigurableRowBuilder`. Your cell should respect the `ConfigurableCell` protocol as you may see in example below:
```swift ```swift
import Tablet let tableDirector = TableDirector(tableView: tableView)
tableDirector += section
```
Done. Your table is ready. Your cells have to conform to `ConfigurableCell` protocol:
```swift
class StringTableViewCell: UITableViewCell, ConfigurableCell {
class MyTableViewCell : UITableViewCell, ConfigurableCell { func configure(with string: String) {
textLabel?.text = string
}
}
typealias Item = User class UserTableViewCell: UITableViewCell, ConfigurableCell {
static func reusableIdentifier() -> String { static var estimatedHeight: CGFloat? {
return "reusable_id" return 100
}
static func estimatedHeight() -> Float {
return 255
} }
func configureWithItem(item: Item) { // item is user here // is not required to be implemented
// by default reuse id is equal to cell's class name
static var reuseIdentifier: String {
return "my id"
}
textLabel?.text = item.username func configure(with user: User) {
detailTextLabel?.text = item.isActive ? "Active" : "Inactive"
} textLabel?.text = user.name
detailTextLabel?.text = "Rating: \(user.rating)"
}
} }
``` ```
Once you've implemented the protocol, simply use the `TableConfigurableRowBuilder` to build cells: You could have as many rows and sections as you need.
## Row actions
It nice to have some actions that related to your cells:
```swift ```swift
import Tablet let action = TableRowAction<StringTableViewCell>(.click) { (options) in
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>() // you could access any useful information that relates to the action
rowBuilder.appendItems(users)
director = TableDirector(tableView: tableView) // options.cell - StringTableViewCell?
tableDirector.appendSection(TableSectionBuilder(rows: [rowBuilder])) // options.item - String
// options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]?
}
let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
``` ```
Or, using nice chaining approach:
### Cell actions
Tablet provides a chaining approach to handle actions from your cells:
```swift ```swift
import Tablet let row = TableRow<StringTableViewCell>(item: "some")
.on(.click) { (options) in
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { data -> Void in }
.on(.shouldHighlight) { (options) -> Bool in
} return false
.action(.click) { data -> Void in }
}
.action(.shouldHighlight) { data -> ReturnValue in
return false
}
``` ```
You could find all available actions [here](Sources/TableRowAction.swift).
### Custom cell actions ## Custom row actions
You are able to define your own actions:
```swift ```swift
import Tablet struct MyActions {
static let ButtonClicked = "ButtonClicked"
}
let kMyAction = "action_key" class MyTableViewCell: UITableViewCell, ConfigurableCell {
class MyTableViewCell : UITableViewCell { @IBAction func myButtonClicked(sender: UIButton) {
@IBAction func buttonClicked(sender: UIButton) { TableCellAction(key: MyActions.ButtonClicked, sender: self).invoke()
}
Action(key: kMyAction, sender: self, userInfo: nil).invoke()
}
} }
``` ```
And receive this actions with your row builder: And handle them accordingly:
```swift ```swift
import Tablet let myAction = TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)) { (options) in
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, id: "reusable_id")
.action(.click) { data -> Void in
}
.action(.willDisplay) { data -> Void in
}
.action(kMyAction) { data -> Void in
}
```
## Extensibility
If you find that Tablet is not provide an action you need, for example you need UITableViewDelegate's `didEndDisplayingCell` method and it's not out of the box,
simply provide an extension for `TableDirector` as follow:
```swift
import Tablet
let kTableDirectorDidEndDisplayingCell = "enddisplaycell"
extension TableDirector {
public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath)
}
} }
``` ```
Catch your action with row builder: ## Multiple actions with same type
It's also possible to use multiple actions with same type:
```swift ```swift
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users) let click1 = TableRowAction<StringTableViewCell>(.click) { (options) in }
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in click1.id = "click1" // optional
} let click2 = TableRowAction<StringTableViewCell>(.click) { (options) in }
click2.id = "click2" // optional
let row = TableRow<StringTableViewCell>(item: "some", actions: [click1, click2])
``` ```
You could also invoke an action that returns a value. Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.
> If you define multiple actions with same type which also return a value, only last return value will be used for table view.
## License You could also remove any action by id:
```swift
row.removeAction(forActionId: "action_id")
```
Tablet is available under the MIT license. See LICENSE for details. # Advanced
## Cell height calculating strategy
By default TableKit relies on <a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html" target="_blank">self-sizing cells</a>. In that case you have to provide an estimated height for your cells:
```swift
class StringTableViewCell: UITableViewCell, ConfigurableCell {
// ...
static var estimatedHeight: CGFloat? {
return 255
}
}
```
It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:
```swift
let tableDirector = TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)
```
It does all dirty work with prototypes for you [behind the scene](Sources/TablePrototypeCellHeightCalculator.swift), so you don't have to worry about anything except of your cell configuration:
```swift
class ImageTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with url: NSURL) {
loadImageAsync(url: url, imageView: imageView)
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
}
}
```
You have to additionally set `preferredMaxLayoutWidth` for all your multiline labels.
## Functional programming
It's never been so easy to deal with table views.
```swift
let users = /* some users array */
let click = TableRowAction<UserTableViewCell>(.click) {
}
let rows = users.filter({ $0.state == .active }).map({ TableRow<UserTableViewCell>(item: $0.name, actions: [click]) })
tableDirector += rows
```
Done, your table is ready.
## Automatic cell registration
TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:
```ruby
MyTableViewCell.swift
MyTableViewCell.xib
```
You can also turn off this behaviour:
```swift
let tableDirector = TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration: false)
```
and register your cell manually.
# Installation
## CocoaPods
To integrate TableKit into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
pod 'TableKit'
```
## Carthage
Add the line `github "maxsokolov/tablekit"` to your `Cartfile`.
## Manual
Clone the repo and drag files from `Sources` folder into your Xcode project.
# Requirements
- iOS 8.0
- Xcode 9.0
# Changelog
Keep an eye on [changes](CHANGELOG.md).
# License
TableKit is available under the MIT license. See LICENSE for details.

View File

@ -0,0 +1,57 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
public protocol ConfigurableCell {
associatedtype CellData
static var reuseIdentifier: String { get }
static var estimatedHeight: CGFloat? { get }
static var defaultHeight: CGFloat? { get }
static var layoutType: LayoutType { get }
func configure(with _: CellData)
}
public extension ConfigurableCell where Self: UITableViewCell {
static var reuseIdentifier: String {
return String(describing: self)
}
static var estimatedHeight: CGFloat? {
return nil
}
static var defaultHeight: CGFloat? {
return nil
}
static var layoutType: LayoutType {
return .auto
}
}

96
Sources/Expandable.swift Normal file
View File

@ -0,0 +1,96 @@
import UIKit
public extension TimeInterval {
static let defaultExpandableAnimationDuration: TimeInterval = 0.3
}
public protocol Expandable {
associatedtype ViewModelType: ExpandableCellViewModel
var viewModel: ViewModelType? { get }
func configure(state: ExpandableState)
}
extension Expandable where Self: UITableViewCell & ConfigurableCell {
public func initState() {
guard let viewModel = viewModel else {
return
}
changeState(expandableState: viewModel.expandableState)
}
private func changeState(expandableState: ExpandableState) {
// layout to get right frames, frame of bottom subview can be used to get expanded height
setNeedsLayout()
layoutIfNeeded()
// apply changes
configure(state: expandableState)
layoutIfNeeded()
}
public func toggleState(animated: Bool = true,
animationDuration: TimeInterval = .defaultExpandableAnimationDuration) {
guard let viewModel = viewModel,
let stateIndex = viewModel.availableStates.firstIndex(where: { $0 == viewModel.expandableState }) else {
return
}
let targetState = stateIndex == viewModel.availableStates.count - 1
? viewModel.availableStates[0]
: viewModel.availableStates[stateIndex + 1]
transition(to: targetState,
animated: animated,
animationDuration: animationDuration)
}
public func transition(to state: ExpandableState,
animated: Bool = true,
animationDuration: TimeInterval = .defaultExpandableAnimationDuration) {
guard let tableView = tableView,
let viewModel = viewModel,
viewModel.expandableState != state else {
return
}
let contentOffset = tableView.contentOffset
if animated {
UIView.animate(withDuration: animationDuration,
animations: { [weak self] in
self?.applyChanges(expandableState: state)
}, completion: { _ in
viewModel.expandableState = state
})
} else {
applyChanges(expandableState: state)
viewModel.expandableState = state
}
tableView.beginUpdates()
tableView.endUpdates()
tableView.setContentOffset(contentOffset, animated: false)
}
public func applyChanges(expandableState: ExpandableState) {
changeState(expandableState: expandableState)
if let indexPath = indexPath,
let tableDirector = (tableView?.delegate as? TableDirector),
let cellHeightCalculator = tableDirector.rowHeightCalculator as? ExpandableCellHeightCalculator {
cellHeightCalculator.updateCached(height: expandableState.height ?? height(layoutType: Self.layoutType), for: indexPath)
}
}
}

View File

@ -0,0 +1,55 @@
import UIKit
public final class ExpandableCellHeightCalculator: RowHeightCalculator {
private weak var tableView: UITableView?
private var prototypes = [String: UITableViewCell]()
private var cachedHeights = [IndexPath: CGFloat]()
public init(tableView: UITableView?) {
self.tableView = tableView
}
public func updateCached(height: CGFloat, for indexPath: IndexPath) {
cachedHeights[indexPath] = height
}
public func height(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
guard let tableView = tableView else {
return 0
}
if let height = cachedHeights[indexPath] {
return height
}
var prototypeCell = prototypes[row.reuseIdentifier]
if prototypeCell == nil {
prototypeCell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier)
prototypes[row.reuseIdentifier] = prototypeCell
}
guard let cell = prototypeCell else {
return 0
}
row.configure(cell)
cell.layoutIfNeeded()
let height = cell.height(layoutType: row.layoutType)
cachedHeights[indexPath] = height
return height
}
public func estimatedHeight(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
return height(forRow: row, at: indexPath)
}
public func invalidate() {
cachedHeights.removeAll()
}
}

View File

@ -0,0 +1,15 @@
public protocol ExpandableCellViewModel: AnyObject {
var expandableState: ExpandableState { get set }
var availableStates: [ExpandableState] { get }
}
public extension ExpandableCellViewModel {
var availableStates: [ExpandableState] {
return [.collapsed, .expanded]
}
}

View File

@ -0,0 +1,41 @@
import UIKit
public enum ExpandableState {
case collapsed
case expanded
case height(value: CGFloat)
}
extension ExpandableState: Equatable { }
extension ExpandableState {
public var isCollapsed: Bool {
guard case .collapsed = self else {
return false
}
return true
}
public var isExpanded: Bool {
guard case .expanded = self else {
return false
}
return true
}
public var height: CGFloat? {
guard case let .height(value: height) = self else {
return nil
}
return height
}
}

7
Sources/LayoutType.swift Normal file
View File

@ -0,0 +1,7 @@
public enum LayoutType {
case manual
case auto
}

View File

@ -18,7 +18,29 @@
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@import Foundation; // --
public func +=(left: TableDirector, right: TableSection) {
left.append(section: right)
}
FOUNDATION_EXPORT double TabletVersionNumber; public func +=(left: TableDirector, right: [TableSection]) {
FOUNDATION_EXPORT const unsigned char TabletVersionString[]; left.append(sections: right)
}
// --
public func +=(left: TableDirector, right: Row) {
left.append(sections: [TableSection(rows: [right])])
}
public func +=(left: TableDirector, right: [Row]) {
left.append(sections: [TableSection(rows: right)])
}
// --
public func +=(left: TableSection, right: Row) {
left.append(row: right)
}
public func +=(left: TableSection, right: [Row]) {
left.append(rows: right)
}

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
/**
A custom action that you can trigger from your cell.
You can easily catch actions using a chaining manner with your row.
*/
open class TableCellAction {
/// The cell that triggers an action.
public let cell: UITableViewCell
/// The action unique key.
public let key: String
/// The custom user info.
public let userInfo: [AnyHashable: Any]?
public init(key: String, sender: UITableViewCell, userInfo: [AnyHashable: Any]? = nil) {
self.key = key
self.cell = sender
self.userInfo = userInfo
}
open func invoke() {
NotificationCenter.default.post(name: Notification.Name(rawValue: TableKitNotifications.CellAction), object: self, userInfo: userInfo)
}
}

View File

@ -0,0 +1,58 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
class TableCellRegisterer {
private var registeredIds = Set<String>()
private weak var tableView: UITableView?
init(tableView: UITableView?) {
self.tableView = tableView
}
func register(cellType: AnyClass, forCellReuseIdentifier reuseIdentifier: String) {
if registeredIds.contains(reuseIdentifier) {
return
}
// check if cell is already registered, probably cell has been registered by storyboard
if tableView?.dequeueReusableCell(withIdentifier: reuseIdentifier) != nil {
registeredIds.insert(reuseIdentifier)
return
}
let bundle = Bundle(for: cellType)
// we hope that cell's xib file has name that equals to cell's class name
// in that case we could register nib
if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") {
tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier)
// otherwise, register cell class
} else {
tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier)
}
registeredIds.insert(reuseIdentifier)
}
}

475
Sources/TableDirector.swift Normal file
View File

@ -0,0 +1,475 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
/**
Responsible for table view's datasource and delegate.
*/
open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
open private(set) weak var tableView: UITableView?
open fileprivate(set) var sections = [TableSection]()
private weak var scrollDelegate: UIScrollViewDelegate?
private var cellRegisterer: TableCellRegisterer?
public private(set) var rowHeightCalculator: RowHeightCalculator?
private var sectionsIndexTitlesIndexes: [Int]?
@available(*, deprecated, message: "Produced incorrect behaviour")
open var shouldUsePrototypeCellHeightCalculation: Bool = false {
didSet {
if shouldUsePrototypeCellHeightCalculation {
rowHeightCalculator = TablePrototypeCellHeightCalculator(tableView: tableView)
} else {
rowHeightCalculator = nil
}
}
}
open var isEmpty: Bool {
return sections.isEmpty
}
public init(
tableView: UITableView,
scrollDelegate: UIScrollViewDelegate? = nil,
shouldUseAutomaticCellRegistration: Bool = true,
cellHeightCalculator: RowHeightCalculator?)
{
super.init()
if shouldUseAutomaticCellRegistration {
self.cellRegisterer = TableCellRegisterer(tableView: tableView)
}
self.rowHeightCalculator = cellHeightCalculator
self.scrollDelegate = scrollDelegate
self.tableView = tableView
self.tableView?.delegate = self
self.tableView?.dataSource = self
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveAction), name: NSNotification.Name(rawValue: TableKitNotifications.CellAction), object: nil)
}
public convenience init(
tableView: UITableView,
scrollDelegate: UIScrollViewDelegate? = nil,
shouldUseAutomaticCellRegistration: Bool = true,
shouldUsePrototypeCellHeightCalculation: Bool = false)
{
let heightCalculator: TablePrototypeCellHeightCalculator? = shouldUsePrototypeCellHeightCalculation
? TablePrototypeCellHeightCalculator(tableView: tableView)
: nil
self.init(
tableView: tableView,
scrollDelegate: scrollDelegate,
shouldUseAutomaticCellRegistration: shouldUseAutomaticCellRegistration,
cellHeightCalculator: heightCalculator
)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
open func reload() {
tableView?.reloadData()
}
// MARK: - Private
private func row(at indexPath: IndexPath) -> Row? {
if indexPath.section < sections.count && indexPath.row < sections[indexPath.section].rows.count {
return sections[indexPath.section].rows[indexPath.row]
}
return nil
}
// MARK: Public
@discardableResult
open func invoke(
action: TableRowActionType,
cell: UITableViewCell?, indexPath: IndexPath,
userInfo: [AnyHashable: Any]? = nil) -> Any?
{
guard let row = row(at: indexPath) else { return nil }
return row.invoke(
action: action,
cell: cell,
path: indexPath,
userInfo: userInfo
)
}
open override func responds(to selector: Selector) -> Bool {
return super.responds(to: selector) || scrollDelegate?.responds(to: selector) == true
}
open override func forwardingTarget(for selector: Selector) -> Any? {
return scrollDelegate?.responds(to: selector) == true
? scrollDelegate
: super.forwardingTarget(for: selector)
}
// MARK: - Internal
func hasAction(_ action: TableRowActionType, atIndexPath indexPath: IndexPath) -> Bool {
guard let row = row(at: indexPath) else { return false }
return row.has(action: action)
}
@objc
func didReceiveAction(_ notification: Notification) {
guard let action = notification.object as? TableCellAction, let indexPath = tableView?.indexPath(for: action.cell) else { return }
invoke(action: .custom(action.key), cell: action.cell, indexPath: indexPath, userInfo: notification.userInfo)
}
// MARK: - Height
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row]
if rowHeightCalculator != nil {
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
}
return row.defaultHeight
?? row.estimatedHeight
?? rowHeightCalculator?.estimatedHeight(forRow: row, at: indexPath)
?? UITableView.automaticDimension
}
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row]
if rowHeightCalculator != nil {
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
}
let rowHeight = invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat
return rowHeight
?? row.defaultHeight
?? rowHeightCalculator?.height(forRow: row, at: indexPath)
?? UITableView.automaticDimension
}
// MARK: UITableViewDataSource - configuration
open func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section < sections.count else { return 0 }
return sections[section].numberOfRows
}
open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = sections[indexPath.section].rows[indexPath.row]
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier, for: indexPath)
if cell.frame.size.width != tableView.frame.size.width {
cell.frame = CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: cell.frame.size.height)
cell.layoutIfNeeded()
}
row.configure(cell)
invoke(action: .configure, cell: cell, indexPath: indexPath)
return cell
}
// MARK: UITableViewDataSource - section setup
open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard section < sections.count else { return nil }
return sections[section].headerTitle
}
open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
guard section < sections.count else { return nil }
return sections[section].footerTitle
}
// MARK: UITableViewDelegate - section setup
open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard section < sections.count else { return nil }
return sections[section].headerView
}
open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard section < sections.count else { return nil }
return sections[section].footerView
}
open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard section < sections.count else { return 0 }
let section = sections[section]
return section.headerHeight ?? section.headerView?.frame.size.height ?? UITableView.automaticDimension
}
open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
guard section < sections.count else { return 0 }
let section = sections[section]
return section.footerHeight
?? section.footerView?.frame.size.height
?? UITableView.automaticDimension
}
// MARK: UITableViewDataSource - Index
public func sectionIndexTitles(for tableView: UITableView) -> [String]? {
var indexTitles = [String]()
var indexTitlesIndexes = [Int]()
sections.enumerated().forEach { index, section in
if let title = section.indexTitle {
indexTitles.append(title)
indexTitlesIndexes.append(index)
}
}
if !indexTitles.isEmpty {
sectionsIndexTitlesIndexes = indexTitlesIndexes
return indexTitles
}
sectionsIndexTitlesIndexes = nil
return nil
}
public func tableView(
_ tableView: UITableView,
sectionForSectionIndexTitle title: String,
at index: Int) -> Int
{
return sectionsIndexTitlesIndexes?[index] ?? 0
}
// MARK: UITableViewDelegate - actions
open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if invoke(action: .click, cell: cell, indexPath: indexPath) != nil {
tableView.deselectRow(at: indexPath, animated: true)
} else {
invoke(action: .select, cell: cell, indexPath: indexPath)
}
}
open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
invoke(action: .deselect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
}
open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
invoke(action: .willDisplay, cell: cell, indexPath: indexPath)
}
public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
invoke(action: .didEndDisplaying, cell: cell, indexPath: indexPath)
}
open func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return invoke(action: .shouldHighlight, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? true
}
open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if hasAction(.willSelect, atIndexPath: indexPath) {
return invoke(action: .willSelect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? IndexPath
}
return indexPath
}
open func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? {
if hasAction(.willDeselect, atIndexPath: indexPath) {
return invoke(action: .willDeselect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? IndexPath
}
return indexPath
}
@available(iOS 13.0, *)
open func tableView(
_ tableView: UITableView,
shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool
{
invoke(action: .shouldBeginMultipleSelection, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? false
}
@available(iOS 13.0, *)
open func tableView(
_ tableView: UITableView,
didBeginMultipleSelectionInteractionAt indexPath: IndexPath)
{
invoke(action: .didBeginMultipleSelection, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
}
@available(iOS 13.0, *)
open func tableView(
_ tableView: UITableView,
contextMenuConfigurationForRowAt indexPath: IndexPath,
point: CGPoint) -> UIContextMenuConfiguration?
{
invoke(action: .showContextMenu, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath, userInfo: [TableKitUserInfoKeys.ContextMenuInvokePoint: point]) as? UIContextMenuConfiguration
}
// MARK: - Row editing
open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath)
}
open func tableView(_ tableView: UITableView,
leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let currentRow = sections[indexPath.section].rows[indexPath.row]
let configuration = UISwipeActionsConfiguration(actions: currentRow.leadingContextualActions)
configuration.performsFirstActionWithFullSwipe = currentRow.performsFirstActionWithFullSwipe
return configuration
}
open func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let currentRow = sections[indexPath.section].rows[indexPath.row]
let configuration = UISwipeActionsConfiguration(actions: currentRow.trailingContextualActions)
configuration.performsFirstActionWithFullSwipe = currentRow.performsFirstActionWithFullSwipe
return configuration
}
open func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
if invoke(action: .canDelete, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? false {
return UITableViewCell.EditingStyle.delete
}
return UITableViewCell.EditingStyle.none
}
public func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
public func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
return invoke(action: .canMoveTo, cell: tableView.cellForRow(at: sourceIndexPath), indexPath: sourceIndexPath, userInfo: [TableKitUserInfoKeys.CellCanMoveProposedIndexPath: proposedDestinationIndexPath]) as? IndexPath ?? proposedDestinationIndexPath
}
open func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
invoke(action: .clickDelete, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
}
}
open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return invoke(action: .canMove, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? false
}
open func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
invoke(action: .move, cell: tableView.cellForRow(at: sourceIndexPath), indexPath: sourceIndexPath, userInfo: [TableKitUserInfoKeys.CellMoveDestinationIndexPath: destinationIndexPath])
}
open func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
invoke(action: .accessoryButtonTap, cell: cell, indexPath: indexPath)
}
}
// MARK: - Sections manipulation
extension TableDirector {
@discardableResult
public func append(section: TableSection) -> Self {
append(sections: [section])
return self
}
@discardableResult
public func append(sections: [TableSection]) -> Self {
self.sections.append(contentsOf: sections)
return self
}
@discardableResult
public func append(rows: [Row]) -> Self {
append(section: TableSection(rows: rows))
return self
}
@discardableResult
public func insert(section: TableSection, atIndex index: Int) -> Self {
sections.insert(section, at: index)
return self
}
@discardableResult
public func replaceSection(at index: Int, with section: TableSection) -> Self {
if index < sections.count {
sections[index] = section
}
return self
}
@discardableResult
public func delete(sectionAt index: Int) -> Self {
sections.remove(at: index)
return self
}
@discardableResult
public func remove(sectionAt index: Int) -> Self {
return delete(sectionAt: index)
}
@discardableResult
public func clear() -> Self {
rowHeightCalculator?.invalidate()
sections.removeAll()
return self
}
// MARK: - deprecated methods
@available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead")
@discardableResult
public func delete(index: Int) -> Self {
sections.remove(at: index)
return self
}
}

111
Sources/TableKit.swift Normal file
View File

@ -0,0 +1,111 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
struct TableKitNotifications {
static let CellAction = "TableKitNotificationsCellAction"
}
public struct TableKitUserInfoKeys {
public static let CellMoveDestinationIndexPath = "TableKitCellMoveDestinationIndexPath"
public static let CellCanMoveProposedIndexPath = "CellCanMoveProposedIndexPath"
public static let ContextMenuInvokePoint = "ContextMenuInvokePoint"
}
public protocol RowConfigurable {
func configure(_ cell: UITableViewCell)
}
public protocol RowActionable {
var leadingContextualActions: [UIContextualAction] { get }
var trailingContextualActions: [UIContextualAction] { get }
var performsFirstActionWithFullSwipe: Bool { get }
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
func invoke(
action: TableRowActionType,
cell: UITableViewCell?,
path: IndexPath,
userInfo: [AnyHashable: Any]?) -> Any?
func has(action: TableRowActionType) -> Bool
}
public protocol RowHashable {
var hashValue: Int { get }
}
public protocol Row: RowConfigurable, RowActionable, RowHashable {
var reuseIdentifier: String { get }
var cellType: AnyClass { get }
var layoutType: LayoutType { get }
var estimatedHeight: CGFloat? { get }
var defaultHeight: CGFloat? { get }
}
public enum TableRowActionType {
case click
case clickDelete
case select
case deselect
case willSelect
case willDeselect
case willDisplay
case didEndDisplaying
case shouldHighlight
case shouldBeginMultipleSelection
case didBeginMultipleSelection
case height
case canEdit
case configure
case canDelete
case canMove
case canMoveTo
case move
case showContextMenu
case accessoryButtonTap
case custom(String)
var key: String {
switch (self) {
case .custom(let key):
return key
default:
return "_\(self)"
}
}
}
public protocol RowHeightCalculator {
func height(forRow row: Row, at indexPath: IndexPath) -> CGFloat
func estimatedHeight(forRow row: Row, at indexPath: IndexPath) -> CGFloat
func invalidate()
}

View File

@ -0,0 +1,87 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
open class TablePrototypeCellHeightCalculator: RowHeightCalculator {
private(set) weak var tableView: UITableView?
private var prototypes = [String: UITableViewCell]()
private var cachedHeights = [Int: CGFloat]()
private var separatorHeight = 1 / UIScreen.main.scale
public init(tableView: UITableView?) {
self.tableView = tableView
}
open func height(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
guard let tableView = tableView else { return 0 }
let hash = row.hashValue ^ Int(tableView.bounds.size.width).hashValue
if let height = cachedHeights[hash] {
return height
}
var prototypeCell = prototypes[row.reuseIdentifier]
if prototypeCell == nil {
prototypeCell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier)
prototypes[row.reuseIdentifier] = prototypeCell
}
guard let cell = prototypeCell else { return 0 }
cell.prepareForReuse()
row.configure(cell)
cell.bounds = CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: cell.bounds.height)
cell.setNeedsLayout()
cell.layoutIfNeeded()
let height = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height + (tableView.separatorStyle != .none ? separatorHeight : 0)
cachedHeights[hash] = height
return height
}
open func estimatedHeight(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
guard let tableView = tableView else { return 0 }
let hash = row.hashValue ^ Int(tableView.bounds.size.width).hashValue
if let height = cachedHeights[hash] {
return height
}
if let estimatedHeight = row.estimatedHeight , estimatedHeight > 0 {
return estimatedHeight
}
return UITableView.automaticDimension
}
open func invalidate() {
cachedHeights.removeAll()
}
}

156
Sources/TableRow.swift Normal file
View File

@ -0,0 +1,156 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell {
public let item: CellType.CellData
private lazy var actions = [String: [TableRowAction<CellType>]]()
open var leadingContextualActions: [UIContextualAction] {
[]
}
open var trailingContextualActions: [UIContextualAction] {
[]
}
open var performsFirstActionWithFullSwipe: Bool {
false
}
open var hashValue: Int {
return ObjectIdentifier(self).hashValue
}
open var reuseIdentifier: String {
return CellType.reuseIdentifier
}
open var estimatedHeight: CGFloat? {
return CellType.estimatedHeight
}
open var defaultHeight: CGFloat? {
return CellType.defaultHeight
}
open var layoutType: LayoutType {
return CellType.layoutType
}
open var cellType: AnyClass {
return CellType.self
}
public init(item: CellType.CellData,
actions: [TableRowAction<CellType>]? = nil) {
self.item = item
actions?.forEach { on($0) }
}
// MARK: - RowConfigurable -
open func configure(_ cell: UITableViewCell) {
(cell as? CellType)?.configure(with: item)
}
// MARK: - RowActionable -
open func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? {
return actions[action.key]?.compactMap({ $0.invokeActionOn(cell: cell, item: item, path: path, userInfo: userInfo) }).last
}
open func has(action: TableRowActionType) -> Bool {
return actions[action.key] != nil
}
open func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool {
if actions[TableRowActionType.canEdit.key] != nil {
return invoke(action: .canEdit, cell: nil, path: indexPath) as? Bool ?? false
}
return !leadingContextualActions.isEmpty
|| !trailingContextualActions.isEmpty
|| actions[TableRowActionType.clickDelete.key] != nil
}
// MARK: - actions -
@discardableResult
open func on(_ action: TableRowAction<CellType>) -> Self {
if actions[action.type.key] == nil {
actions[action.type.key] = [TableRowAction<CellType>]()
}
actions[action.type.key]?.append(action)
return self
}
@discardableResult
open func on<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) -> Self {
return on(TableRowAction<CellType>(type, handler: handler))
}
@discardableResult
open func on(_ key: String, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> ()) -> Self {
return on(TableRowAction<CellType>(.custom(key), handler: handler))
}
open func removeAllActions() {
actions.removeAll()
}
open func removeAction(forActionId actionId: String) {
for (key, value) in actions {
if let actionIndex = value.firstIndex(where: { $0.id == actionId }) {
actions[key]?.remove(at: actionIndex)
}
}
}
// MARK: - deprecated actions -
@available(*, deprecated, message: "Use 'on' method instead")
@discardableResult
open func action(_ action: TableRowAction<CellType>) -> Self {
return on(action)
}
@available(*, deprecated, message: "Use 'on' method instead")
@discardableResult
open func action<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) -> Self {
return on(TableRowAction<CellType>(type, handler: handler))
}
}

View File

@ -0,0 +1,83 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
open class TableRowActionOptions<CellType: ConfigurableCell> where CellType: UITableViewCell {
public let item: CellType.CellData
public let cell: CellType?
public let indexPath: IndexPath
public let userInfo: [AnyHashable: Any]?
init(item: CellType.CellData, cell: CellType?, path: IndexPath, userInfo: [AnyHashable: Any]?) {
self.item = item
self.cell = cell
self.indexPath = path
self.userInfo = userInfo
}
}
private enum TableRowActionHandler<CellType: ConfigurableCell> where CellType: UITableViewCell {
case voidAction((TableRowActionOptions<CellType>) -> Void)
case action((TableRowActionOptions<CellType>) -> Any?)
func invoke(withOptions options: TableRowActionOptions<CellType>) -> Any? {
switch self {
case .voidAction(let handler):
return handler(options)
case .action(let handler):
return handler(options)
}
}
}
open class TableRowAction<CellType: ConfigurableCell> where CellType: UITableViewCell {
open var id: String?
public let type: TableRowActionType
private let handler: TableRowActionHandler<CellType>
public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) {
self.type = type
self.handler = .voidAction(handler)
}
public init(_ key: String, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) {
self.type = .custom(key)
self.handler = .voidAction(handler)
}
public init<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) {
self.type = type
self.handler = .action(handler)
}
public func invokeActionOn(cell: UITableViewCell?, item: CellType.CellData, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? {
return handler.invoke(withOptions: TableRowActionOptions(item: item, cell: cell as? CellType, path: path, userInfo: userInfo))
}
}

110
Sources/TableSection.swift Normal file
View File

@ -0,0 +1,110 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
open class TableSection {
open private(set) var rows = [Row]()
open var headerTitle: String?
open var footerTitle: String?
open var indexTitle: String?
open var headerView: UIView?
open var footerView: UIView?
open var headerHeight: CGFloat? = nil
open var footerHeight: CGFloat? = nil
open var numberOfRows: Int {
return rows.count
}
open var isEmpty: Bool {
return rows.isEmpty
}
public init(rows: [Row]? = nil) {
if let initialRows = rows {
self.rows.append(contentsOf: initialRows)
}
}
public convenience init(headerTitle: String?, footerTitle: String?, rows: [Row]? = nil) {
self.init(rows: rows)
self.headerTitle = headerTitle
self.footerTitle = footerTitle
}
public convenience init(headerView: UIView?, footerView: UIView?, rows: [Row]? = nil) {
self.init(rows: rows)
self.headerView = headerView
self.footerView = footerView
}
// MARK: - Public -
open func clear() {
rows.removeAll()
}
open func append(row: Row) {
append(rows: [row])
}
open func append(rows: [Row]) {
self.rows.append(contentsOf: rows)
}
open func insert(row: Row, at index: Int) {
rows.insert(row, at: index)
}
open func insert(rows: [Row], at index: Int) {
self.rows.insert(contentsOf: rows, at: index)
}
open func replace(rowAt index: Int, with row: Row) {
rows[index] = row
}
open func swap(from: Int, to: Int) {
rows.swapAt(from, to)
}
open func delete(rowAt index: Int) {
rows.remove(at: index)
}
open func remove(rowAt index: Int) {
rows.remove(at: index)
}
// MARK: - deprecated methods -
@available(*, deprecated, message: "Use 'delete(rowAt:)' method instead")
open func delete(index: Int) {
rows.remove(at: index)
}
}

View File

@ -0,0 +1,32 @@
import UIKit
extension UITableViewCell {
var tableView: UITableView? {
var view = superview
while view != nil && !(view is UITableView) {
view = view?.superview
}
return view as? UITableView
}
var indexPath: IndexPath? {
guard let indexPath = tableView?.indexPath(for: self) else {
return nil
}
return indexPath
}
public func height(layoutType: LayoutType) -> CGFloat {
switch layoutType {
case .auto:
return contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
case .manual:
return contentView.subviews.map { $0.frame.maxY }.max() ?? 0
}
}
}

17
TableKit.podspec Normal file
View File

@ -0,0 +1,17 @@
Pod::Spec.new do |s|
s.name = 'TableKit'
s.module_name = 'TableKit'
s.version = '2.12'
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/TableKit'
s.summary = 'Type-safe declarative table views with Swift.'
s.author = { 'Max Sokolov' => 'i@maxsokolov.net' }
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.platforms = { :ios => '12.0' }
s.ios.deployment_target = '12.0'
s.source_files = 'Sources/*.swift'
s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/TableKit.git', :tag => s.version }
end

View File

@ -0,0 +1,502 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2CBFA2F521F692F100147B56 /* ExpandableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CBFA2F421F692F100147B56 /* ExpandableState.swift */; };
3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */; };
3201E78621BE9E25001DF9E7 /* UITableViewCell+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */; };
3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78721BE9EB2001DF9E7 /* Expandable.swift */; };
3201E78A21BE9ED4001DF9E7 /* ExpandableCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */; };
32BDFE9F21C167F400D0BBB4 /* LayoutType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */; };
50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */; };
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E858571DB153F500A9AA55 /* TableKit.swift */; };
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */; };
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */; };
DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A81D0EC2C90021F650 /* Operators.swift */; };
DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */; };
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */; };
DA9EA7B41D0EC2C90021F650 /* TableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */; };
DA9EA7B51D0EC2C90021F650 /* TableRowAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */; };
DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */; };
DA9EA7C91D0EC45F0021F650 /* TableKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9EA7561D0B679A0021F650 /* TableKit.framework */; };
DA9EA7CF1D0EC4930021F650 /* TableKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7BE1D0EC41D0021F650 /* TableKitTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DA9EA7CA1D0EC45F0021F650 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA9EA74D1D0B679A0021F650 /* Project object */;
proxyType = 1;
remoteGlobalIDString = DA9EA7551D0B679A0021F650;
remoteInfo = TableKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
2CBFA2F421F692F100147B56 /* ExpandableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableState.swift; sourceTree = "<group>"; };
3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableCellHeightCalculator.swift; sourceTree = "<group>"; };
3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extensions.swift"; sourceTree = "<group>"; };
3201E78721BE9EB2001DF9E7 /* Expandable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Expandable.swift; sourceTree = "<group>"; };
3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableCellViewModel.swift; sourceTree = "<group>"; };
32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutType.swift; sourceTree = "<group>"; };
50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellRegisterer.swift; sourceTree = "<group>"; };
50E858571DB153F500A9AA55 /* TableKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableKit.swift; sourceTree = "<group>"; };
DA9EA7561D0B679A0021F650 /* TableKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = "<group>"; };
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TablePrototypeCellHeightCalculator.swift; sourceTree = "<group>"; };
DA9EA7A81D0EC2C90021F650 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellAction.swift; sourceTree = "<group>"; };
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRow.swift; sourceTree = "<group>"; };
DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowAction.swift; sourceTree = "<group>"; };
DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSection.swift; sourceTree = "<group>"; };
DA9EA7B91D0EC34E0021F650 /* TableKit.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TableKit.plist; sourceTree = "<group>"; };
DA9EA7BA1D0EC34E0021F650 /* TableKitTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TableKitTests.plist; sourceTree = "<group>"; };
DA9EA7BE1D0EC41D0021F650 /* TableKitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableKitTests.swift; sourceTree = "<group>"; };
DA9EA7C41D0EC45F0021F650 /* TableKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DA9EA7521D0B679A0021F650 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DA9EA7C11D0EC45F0021F650 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DA9EA7C91D0EC45F0021F650 /* TableKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DA9EA74C1D0B679A0021F650 = {
isa = PBXGroup;
children = (
DA9EA7B81D0EC31B0021F650 /* Configs */,
DA9EA7571D0B679A0021F650 /* Products */,
DA9EA7A51D0EC2B90021F650 /* Sources */,
DA9EA7BD1D0EC3D70021F650 /* Tests */,
);
sourceTree = "<group>";
};
DA9EA7571D0B679A0021F650 /* Products */ = {
isa = PBXGroup;
children = (
DA9EA7561D0B679A0021F650 /* TableKit.framework */,
DA9EA7C41D0EC45F0021F650 /* TableKitTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
DA9EA7A51D0EC2B90021F650 /* Sources */ = {
isa = PBXGroup;
children = (
DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */,
3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */,
DA9EA7A81D0EC2C90021F650 /* Operators.swift */,
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */,
50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */,
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */,
50E858571DB153F500A9AA55 /* TableKit.swift */,
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */,
DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */,
DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */,
DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */,
3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */,
3201E78721BE9EB2001DF9E7 /* Expandable.swift */,
3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */,
32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */,
2CBFA2F421F692F100147B56 /* ExpandableState.swift */,
);
path = Sources;
sourceTree = "<group>";
};
DA9EA7B81D0EC31B0021F650 /* Configs */ = {
isa = PBXGroup;
children = (
DA9EA7B91D0EC34E0021F650 /* TableKit.plist */,
DA9EA7BA1D0EC34E0021F650 /* TableKitTests.plist */,
);
path = Configs;
sourceTree = "<group>";
};
DA9EA7BD1D0EC3D70021F650 /* Tests */ = {
isa = PBXGroup;
children = (
DA9EA7BE1D0EC41D0021F650 /* TableKitTests.swift */,
);
path = Tests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
DA9EA7531D0B679A0021F650 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
DA9EA7551D0B679A0021F650 /* TableKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA9EA75E1D0B679A0021F650 /* Build configuration list for PBXNativeTarget "TableKit" */;
buildPhases = (
DA9EA7511D0B679A0021F650 /* Sources */,
DA9EA7521D0B679A0021F650 /* Frameworks */,
DA9EA7531D0B679A0021F650 /* Headers */,
DA9EA7541D0B679A0021F650 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TableKit;
productName = TableKit;
productReference = DA9EA7561D0B679A0021F650 /* TableKit.framework */;
productType = "com.apple.product-type.framework";
};
DA9EA7C31D0EC45F0021F650 /* TableKitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA9EA7CC1D0EC45F0021F650 /* Build configuration list for PBXNativeTarget "TableKitTests" */;
buildPhases = (
DA9EA7C01D0EC45F0021F650 /* Sources */,
DA9EA7C11D0EC45F0021F650 /* Frameworks */,
DA9EA7C21D0EC45F0021F650 /* Resources */,
);
buildRules = (
);
dependencies = (
DA9EA7CB1D0EC45F0021F650 /* PBXTargetDependency */,
);
name = TableKitTests;
productName = TableKitTests;
productReference = DA9EA7C41D0EC45F0021F650 /* TableKitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DA9EA74D1D0B679A0021F650 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = "Max Sokolov";
TargetAttributes = {
DA9EA7551D0B679A0021F650 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 1000;
};
DA9EA7C31D0EC45F0021F650 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 1000;
};
};
};
buildConfigurationList = DA9EA7501D0B679A0021F650 /* Build configuration list for PBXProject "TableKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = DA9EA74C1D0B679A0021F650;
productRefGroup = DA9EA7571D0B679A0021F650 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DA9EA7551D0B679A0021F650 /* TableKit */,
DA9EA7C31D0EC45F0021F650 /* TableKitTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
DA9EA7541D0B679A0021F650 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DA9EA7C21D0EC45F0021F650 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DA9EA7511D0B679A0021F650 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3201E78A21BE9ED4001DF9E7 /* ExpandableCellViewModel.swift in Sources */,
50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */,
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */,
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */,
3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */,
2CBFA2F521F692F100147B56 /* ExpandableState.swift in Sources */,
DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */,
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */,
3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */,
DA9EA7B51D0EC2C90021F650 /* TableRowAction.swift in Sources */,
DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */,
32BDFE9F21C167F400D0BBB4 /* LayoutType.swift in Sources */,
3201E78621BE9E25001DF9E7 /* UITableViewCell+Extensions.swift in Sources */,
DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */,
DA9EA7B41D0EC2C90021F650 /* TableRow.swift in Sources */,
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA9EA7C01D0EC45F0021F650 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DA9EA7CF1D0EC4930021F650 /* TableKitTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DA9EA7CB1D0EC45F0021F650 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA9EA7551D0B679A0021F650 /* TableKit */;
targetProxy = DA9EA7CA1D0EC45F0021F650 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
DA9EA75C1D0B679A0021F650 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
DA9EA75D1D0B679A0021F650 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
DA9EA75F1D0B679A0021F650 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Configs/TableKit.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
DA9EA7601D0B679A0021F650 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = "$(SRCROOT)/Configs/TableKit.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
DA9EA7CD1D0EC45F0021F650 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = Configs/TableKitTests.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
DA9EA7CE1D0EC45F0021F650 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = Configs/TableKitTests.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DA9EA7501D0B679A0021F650 /* Build configuration list for PBXProject "TableKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA9EA75C1D0B679A0021F650 /* Debug */,
DA9EA75D1D0B679A0021F650 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DA9EA75E1D0B679A0021F650 /* Build configuration list for PBXNativeTarget "TableKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA9EA75F1D0B679A0021F650 /* Debug */,
DA9EA7601D0B679A0021F650 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DA9EA7CC1D0EC45F0021F650 /* Build configuration list for PBXNativeTarget "TableKitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA9EA7CD1D0EC45F0021F650 /* Debug */,
DA9EA7CE1D0EC45F0021F650 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = DA9EA74D1D0B679A0021F650 /* Project object */;
}

View File

@ -2,6 +2,6 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "self:Tablet.xcodeproj"> location = "self:">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0720" LastUpgradeVersion = "1000"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -14,10 +14,10 @@
buildForAnalyzing = "YES"> buildForAnalyzing = "YES">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19" BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
BuildableName = "Tablet.framework" BuildableName = "TableKit.framework"
BlueprintName = "Tablet" BlueprintName = "TableKit"
ReferencedContainer = "container:Tablet.xcodeproj"> ReferencedContainer = "container:TableKit.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
@ -32,20 +32,20 @@
skipped = "NO"> skipped = "NO">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6721C9D743D009E9C19" BlueprintIdentifier = "DA9EA7C31D0EC45F0021F650"
BuildableName = "TabletTests.xctest" BuildableName = "TableKitTests.xctest"
BlueprintName = "TabletTests" BlueprintName = "TableKitTests"
ReferencedContainer = "container:Tablet.xcodeproj"> ReferencedContainer = "container:TableKit.xcodeproj">
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19" BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
BuildableName = "Tablet.framework" BuildableName = "TableKit.framework"
BlueprintName = "Tablet" BlueprintName = "TableKit"
ReferencedContainer = "container:Tablet.xcodeproj"> ReferencedContainer = "container:TableKit.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <AdditionalOptions>
@ -64,10 +64,10 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19" BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
BuildableName = "Tablet.framework" BuildableName = "TableKit.framework"
BlueprintName = "Tablet" BlueprintName = "TableKit"
ReferencedContainer = "container:Tablet.xcodeproj"> ReferencedContainer = "container:TableKit.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <AdditionalOptions>
@ -82,10 +82,10 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19" BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
BuildableName = "Tablet.framework" BuildableName = "TableKit.framework"
BlueprintName = "Tablet" BlueprintName = "TableKit"
ReferencedContainer = "container:Tablet.xcodeproj"> ReferencedContainer = "container:TableKit.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
</ProfileAction> </ProfileAction>

View File

@ -1,16 +0,0 @@
Pod::Spec.new do |s|
s.name = 'Tablet'
s.version = '0.3.0'
s.homepage = 'https://github.com/maxsokolov/tablet'
s.summary = 'Powerful type-safe tool for UITableView. Swift 2.0 is required.'
s.author = { 'Max Sokolov' => 'i@maxsokolov.net' }
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.platforms = { :ios => '8.0' }
s.ios.deployment_target = '8.0'
s.source_files = 'Tablet/*.swift'
s.module_name = 'Tablet'
s.source = { :git => 'https://github.com/maxsokolov/tablet.git', :tag => s.version }
end

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Tablet/Tablet.xcodeproj">
</FileRef>
<FileRef
location = "group:TabletDemo/TabletDemo.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,224 +0,0 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
/**
Responsible for table view's datasource and delegate.
*/
public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
public private(set) weak var tableView: UITableView!
public weak var scrollDelegate: UIScrollViewDelegate?
private var sections = [TableSectionBuilder]()
public init(tableView: UITableView) {
super.init()
self.tableView = tableView
self.tableView.delegate = self
self.tableView.dataSource = self
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: TabletNotifications.CellAction, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
// MARK: Private methods
/**
Find a row builder that responsible for building a row from cell with given item type.
- Parameters:
- indexPath: path of cell to dequeue
- Returns: A touple - (builder, builderItemIndex)
*/
private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) {
return sections[indexPath.section].builderAtIndex(indexPath.row)!
}
// MARK: Public
public func invokeAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? {
let builder = builderAtIndexPath(indexPath)
return builder.0.invokeAction(action, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil)
}
public override func respondsToSelector(selector: Selector) -> Bool {
return super.respondsToSelector(selector) || scrollDelegate?.respondsToSelector(selector) == true
}
public override func forwardingTargetForSelector(selector: Selector) -> AnyObject? {
return scrollDelegate?.respondsToSelector(selector) == true ? scrollDelegate : super.forwardingTargetForSelector(selector)
}
// MARK: Internal
func didReceiveAction(notification: NSNotification) {
if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) {
let builder = builderAtIndexPath(indexPath)
builder.0.invokeAction(.custom(action.key), cell: action.cell, indexPath: indexPath, itemIndex: builder.1, userInfo: notification.userInfo)
}
}
}
public extension TableDirector {
// MARK: UITableViewDataSource - configuration
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].numberOfRowsInSection
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let builder = builderAtIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath)
if cell.frame.size.width != tableView.frame.size.width {
cell.frame = CGRectMake(0, 0, tableView.frame.size.width, cell.frame.size.height)
cell.layoutIfNeeded()
}
builder.0.invokeAction(.configure, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil)
return cell
}
}
public extension TableDirector {
// MARK: UITableViewDataSource - section setup
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
}
func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footerTitle
}
// MARK: UITableViewDelegate - section setup
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sections[section].headerView
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return sections[section].footerView
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return sections[section].headerHeight
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return sections[section].footerHeight
}
}
public extension TableDirector {
// MARK: UITableViewDelegate - actions
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CGFloat(builderAtIndexPath(indexPath).0.estimatedRowHeight)
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return invokeAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
}
/*func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
return invokeAction(.willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath
}*/
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if invokeAction(.click, cell: cell, indexPath: indexPath) != nil {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
} else {
invokeAction(.select, cell: cell, indexPath: indexPath)
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.willDisplay, cell: cell, indexPath: indexPath)
}
func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return invokeAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
}
}
public extension TableDirector {
// MARK: Sections manipulation
public func appendSection(section: TableSectionBuilder) {
appendSections([section])
}
public func appendSections(sections: [TableSectionBuilder]) {
sections.forEach { $0.willMoveToDirector(tableView) }
self.sections.appendContentsOf(sections)
}
public func clearSections() {
sections.removeAll()
}
}
public func +=(left: TableDirector, right: RowBuilder) {
left.appendSection(TableSectionBuilder(rows: [right]))
}
public func +=(left: TableDirector, right: [RowBuilder]) {
left.appendSection(TableSectionBuilder(rows: right))
}
public func +=(left: TableDirector, right: TableSectionBuilder) {
left.appendSection(right)
}
public func +=(left: TableDirector, right: [TableSectionBuilder]) {
left.appendSections(right)
}

View File

@ -1,191 +0,0 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
public typealias ReturnValue = AnyObject?
enum ActionHandler<I, C> {
case actionBlock((data: ActionData<I, C>) -> Void)
case actionReturnBlock((data: ActionData<I, C>) -> AnyObject?)
func invoke(data: ActionData<I, C>) -> ReturnValue {
switch (self) {
case .actionBlock(let closure):
closure(data: data)
return true
case .actionReturnBlock(let closure):
return closure(data: data)
}
}
}
public class RowBuilder : NSObject {
public private(set) var reusableIdentifier: String
public var numberOfRows: Int {
return 0
}
public var estimatedRowHeight: Float {
return 44
}
init(id: String) {
reusableIdentifier = id
}
// MARK: internal methods, must be overriden in subclass
func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
return nil
}
func registerCell(inTableView tableView: UITableView) {
}
}
/**
Responsible for building cells of given type and passing items to them.
*/
public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
private var actions = Dictionary<String, ActionHandler<I, C>>()
private var items = [I]()
public override var numberOfRows: Int {
return items.count
}
public init(item: I, id: String? = nil) {
super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "")
items.append(item)
}
public init(items: [I]? = nil, id: String? = nil) {
super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "")
if items != nil {
self.items.appendContentsOf(items!)
}
}
// MARK: Chaining actions
public func action(key: String, closure: (data: ActionData<I, C>) -> Void) -> Self {
actions[key] = .actionBlock(closure)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> Void) -> Self {
actions[actionType.key] = .actionBlock(closure)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> ReturnValue) -> Self {
actions[actionType.key] = .actionReturnBlock(closure)
return self
}
// MARK: Internal
override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if let action = actions[actionType.key] {
return action.invoke(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex, userInfo: userInfo))
}
return nil
}
override func registerCell(inTableView tableView: UITableView) {
if tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) != nil {
return
}
guard let resource = NSStringFromClass(C).componentsSeparatedByString(".").last else { return }
let bundle = NSBundle(forClass: C.self)
if let _ = bundle.pathForResource(resource, ofType: "nib") { // existing cell
tableView.registerNib(UINib(nibName: resource, bundle: bundle), forCellReuseIdentifier: reusableIdentifier)
} else {
tableView.registerClass(C.self, forCellReuseIdentifier: reusableIdentifier)
}
}
}
/**
Responsible for building configurable cells of given type and passing items to them.
*/
public class TableConfigurableRowBuilder<I, C: ConfigurableCell where C.Item == I, C: UITableViewCell> : TableRowBuilder<I, C> {
public override var estimatedRowHeight: Float {
return C.estimatedHeight()
}
public init(item: I) {
super.init(item: item, id: C.reusableIdentifier())
}
public init(items: [I]? = nil) {
super.init(items: items, id: C.reusableIdentifier())
}
override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
switch actionType {
case .configure:
(cell as? C)?.configureWithItem(items[itemIndex])
default: break
}
return super.invokeAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
}
public extension TableRowBuilder {
// MARK: Items manipulation
public func appendItems(items: [I]) {
self.items.appendContentsOf(items)
}
public func clear() {
items.removeAll()
}
}
public func +=<I, C>(left: TableRowBuilder<I, C>, right: I) {
left.appendItems([right])
}
public func +=<I, C>(left: TableRowBuilder<I, C>, right: [I]) {
left.appendItems(right)
}

View File

@ -1,109 +0,0 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
/**
Responsible for building a certain table view section.
Can host several row builders.
*/
public class TableSectionBuilder {
weak var tableView: UITableView?
private var builders = [RowBuilder]()
public var headerTitle: String?
public var footerTitle: String?
public var headerView: UIView?
public var headerHeight: CGFloat = UITableViewAutomaticDimension
public var footerView: UIView?
public var footerHeight: CGFloat = UITableViewAutomaticDimension
/// A total number of rows in section of each row builder.
public var numberOfRowsInSection: Int {
return builders.reduce(0) { $0 + $1.numberOfRows }
}
public init(headerTitle: String? = nil, footerTitle: String? = nil, rows: [RowBuilder]? = nil) {
self.headerTitle = headerTitle
self.footerTitle = footerTitle
if let initialRows = rows {
builders.appendContentsOf(initialRows)
}
}
public init(headerView: UIView? = nil, headerHeight: CGFloat = UITableViewAutomaticDimension, footerView: UIView? = nil, footerHeight: CGFloat = UITableViewAutomaticDimension) {
self.headerView = headerView
self.headerHeight = headerHeight
self.footerView = footerView
self.footerHeight = footerHeight
}
// MARK: Public
public func clear() {
builders.removeAll()
}
public func appendRow(row: RowBuilder) {
appendRows([row])
}
public func appendRows(rowBuilders: [RowBuilder]) {
if let tableView = tableView { rowBuilders.forEach { $0.registerCell(inTableView: tableView) } }
builders.appendContentsOf(rowBuilders)
}
// MARK: Internal
func builderAtIndex(var index: Int) -> (RowBuilder, Int)? {
for builder in builders {
if index < builder.numberOfRows {
return (builder, index)
}
index -= builder.numberOfRows
}
return nil
}
func willMoveToDirector(tableView: UITableView) {
self.tableView = tableView
self.builders.forEach { $0.registerCell(inTableView: tableView) }
}
}
public func +=(left: TableSectionBuilder, right: RowBuilder) {
left.appendRow(right)
}
public func +=(left: TableSectionBuilder, right: [RowBuilder]) {
left.appendRows(right)
}

View File

@ -1,117 +0,0 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
struct TabletNotifications {
static let CellAction = "TabletNotificationsCellAction"
}
/**
The actions that Tablet provides.
*/
public enum ActionType {
case click
case select
case deselect
case willSelect
case configure
case willDisplay
case shouldHighlight
case height
case custom(String)
var key: String {
switch (self) {
case .custom(let key):
return key
default:
return "_\(self)"
}
}
}
public class ActionData<I, C> {
public let cell: C?
public let item: I
public let itemIndex: Int
public let indexPath: NSIndexPath
public let userInfo: [NSObject: AnyObject]?
init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int, userInfo: [NSObject: AnyObject]?) {
self.cell = cell
self.indexPath = indexPath
self.item = item
self.itemIndex = itemIndex
self.userInfo = userInfo
}
}
/**
A custom action that you can trigger from your cell.
You can eacily catch actions using a chaining manner with your row builder.
*/
public class Action {
/// The cell that triggers an action.
public let cell: UITableViewCell
/// The action unique key.
public let key: String
/// The custom user info.
public let userInfo: [NSObject: AnyObject]?
public init(key: String, sender: UITableViewCell, userInfo: [NSObject: AnyObject]? = nil) {
self.key = key
self.cell = sender
self.userInfo = userInfo
}
public func invoke() {
NSNotificationCenter.defaultCenter().postNotificationName(TabletNotifications.CellAction, object: self, userInfo: userInfo)
}
}
/**
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
*/
public protocol ConfigurableCell {
typealias Item
static func reusableIdentifier() -> String
static func estimatedHeight() -> Float
func configureWithItem(item: Item)
}
public extension ConfigurableCell where Self: UITableViewCell {
static func reusableIdentifier() -> String {
return NSStringFromClass(self).componentsSeparatedByString(".").last ?? ""
}
}

View File

@ -1,423 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
DAC2D6741C9D743D009E9C19 /* Tablet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC2D6691C9D743D009E9C19 /* Tablet.framework */; };
DAC2D6871C9D7517009E9C19 /* Tablet.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2D6851C9D7517009E9C19 /* Tablet.h */; settings = {ATTRIBUTES = (Public, ); }; };
DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */; };
DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */; };
DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */; };
DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68F1C9D799E009E9C19 /* Tablet.swift */; };
DAC2D6991C9D7A3F009E9C19 /* TabletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DAC2D6751C9D743D009E9C19 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DAC2D6601C9D743D009E9C19 /* Project object */;
proxyType = 1;
remoteGlobalIDString = DAC2D6681C9D743D009E9C19;
remoteInfo = Tablet;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
DAC2D6691C9D743D009E9C19 /* Tablet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Tablet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TabletTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DAC2D6841C9D7517009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAC2D6851C9D7517009E9C19 /* Tablet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tablet.h; sourceTree = "<group>"; };
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = "<group>"; };
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = "<group>"; };
DAC2D6951C9D7A3B009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabletTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DAC2D6651C9D743D009E9C19 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC2D6701C9D743D009E9C19 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DAC2D6741C9D743D009E9C19 /* Tablet.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DAC2D65F1C9D743D009E9C19 = {
isa = PBXGroup;
children = (
DAC2D68B1C9D7990009E9C19 /* Classes */,
DAC2D6941C9D7A03009E9C19 /* Tests */,
DAC2D6831C9D74EE009E9C19 /* Supporting Files */,
DAC2D66A1C9D743D009E9C19 /* Products */,
);
sourceTree = "<group>";
};
DAC2D66A1C9D743D009E9C19 /* Products */ = {
isa = PBXGroup;
children = (
DAC2D6691C9D743D009E9C19 /* Tablet.framework */,
DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
DAC2D6831C9D74EE009E9C19 /* Supporting Files */ = {
isa = PBXGroup;
children = (
DAC2D6841C9D7517009E9C19 /* Info.plist */,
DAC2D6851C9D7517009E9C19 /* Tablet.h */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
DAC2D68B1C9D7990009E9C19 /* Classes */ = {
isa = PBXGroup;
children = (
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */,
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */,
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */,
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */,
);
name = Classes;
sourceTree = "<group>";
};
DAC2D6941C9D7A03009E9C19 /* Tests */ = {
isa = PBXGroup;
children = (
DAC2D6951C9D7A3B009E9C19 /* Info.plist */,
DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */,
);
name = Tests;
path = ../Tests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
DAC2D6661C9D743D009E9C19 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
DAC2D6871C9D7517009E9C19 /* Tablet.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
DAC2D6681C9D743D009E9C19 /* Tablet */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAC2D67D1C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "Tablet" */;
buildPhases = (
DAC2D6641C9D743D009E9C19 /* Sources */,
DAC2D6651C9D743D009E9C19 /* Frameworks */,
DAC2D6661C9D743D009E9C19 /* Headers */,
DAC2D6671C9D743D009E9C19 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Tablet;
productName = Tablet;
productReference = DAC2D6691C9D743D009E9C19 /* Tablet.framework */;
productType = "com.apple.product-type.framework";
};
DAC2D6721C9D743D009E9C19 /* TabletTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAC2D6801C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "TabletTests" */;
buildPhases = (
DAC2D66F1C9D743D009E9C19 /* Sources */,
DAC2D6701C9D743D009E9C19 /* Frameworks */,
DAC2D6711C9D743D009E9C19 /* Resources */,
);
buildRules = (
);
dependencies = (
DAC2D6761C9D743D009E9C19 /* PBXTargetDependency */,
);
name = TabletTests;
productName = TabletTests;
productReference = DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DAC2D6601C9D743D009E9C19 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = Tablet;
TargetAttributes = {
DAC2D6681C9D743D009E9C19 = {
CreatedOnToolsVersion = 7.2;
};
DAC2D6721C9D743D009E9C19 = {
CreatedOnToolsVersion = 7.2;
};
};
};
buildConfigurationList = DAC2D6631C9D743D009E9C19 /* Build configuration list for PBXProject "Tablet" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = DAC2D65F1C9D743D009E9C19;
productRefGroup = DAC2D66A1C9D743D009E9C19 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DAC2D6681C9D743D009E9C19 /* Tablet */,
DAC2D6721C9D743D009E9C19 /* TabletTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
DAC2D6671C9D743D009E9C19 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC2D6711C9D743D009E9C19 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DAC2D6641C9D743D009E9C19 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */,
DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */,
DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */,
DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC2D66F1C9D743D009E9C19 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DAC2D6991C9D7A3F009E9C19 /* TabletTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DAC2D6761C9D743D009E9C19 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAC2D6681C9D743D009E9C19 /* Tablet */;
targetProxy = DAC2D6751C9D743D009E9C19 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
DAC2D67B1C9D743D009E9C19 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
DAC2D67C1C9D743D009E9C19 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
DAC2D67E1C9D743D009E9C19 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
DAC2D67F1C9D743D009E9C19 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
DAC2D6811C9D743D009E9C19 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = ../Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet.TabletTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
DAC2D6821C9D743D009E9C19 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = ../Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet.TabletTests;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DAC2D6631C9D743D009E9C19 /* Build configuration list for PBXProject "Tablet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAC2D67B1C9D743D009E9C19 /* Debug */,
DAC2D67C1C9D743D009E9C19 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DAC2D67D1C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "Tablet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAC2D67E1C9D743D009E9C19 /* Debug */,
DAC2D67F1C9D743D009E9C19 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DAC2D6801C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "TabletTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAC2D6811C9D743D009E9C19 /* Debug */,
DAC2D6821C9D743D009E9C19 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = DAC2D6601C9D743D009E9C19 /* Project object */;
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Tablet.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAC2D6681C9D743D009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>DAC2D6721C9D743D009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,15 +0,0 @@
//
// AppDelegate.swift
// TabletDemo
//
// Created by Max Sokolov on 08/11/15.
// Copyright © 2015 Tablet. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

View File

@ -1,33 +0,0 @@
//
// MainViewController.swift
// TabletDemo
//
// Created by Max Sokolov on 19/03/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import Foundation
import UIKit
import Tablet
class MainViewController : UITableViewController {
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
tableDirector = TableDirector(tableView: tableView)
tableDirector += TableRowBuilder<Int, UITableViewCell>(items: [1, 2, 3, 4], id: "cell")
.action(.configure) { data -> Void in
data.cell?.accessoryType = .DisclosureIndicator
data.cell?.textLabel?.text = "\(data.item)"
}
.action(.click) { data -> Void in
}
}
}

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="dOU-ON-YYD">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--Main View Controller-->
<scene sceneID="oP2-EZ-N5W">
<objects>
<tableViewController id="ZyD-Ww-nfe" customClass="MainViewController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="xii-6e-J4f">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" id="eQJ-7O-k03">
<rect key="frame" x="0.0" y="92" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="eQJ-7O-k03" id="Ufi-z4-pLt">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="ZyD-Ww-nfe" id="dup-HU-FfF"/>
<outlet property="delegate" destination="ZyD-Ww-nfe" id="bRX-uu-KTX"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="6ba-xC-prc"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="uxV-eY-exN" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1074" y="287"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Nb3-Yi-ik8">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="dOU-ON-YYD" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Ngx-L8-cbs">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="ZyD-Ww-nfe" kind="relationship" relationship="rootViewController" id="vRk-x4-ylh"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1px-T5-UXL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="334" y="287"/>
</scene>
</scenes>
</document>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>TabletDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAB7EB261BEF787300D2AD5E</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View File

@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D5DB1C9D6433009E9C19"
BuildableName = "TabletTests.xctest"
BlueprintName = "TabletTests"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>TabletDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAB7EB261BEF787300D2AD5E</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>DAC2D5DB1C9D6433009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

245
Tests/TableKitTests.swift Normal file
View File

@ -0,0 +1,245 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import XCTest
import TableKit
class TestController: UITableViewController {
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
tableDirector = TableDirector(tableView: tableView)
}
}
struct TestData {
let title: String
}
struct TestTableViewCellOptions {
static let ReusableIdentifier: String = "ReusableIdentifier"
static let CellAction: String = "CellAction"
static let CellActionUserInfoKey: String = "CellActionUserInfoKey"
static let CellActionUserInfoValue: String = "CellActionUserInfoValue"
static let EstimatedHeight: CGFloat = 255
}
class TestTableViewCell: UITableViewCell, ConfigurableCell {
typealias T = TestData
static var estimatedHeight: CGFloat? {
return TestTableViewCellOptions.EstimatedHeight
}
static var reuseIdentifier: String {
return TestTableViewCellOptions.ReusableIdentifier
}
func configure(with item: T) {
textLabel?.text = item.title
}
func raiseAction() {
TableCellAction(key: TestTableViewCellOptions.CellAction, sender: self, userInfo: nil).invoke()
}
}
class TableKitTests: XCTestCase {
var testController: TestController!
override func setUp() {
super.setUp()
testController = TestController()
testController.tableView.frame = UIScreen.main.bounds
testController.tableView.isHidden = false
testController.tableView.setNeedsLayout()
testController.tableView.layoutIfNeeded()
}
override func tearDown() {
testController = nil
super.tearDown()
}
func testTableDirectorHasTableView() {
XCTAssertNotNil(testController.tableView, "TestController should have table view")
XCTAssertNotNil(testController.tableDirector, "TestController should have table director")
XCTAssertNotNil(testController.tableDirector.tableView, "TableDirector should have table view")
}
func testRowInSection() {
let data = TestData(title: "title")
let row = TableRow<TestTableViewCell>(item: data)
testController.tableDirector += row
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell)
XCTAssertTrue(cell?.textLabel?.text == data.title)
}
func testManyRowsInSection() {
let data = [TestData(title: "1"), TestData(title: "2"), TestData(title: "3")]
let rows: [Row] = data.map({ TableRow<TestTableViewCell>(item: $0) })
testController.tableDirector += rows
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == data.count, "Table view should have certain number of rows in a section")
for (index, element) in data.enumerated() {
let cell = testController.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell)
XCTAssertTrue(cell?.textLabel?.text == element.title)
}
}
func testTableSectionCreatesSectionWithHeaderAndFooterTitles() {
let row = TableRow<TestTableViewCell>(item: TestData(title: "title"))
let sectionHeaderTitle = "Header Title"
let sectionFooterTitle = "Footer Title"
let section = TableSection(headerTitle: sectionHeaderTitle, footerTitle: sectionFooterTitle, rows: [row])
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForHeaderInSection: 0) == sectionHeaderTitle)
XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForFooterInSection: 0) == sectionFooterTitle)
}
func testTableSectionCreatesSectionWithHeaderAndFooterViews() {
let row = TableRow<TestTableViewCell>(item: TestData(title: "title"))
let sectionHeaderView = UIView()
let sectionFooterView = UIView()
let section = TableSection(headerView: sectionHeaderView, footerView: sectionFooterView, rows: nil)
section += row
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForHeaderInSection: 0) == sectionHeaderView)
XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForFooterInSection: 0) == sectionFooterView)
}
func testRowBuilderCustomActionInvokedAndSentUserInfo() {
let expectation = self.expectation(description: "cell action")
let row = TableRow<TestTableViewCell>(item: TestData(title: "title"))
.on(TableRowAction(.custom(TestTableViewCellOptions.CellAction)) { (data) in
XCTAssertNotNil(data.cell, "Action data should have a cell")
expectation.fulfill()
})
testController.view.isHidden = false
testController.tableDirector += row
testController.tableView.reloadData()
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell")
cell?.raiseAction()
waitForExpectations(timeout: 1.0, handler: nil)
}
func testReplaceSectionOnExistingIndex() {
let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1"))
let row2 = TableRow<TestTableViewCell>(item: TestData(title: "title2"))
let section1 = TableSection(headerView: nil, footerView: nil, rows: nil)
section1 += row1
let section2 = TableSection(headerView: nil, footerView: nil, rows: nil)
section2 += row2
testController.tableDirector += section1
testController.tableView.reloadData()
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertTrue(cell?.textLabel?.text == "title1")
testController.tableDirector.replaceSection(at: 0, with: section2)
testController.tableView.reloadData()
let cell1 = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertTrue(cell1?.textLabel?.text == "title2")
}
func testReplaceSectionOnWrongIndex() {
let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1"))
let row2 = TableRow<TestTableViewCell>(item: TestData(title: "title2"))
let section1 = TableSection(headerView: nil, footerView: nil, rows: nil)
section1 += row1
let section2 = TableSection(headerView: nil, footerView: nil, rows: nil)
section2 += row2
testController.tableDirector += section1
testController.tableView.reloadData()
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertTrue(cell?.textLabel?.text == "title1")
testController.tableDirector.replaceSection(at: 33, with: section2)
testController.tableView.reloadData()
let cell1 = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertTrue(cell1?.textLabel?.text == "title1")
}
}

View File

@ -1,200 +0,0 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import XCTest
import Tablet
class TestController: UITableViewController {
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
tableDirector = TableDirector(tableView: tableView)
}
}
struct TestData {
let title: String
}
struct TestTableViewCellOptions {
static let ReusableIdentifier: String = "ReusableIdentifier"
static let CellAction: String = "CellAction"
static let CellActionUserInfoKey: String = "CellActionUserInfoKey"
static let CellActionUserInfoValue: String = "CellActionUserInfoValue"
static let EstimatedHeight: Float = 255
}
class TestTableViewCell: UITableViewCell, ConfigurableCell {
typealias Item = TestData
static func reusableIdentifier() -> String {
return TestTableViewCellOptions.ReusableIdentifier
}
static func estimatedHeight() -> Float {
return TestTableViewCellOptions.EstimatedHeight
}
func configureWithItem(item: Item) {
textLabel?.text = item.title
}
func raiseAction() {
Action(key: TestTableViewCellOptions.CellAction, sender: self, userInfo: [TestTableViewCellOptions.CellActionUserInfoKey: TestTableViewCellOptions.CellActionUserInfoValue]).invoke()
}
}
class TabletTests: XCTestCase {
var testController: TestController!
override func setUp() {
super.setUp()
testController = TestController()
}
override func tearDown() {
testController = nil
super.tearDown()
}
func testTableDirectorHasTableView() {
XCTAssertNotNil(testController.tableView, "TestController should have table view")
XCTAssertNotNil(testController.tableDirector, "TestController should have table director")
XCTAssertNotNil(testController.tableDirector.tableView, "TableDirector should have table view")
}
func testSimpleRowBuilderCreatesRowsAndSection() {
let source = ["1", "2", "3"]
let rows = TableRowBuilder<String, UITableViewCell>(items: source)
.action(.configure) { data -> Void in
XCTAssertNotNil(data.cell, "Action should have a cell")
data.cell?.textLabel?.text = "\(data.item)"
}
testController.view.hidden = false
testController.tableDirector += rows
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == source.count, "Table view should have certain number of rows in a section")
for (index, element) in source.enumerate() {
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
XCTAssertNotNil(cell)
XCTAssertTrue(cell?.textLabel?.text == element)
}
}
func testConfigurableRowBuilderCreatesRowsAndSection() {
let testData = TestData(title: "title")
testController.view.hidden = false
testController.tableDirector += TableConfigurableRowBuilder<TestData, TestTableViewCell>(item: testData)
testController.tableView.reloadData()
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell
XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell")
XCTAssertTrue(cell?.textLabel?.text == testData.title, "Cell's textLabel.text should equal to testData's title")
}
func testSectionBuilderCreatesSectionWithHeaderAndFooterTitles() {
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let sectionHeaderTitle = "Header Title"
let sectionFooterTitle = "Footer Title"
let section = TableSectionBuilder(headerTitle: sectionHeaderTitle, footerTitle: sectionFooterTitle, rows: [row])
testController.view.hidden = false
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForHeaderInSection: 0) == sectionHeaderTitle)
XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForFooterInSection: 0) == sectionFooterTitle)
}
func testSectionBuilderCreatesSectionWithHeaderAndFooterViews() {
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let sectionHeaderView = UIView()
let sectionFooterView = UIView()
let section = TableSectionBuilder(headerView: sectionHeaderView, headerHeight: 44, footerView: sectionFooterView, footerHeight: 44)
section += row
testController.view.hidden = false
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForHeaderInSection: 0) == sectionHeaderView)
XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForFooterInSection: 0) == sectionFooterView)
}
func testRowBuilderCustomActionInvokedAndSentUserInfo() {
let expectation = expectationWithDescription("cell action")
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
.action(TestTableViewCellOptions.CellAction) { data -> Void in
XCTAssertNotNil(data.cell, "Action data should have a cell")
XCTAssertNotNil(data.userInfo, "Action data should have a user info dictionary")
XCTAssertTrue(data.userInfo?[TestTableViewCellOptions.CellActionUserInfoKey] as? String == TestTableViewCellOptions.CellActionUserInfoValue, "UserInfo should have correct value for key")
expectation.fulfill()
}
testController.view.hidden = false
testController.tableDirector += row
testController.tableView.reloadData()
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell
XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell")
cell?.raiseAction()
waitForExpectationsWithTimeout(1.0, handler: nil)
}
}