Compare commits
384 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
fec9537745 | |
|
|
246c0d06c0 | |
|
|
54bf141aff | |
|
|
01134b83b4 | |
|
|
3fefa09c3a | |
|
|
4886e43d20 | |
|
|
41826e18db | |
|
|
caec2dd10e | |
|
|
efc03d7c22 | |
|
|
94c40faa63 | |
|
|
d87a23587e | |
|
|
49b3f868f3 | |
|
|
37482d3b69 | |
|
|
2cc161f0c0 | |
|
|
8bf4840d9d | |
|
|
3b266fb7c7 | |
|
|
eb93fe253c | |
|
|
44c55d2f05 | |
|
|
efe99eeb46 | |
|
|
a0658f0b2e | |
|
|
065cd9ace3 | |
|
|
c3652eec6f | |
|
|
01bf1e01e3 | |
|
|
45aee4522d | |
|
|
f39e7ac602 | |
|
|
dc1a924a80 | |
|
|
402757c41f | |
|
|
d6520346c3 | |
|
|
959032a03f | |
|
|
00de0cd809 | |
|
|
337175c507 | |
|
|
a4bacd2c16 | |
|
|
603264a6d1 | |
|
|
1d4fdaad0d | |
|
|
f5ad500ba4 | |
|
|
1b4a988b35 | |
|
|
f8f2ca6852 | |
|
|
ff18a4d8b8 | |
|
|
d098691621 | |
|
|
8b6319d510 | |
|
|
b049864758 | |
|
|
7f349c6944 | |
|
|
a768352b47 | |
|
|
65cf31717b | |
|
|
9d10bc18bf | |
|
|
497b4e009c | |
|
|
6ef5ad504e | |
|
|
e22ec03990 | |
|
|
5982d5db3a | |
|
|
6eaf2cf3a2 | |
|
|
921e2b42b3 | |
|
|
70e6addcd0 | |
|
|
86f07b8e7c | |
|
|
1c92c14a1a | |
|
|
7416271076 | |
|
|
0812293813 | |
|
|
f16a5a29c9 | |
|
|
b379ac6272 | |
|
|
0c87f3ee88 | |
|
|
ce24369557 | |
|
|
85a45c33f2 | |
|
|
92b723b8ca | |
|
|
12883a33de | |
|
|
1d28233ebe | |
|
|
dd8e5d0625 | |
|
|
21aab6256f | |
|
|
cc02531b27 | |
|
|
b62dea8702 | |
|
|
279fdd4854 | |
|
|
02f77ed699 | |
|
|
74af45f3cd | |
|
|
f05511cfba | |
|
|
de5593f6a9 | |
|
|
a1e1e9d908 | |
|
|
70d83fc5b0 | |
|
|
23b51227fd | |
|
|
d8ae329118 | |
|
|
fd50b732c9 | |
|
|
709f739ab6 | |
|
|
9fff8c861c | |
|
|
da76188afd | |
|
|
e3870d5ce8 | |
|
|
a7488b1d4f | |
|
|
4712406618 | |
|
|
d595a5d2df | |
|
|
8463401b94 | |
|
|
05119aae43 | |
|
|
f3c7ea9e43 | |
|
|
ae24e5c7c0 | |
|
|
0c96110ca2 | |
|
|
ad979f4376 | |
|
|
6f849e4cae | |
|
|
293caeebdb | |
|
|
cc69951856 | |
|
|
898191b0fa | |
|
|
bd8ac5a28b | |
|
|
9ef7a7a8bb | |
|
|
ce2874c58d | |
|
|
c9bd52797d | |
|
|
06becd8624 | |
|
|
c277b6529c | |
|
|
39b4df6ee9 | |
|
|
b49b01ff59 | |
|
|
92b3e3b9a7 | |
|
|
1b97d33cf6 | |
|
|
721ba1972a | |
|
|
ef536b71b3 | |
|
|
d12f3549aa | |
|
|
155a4d1cb7 | |
|
|
6b25d54bd8 | |
|
|
9238ab8472 | |
|
|
78b67e4c42 | |
|
|
3821d4300c | |
|
|
065d8ccae3 | |
|
|
d4fc924676 | |
|
|
2b96888d12 | |
|
|
9bbd9c4b69 | |
|
|
44cbb8b7d8 | |
|
|
48a36e0969 | |
|
|
67003df587 | |
|
|
0e1aa0261f | |
|
|
3ba9f45ded | |
|
|
6abdc8ad8d | |
|
|
7132a0a548 | |
|
|
934e7623f9 | |
|
|
e74fee3653 | |
|
|
d5d785218a | |
|
|
443b8aaa62 | |
|
|
0f8d0db84e | |
|
|
f19b637999 | |
|
|
b307b2d809 | |
|
|
ecc90b1db8 | |
|
|
28f6a60d61 | |
|
|
1eed11abbf | |
|
|
162aee0033 | |
|
|
135bc22804 | |
|
|
f794dea7ee | |
|
|
a5d8f0874a | |
|
|
8991138f18 | |
|
|
becdc205bb | |
|
|
c3d6f0af60 | |
|
|
b449e05edc | |
|
|
db6dc40fac | |
|
|
e18c6ef669 | |
|
|
b74403e2c6 | |
|
|
5f6de4703a | |
|
|
c040613654 | |
|
|
0af6956d62 | |
|
|
3da097f1f1 | |
|
|
3de586c4f6 | |
|
|
7f65f0c4e0 | |
|
|
9ae69b71ca | |
|
|
a240acee2d | |
|
|
9e1372252a | |
|
|
5a7a9b9655 | |
|
|
7eca499a59 | |
|
|
8de932b1db | |
|
|
be410f0cc3 | |
|
|
6601b14b7f | |
|
|
da98b9892f | |
|
|
04e62a8bc0 | |
|
|
9ba8ef4932 | |
|
|
24d3540dd5 | |
|
|
6f441b2f7d | |
|
|
38230a9a57 | |
|
|
676d4fdf88 | |
|
|
262d2b83e2 | |
|
|
a88043b33a | |
|
|
5db9726dab | |
|
|
81fbd2f5e5 | |
|
|
0b46f7c52f | |
|
|
67a41b062f | |
|
|
edcbcf60b7 | |
|
|
6e2b707df7 | |
|
|
a25e4c0eb4 | |
|
|
bfc59a0670 | |
|
|
3751eec584 | |
|
|
0916b5f990 | |
|
|
24a7f0589f | |
|
|
3f88b6d0aa | |
|
|
6c73ef4860 | |
|
|
c09e06f67d | |
|
|
6a1650a68b | |
|
|
eebaaf5268 | |
|
|
29202d4405 | |
|
|
0f93d296ed | |
|
|
1be65d18ad | |
|
|
70d6cb8d73 | |
|
|
aeae61d48b | |
|
|
1df6447d73 | |
|
|
9218051f10 | |
|
|
52e44dcc6e | |
|
|
90aebed304 | |
|
|
40d138eac1 | |
|
|
7a310f6db2 | |
|
|
9a50eba12e | |
|
|
ddced231c3 | |
|
|
155524f13c | |
|
|
39eda5a8f3 | |
|
|
85cf81ecd6 | |
|
|
99fc12e723 | |
|
|
60003d7557 | |
|
|
5e5643c342 | |
|
|
a7530eb3c7 | |
|
|
1ae302f7ab | |
|
|
f132560f39 | |
|
|
a0113c23e0 | |
|
|
520f1f91f0 | |
|
|
d3773efbcc | |
|
|
4dd21a7d2e | |
|
|
a9a2496831 | |
|
|
7a732cb7c3 | |
|
|
d42757fb68 | |
|
|
a9c5d997cb | |
|
|
b8084efc08 | |
|
|
ab6e88ad65 | |
|
|
0c527fd47f | |
|
|
7438856134 | |
|
|
cf37c2b5eb | |
|
|
f90db7de16 | |
|
|
0186da5452 | |
|
|
09d5d75cef | |
|
|
8477cea35b | |
|
|
aaafb0d28d | |
|
|
4b4d2a616d | |
|
|
b7398a2986 | |
|
|
762fd7546b | |
|
|
a5ff6c520b | |
|
|
d3ccd86577 | |
|
|
f23901d4a1 | |
|
|
663111500f | |
|
|
26192a89b8 | |
|
|
2e8b3f3232 | |
|
|
da51e2b7e9 | |
|
|
e23892e33e | |
|
|
40653d7a18 | |
|
|
2d9bb07795 | |
|
|
c5c180594e | |
|
|
0bf04ebae8 | |
|
|
5dde1c8d70 | |
|
|
9fbe7c9383 | |
|
|
080b3f0458 | |
|
|
126720f388 | |
|
|
acebd1c3e8 | |
|
|
ffac9fae14 | |
|
|
c31aa997fa | |
|
|
8de38d8dee | |
|
|
740768fdfd | |
|
|
1539f39e54 | |
|
|
1137b47af1 | |
|
|
b68bd46ca8 | |
|
|
89e3113a35 | |
|
|
f74d35cc8c | |
|
|
7e3d52fb92 | |
|
|
dd6092f87f | |
|
|
221efc00ab | |
|
|
23cceddfe2 | |
|
|
1bc7301c7e | |
|
|
86b93ae78d | |
|
|
1266a91446 | |
|
|
d4efb6a679 | |
|
|
0d7d97db57 | |
|
|
018c35ef53 | |
|
|
03b67c5178 | |
|
|
09e5c4b8a4 | |
|
|
23378e2de4 | |
|
|
dd78ffa41e | |
|
|
1bd049e276 | |
|
|
d01c6c7fba | |
|
|
a2f8bdde30 | |
|
|
98c481be71 | |
|
|
9d0c278a34 | |
|
|
fadb2f4e60 | |
|
|
cc75e84f58 | |
|
|
7cc572c7aa | |
|
|
3e4f7d81ff | |
|
|
1ed3db1cb1 | |
|
|
8b5ad2fcd1 | |
|
|
885977b0ef | |
|
|
c8fca159ee | |
|
|
6f3daa337b | |
|
|
1e018523bb | |
|
|
c352099fa2 | |
|
|
9bf4e72f86 | |
|
|
721596131b | |
|
|
8509c8d64e | |
|
|
42eec9243e | |
|
|
533c6e6eb8 | |
|
|
310cec9c12 | |
|
|
1e4f76e04f | |
|
|
272d238a2a | |
|
|
653948ec08 | |
|
|
699d2aa00d | |
|
|
166a7421f4 | |
|
|
0463cfdf40 | |
|
|
e3ddb25b22 | |
|
|
87821ac3c4 | |
|
|
51de86c81a | |
|
|
96b2a19f1d | |
|
|
708f164c96 | |
|
|
bf01dc638a | |
|
|
01dc5dfd21 | |
|
|
234e0efc85 | |
|
|
44c1a861d8 | |
|
|
c27a386807 | |
|
|
21f131bb19 | |
|
|
0c5f24a491 | |
|
|
6e4ea9caf6 | |
|
|
229ef94c0e | |
|
|
66f318bb15 | |
|
|
7faee8f24e | |
|
|
e121c3fbc6 | |
|
|
e096889819 | |
|
|
b5c7493140 | |
|
|
08fb8af74e | |
|
|
bdf009b980 | |
|
|
4bda514277 | |
|
|
7f1edda39e | |
|
|
82fce34c2e | |
|
|
3f54cacab0 | |
|
|
051dba0e97 | |
|
|
94925e301b | |
|
|
bc503bcb4f | |
|
|
92ee2b731c | |
|
|
bba29151ec | |
|
|
b87d2b8aec | |
|
|
779bf8dd43 | |
|
|
ba8aa05d8b | |
|
|
0250b08302 | |
|
|
348c12e1fc | |
|
|
87d959f05f | |
|
|
874f767528 | |
|
|
05bc9039ea | |
|
|
cb9789ccd8 | |
|
|
a41222300f | |
|
|
ae2707920f | |
|
|
39d4f8ce4e | |
|
|
08a56c799c | |
|
|
6498e39daa | |
|
|
0e42c836c1 | |
|
|
e6450068a5 | |
|
|
87e7dbd6c4 | |
|
|
c3fda15cb4 | |
|
|
7083462aca | |
|
|
32a257149e | |
|
|
03186a2cf5 | |
|
|
ef384cb0f1 | |
|
|
7d9d6ff60b | |
|
|
fea004861c | |
|
|
9c909b8471 | |
|
|
b71d141cab | |
|
|
dd97d7a279 | |
|
|
ae34cefe7a | |
|
|
b05b5871c5 | |
|
|
6df6d3628c | |
|
|
08ce2d62e4 | |
|
|
f652ba085f | |
|
|
db8a8740ff | |
|
|
f0afd40e5a | |
|
|
2b316c8452 | |
|
|
47410ea182 | |
|
|
66329171dd | |
|
|
9537f037d7 | |
|
|
dbcb583fed | |
|
|
a28422c1af | |
|
|
afbb7154ca | |
|
|
4bcd3a1258 | |
|
|
d048613605 | |
|
|
54551ef77e | |
|
|
fdd74a650c | |
|
|
1cf2bb1890 | |
|
|
c9a3255e98 | |
|
|
14de20d154 | |
|
|
d59f2d1074 | |
|
|
3e71cca18d | |
|
|
9309ac7766 | |
|
|
eefb885c44 | |
|
|
28b6b3796b | |
|
|
ffefdaba82 | |
|
|
51cbfe71b2 | |
|
|
e639f2b4b8 | |
|
|
0fefdecac0 | |
|
|
5ac120da0c | |
|
|
4a9ed7f4ff |
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
5.7
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
language: objective-c
|
||||||
|
osx_image: xcode11
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
env:
|
||||||
|
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:
|
||||||
|
- set -o pipefail
|
||||||
|
- xcodebuild -version
|
||||||
|
- 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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?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>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
|
var window: UIWindow?
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import UIKit
|
||||||
|
import TableKit
|
||||||
|
|
||||||
|
class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell {
|
||||||
|
|
||||||
|
func configure(with text: String) {
|
||||||
|
|
||||||
|
accessoryType = .disclosureIndicator
|
||||||
|
textLabel?.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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="8191" 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="8154"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--View Controller-->
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<animations/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1,492 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* 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 */; };
|
||||||
|
DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */; };
|
||||||
|
DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */; };
|
||||||
|
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 */
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
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>"; };
|
||||||
|
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>"; };
|
||||||
|
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>"; };
|
||||||
|
DACB71751CC2D63D00432BD3 /* MainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
DAB7EB241BEF787300D2AD5E /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
DA9EA7D91D0EC65B0021F650 /* TableKit.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase 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 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */,
|
||||||
|
DAC2D5C61C9D2FE5009E9C19 /* Classes */,
|
||||||
|
DAC2D5CB1C9D3058009E9C19 /* Resources */,
|
||||||
|
DA539C871CF50B1800368ACB /* Frameworks */,
|
||||||
|
DAB7EB281BEF787300D2AD5E /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAB7EB281BEF787300D2AD5E /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAB7EB271BEF787300D2AD5E /* TableKitDemo.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D5C61C9D2FE5009E9C19 /* Classes */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAC2D5C81C9D3014009E9C19 /* Application */,
|
||||||
|
DAC2D5C71C9D3005009E9C19 /* Presentation */,
|
||||||
|
);
|
||||||
|
path = Classes;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D5C71C9D3005009E9C19 /* Presentation */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DACB71731CC2D5ED00432BD3 /* Controllers */,
|
||||||
|
DACB71741CC2D5FD00432BD3 /* Views */,
|
||||||
|
);
|
||||||
|
path = Presentation;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D5C81C9D3014009E9C19 /* Application */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */,
|
||||||
|
);
|
||||||
|
path = Application;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D5CB1C9D3058009E9C19 /* Resources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAC2D69D1C9E78B5009E9C19 /* Info.plist */,
|
||||||
|
DAC2D69A1C9E75BE009E9C19 /* Assets */,
|
||||||
|
DAC2D5CC1C9D306C009E9C19 /* Storyboards */,
|
||||||
|
);
|
||||||
|
path = Resources;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D5CC1C9D306C009E9C19 /* Storyboards */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */,
|
||||||
|
DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */,
|
||||||
|
);
|
||||||
|
path = Storyboards;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
DAC2D69A1C9E75BE009E9C19 /* Assets */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */,
|
||||||
|
);
|
||||||
|
path = Assets;
|
||||||
|
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 */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
DAB7EB261BEF787300D2AD5E /* TableKitDemo */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TableKitDemo" */;
|
||||||
|
buildPhases = (
|
||||||
|
DAB7EB231BEF787300D2AD5E /* Sources */,
|
||||||
|
DAB7EB241BEF787300D2AD5E /* Frameworks */,
|
||||||
|
DAB7EB251BEF787300D2AD5E /* Resources */,
|
||||||
|
DA9EA7DD1D0EC65B0021F650 /* Embed Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
DA9EA7DC1D0EC65B0021F650 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = TableKitDemo;
|
||||||
|
productName = TabletDemo;
|
||||||
|
productReference = DAB7EB271BEF787300D2AD5E /* TableKitDemo.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
DAB7EB1F1BEF787300D2AD5E /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 0720;
|
||||||
|
LastUpgradeCheck = 1000;
|
||||||
|
ORGANIZATIONNAME = Tablet;
|
||||||
|
TargetAttributes = {
|
||||||
|
DAB7EB261BEF787300D2AD5E = {
|
||||||
|
CreatedOnToolsVersion = 7.0.1;
|
||||||
|
DevelopmentTeam = Z48R734SJX;
|
||||||
|
LastSwiftMigration = 1000;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TableKitDemo" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = DAB7EB1E1BEF787300D2AD5E;
|
||||||
|
productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectReferences = (
|
||||||
|
{
|
||||||
|
ProductGroup = DA9EA7D11D0EC5C50021F650 /* Products */;
|
||||||
|
ProjectRef = DA9EA7D01D0EC5C50021F650 /* TableKit.xcodeproj */;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
DAB7EB261BEF787300D2AD5E /* TableKitDemo */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* 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 */
|
||||||
|
DAB7EB251BEF787300D2AD5E /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */,
|
||||||
|
5079ADE61FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib in Resources */,
|
||||||
|
DA5546661D15765900AA83EE /* NibTableViewCell.xib in Resources */,
|
||||||
|
DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */,
|
||||||
|
DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
DAB7EB231BEF787300D2AD5E /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
DACB71761CC2D63D00432BD3 /* MainController.swift in Sources */,
|
||||||
|
DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */,
|
||||||
|
DA5546681D15771D00AA83EE /* NibCellsController.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;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
DA9EA7DC1D0EC65B0021F650 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
name = TableKit;
|
||||||
|
targetProxy = DA9EA7DB1D0EC65B0021F650 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
DAB7EB371BEF787300D2AD5E /* 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_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;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
DAB7EB381BEF787300D2AD5E /* 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_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;
|
||||||
|
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;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
DAB7EB3A1BEF787300D2AD5E /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
INFOPLIST_FILE = Resources/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
|
||||||
|
PRODUCT_NAME = TableKitDemo;
|
||||||
|
PROVISIONING_PROFILE = "";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
DAB7EB3B1BEF787300D2AD5E /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
INFOPLIST_FILE = Resources/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
|
||||||
|
PRODUCT_NAME = TableKitDemo;
|
||||||
|
PROVISIONING_PROFILE = "";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TableKitDemo" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
DAB7EB371BEF787300D2AD5E /* Debug */,
|
||||||
|
DAB7EB381BEF787300D2AD5E /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TableKitDemo" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
DAB7EB3A1BEF787300D2AD5E /* Debug */,
|
||||||
|
DAB7EB3B1BEF787300D2AD5E /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = DAB7EB1F1BEF787300D2AD5E /* Project object */;
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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")
|
||||||
|
]
|
||||||
|
)
|
||||||
367
README.md
367
README.md
|
|
@ -1,177 +1,244 @@
|
||||||

|
# TableKit
|
||||||
|
|
||||||
<p align="left">
|
<p align="left">
|
||||||
<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://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></a>
|
||||||
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
|
<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>
|
||||||
<a href="https://cocoapods.org/pods/tablet"><img src="https://img.shields.io/badge/pod-0.2.4-blue.svg" alt="CocoaPods compatible" /></a>
|
<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://raw.githubusercontent.com/maxsokolov/tablet/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></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>
|
||||||
|
<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.
|
||||||
|
|
||||||
That's almost all you need in your controller to build a bunch of cells in a section:
|
# Features
|
||||||
|
|
||||||
|
- [x] Type-safe generic cells
|
||||||
|
- [x] Functional programming style friendly
|
||||||
|
- [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] Chainable cell actions (select/deselect etc.)
|
||||||
|
- [x] Support cells created from code, xib, or storyboard
|
||||||
|
- [x] Support different cells height calculation strategies
|
||||||
|
- [x] Support portrait and landscape orientations
|
||||||
|
- [x] No need to subclass
|
||||||
|
- [x] Extensibility
|
||||||
|
|
||||||
|
# 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"], estimatedRowHeight: 42)
|
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 it's type-safe. See the Usage section to learn more.
|
Put rows into section:
|
||||||
|
```swift
|
||||||
|
let section = TableSection(rows: [row1, row2, row3])
|
||||||
|
```
|
||||||
|
And setup your table:
|
||||||
|
```swift
|
||||||
|
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 {
|
||||||
|
|
||||||
## Requirements
|
func configure(with string: String) {
|
||||||
|
|
||||||
|
textLabel?.text = string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- iOS 8.0+
|
class UserTableViewCell: UITableViewCell, ConfigurableCell {
|
||||||
- Xcode 7.0+
|
|
||||||
|
|
||||||
## Installation
|
static var estimatedHeight: CGFloat? {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
### CocoaPods
|
// is not required to be implemented
|
||||||
To integrate Tablet into your Xcode project using CocoaPods, specify it in your `Podfile`:
|
// by default reuse id is equal to cell's class name
|
||||||
|
static var reuseIdentifier: String {
|
||||||
|
return "my id"
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(with user: User) {
|
||||||
|
|
||||||
|
textLabel?.text = user.name
|
||||||
|
detailTextLabel?.text = "Rating: \(user.rating)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
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
|
||||||
|
let action = TableRowAction<StringTableViewCell>(.click) { (options) in
|
||||||
|
|
||||||
|
// you could access any useful information that relates to the action
|
||||||
|
|
||||||
|
// options.cell - StringTableViewCell?
|
||||||
|
// options.item - String
|
||||||
|
// options.indexPath - IndexPath
|
||||||
|
// options.userInfo - [AnyHashable: Any]?
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
|
||||||
|
```
|
||||||
|
Or, using nice chaining approach:
|
||||||
|
```swift
|
||||||
|
let row = TableRow<StringTableViewCell>(item: "some")
|
||||||
|
.on(.click) { (options) in
|
||||||
|
|
||||||
|
}
|
||||||
|
.on(.shouldHighlight) { (options) -> Bool in
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
You could find all available actions [here](Sources/TableRowAction.swift).
|
||||||
|
|
||||||
|
## Custom row actions
|
||||||
|
|
||||||
|
You are able to define your own actions:
|
||||||
|
```swift
|
||||||
|
struct MyActions {
|
||||||
|
|
||||||
|
static let ButtonClicked = "ButtonClicked"
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyTableViewCell: UITableViewCell, ConfigurableCell {
|
||||||
|
|
||||||
|
@IBAction func myButtonClicked(sender: UIButton) {
|
||||||
|
|
||||||
|
TableCellAction(key: MyActions.ButtonClicked, sender: self).invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
And handle them accordingly:
|
||||||
|
```swift
|
||||||
|
let myAction = TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)) { (options) in
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Multiple actions with same type
|
||||||
|
|
||||||
|
It's also possible to use multiple actions with same type:
|
||||||
|
```swift
|
||||||
|
let click1 = TableRowAction<StringTableViewCell>(.click) { (options) 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])
|
||||||
|
```
|
||||||
|
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.
|
||||||
|
|
||||||
|
You could also remove any action by id:
|
||||||
|
```swift
|
||||||
|
row.removeAction(forActionId: "action_id")
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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
|
```ruby
|
||||||
source 'https://github.com/CocoaPods/Specs.git'
|
MyTableViewCell.swift
|
||||||
platform :ios, '8.0'
|
MyTableViewCell.xib
|
||||||
use_frameworks!
|
|
||||||
|
|
||||||
pod 'Tablet'
|
|
||||||
```
|
```
|
||||||
|
You can also turn off this behaviour:
|
||||||
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 tableDirector = TableDirector(tableView: tableView, shouldUseAutomaticCellRegistration: false)
|
||||||
|
|
||||||
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", rowBuilders: [rowBuilder])
|
|
||||||
|
|
||||||
director = TableDirector(tableView: tableView)
|
|
||||||
director.appendSections(sectionBuilder)
|
|
||||||
```
|
```
|
||||||
|
and register your cell manually.
|
||||||
|
|
||||||
### Type-safe configurable cells
|
# Installation
|
||||||
|
|
||||||
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.
|
## CocoaPods
|
||||||
You could easily do this using the `TableConfigurableRowBuilder`. Your cell should respect the `ConfigurableCell` protocol as you may see in example below:
|
To integrate TableKit into your Xcode project using CocoaPods, specify it in your `Podfile`:
|
||||||
|
|
||||||
```swift
|
```ruby
|
||||||
import Tablet
|
pod 'TableKit'
|
||||||
|
|
||||||
class MyTableViewCell : UITableViewCell, ConfigurableCell {
|
|
||||||
|
|
||||||
typealias Item = User
|
|
||||||
|
|
||||||
static func reusableIdentifier() -> String {
|
|
||||||
return "reusable_id"
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureWithItem(item: Item) { // item is user here
|
|
||||||
|
|
||||||
textLabel?.text = item.username
|
|
||||||
detailTextLabel?.text = item.isActive ? "Active" : "Inactive"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
Once you've implemented the protocol, simply use the `TableConfigurableRowBuilder` to build cells:
|
## Carthage
|
||||||
|
Add the line `github "maxsokolov/tablekit"` to your `Cartfile`.
|
||||||
|
## Manual
|
||||||
|
Clone the repo and drag files from `Sources` folder into your Xcode project.
|
||||||
|
|
||||||
```swift
|
# Requirements
|
||||||
import Tablet
|
|
||||||
|
|
||||||
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(estimatedRowHeight: 42)
|
- iOS 8.0
|
||||||
rowBuilder.appendItems(users)
|
- Xcode 9.0
|
||||||
|
|
||||||
director = TableDirector(tableView: tableView)
|
# Changelog
|
||||||
tableDirector.appendSection(TableSectionBuilder(rowBuilders: [rowBuilder]))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cell actions
|
Keep an eye on [changes](CHANGELOG.md).
|
||||||
|
|
||||||
Tablet provides a chaining approach to handle actions from your cells:
|
# License
|
||||||
|
|
||||||
```swift
|
TableKit is available under the MIT license. See LICENSE for details.
|
||||||
import Tablet
|
|
||||||
|
|
||||||
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: [user1, user2, user3], id: "reusable_id")
|
|
||||||
.action(.configure) { data -> Void in
|
|
||||||
|
|
||||||
}
|
|
||||||
.action(.click) { data -> Void in
|
|
||||||
|
|
||||||
}
|
|
||||||
.action(.shouldHighlight) { data -> ReturnValue in
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom cell actions
|
|
||||||
```swift
|
|
||||||
import Tablet
|
|
||||||
|
|
||||||
let kMyAction = "action_key"
|
|
||||||
|
|
||||||
class MyTableViewCell : UITableViewCell {
|
|
||||||
|
|
||||||
@IBAction func buttonClicked(sender: UIButton) {
|
|
||||||
|
|
||||||
Action(key: kMyAction, sender: self, userInfo: nil).invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
And receive this actions with your row builder:
|
|
||||||
```swift
|
|
||||||
import Tablet
|
|
||||||
|
|
||||||
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, id: "reusable_id", estimatedRowHeight: 42)
|
|
||||||
.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:
|
|
||||||
```swift
|
|
||||||
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, estimatedRowHeight: 42)
|
|
||||||
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
You could also invoke an action that returns a value.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Tablet is available under the MIT license. See LICENSE for details.
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
public enum LayoutType {
|
||||||
|
|
||||||
|
case manual
|
||||||
|
|
||||||
|
case auto
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// --
|
||||||
|
public func +=(left: TableDirector, right: TableSection) {
|
||||||
|
left.append(section: right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func +=(left: TableDirector, right: [TableSection]) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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 */;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0700"
|
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 = "DAB7EB261BEF787300D2AD5E"
|
BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
|
||||||
BuildableName = "TabletDemo.app"
|
BuildableName = "TableKit.framework"
|
||||||
BlueprintName = "TabletDemo"
|
BlueprintName = "TableKit"
|
||||||
ReferencedContainer = "container:TabletDemo.xcodeproj">
|
ReferencedContainer = "container:TableKit.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
|
|
@ -28,14 +28,24 @@
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DA9EA7C31D0EC45F0021F650"
|
||||||
|
BuildableName = "TableKitTests.xctest"
|
||||||
|
BlueprintName = "TableKitTests"
|
||||||
|
ReferencedContainer = "container:TableKit.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
|
BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
|
||||||
BuildableName = "TabletDemo.app"
|
BuildableName = "TableKit.framework"
|
||||||
BlueprintName = "TabletDemo"
|
BlueprintName = "TableKit"
|
||||||
ReferencedContainer = "container:TabletDemo.xcodeproj">
|
ReferencedContainer = "container:TableKit.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<AdditionalOptions>
|
||||||
|
|
@ -51,16 +61,15 @@
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<MacroExpansion>
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
|
BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
|
||||||
BuildableName = "TabletDemo.app"
|
BuildableName = "TableKit.framework"
|
||||||
BlueprintName = "TabletDemo"
|
BlueprintName = "TableKit"
|
||||||
ReferencedContainer = "container:TabletDemo.xcodeproj">
|
ReferencedContainer = "container:TableKit.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<AdditionalOptions>
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
|
@ -70,16 +79,15 @@
|
||||||
savedToolIdentifier = ""
|
savedToolIdentifier = ""
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
debugDocumentVersioning = "YES">
|
debugDocumentVersioning = "YES">
|
||||||
<BuildableProductRunnable
|
<MacroExpansion>
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
|
BlueprintIdentifier = "DA9EA7551D0B679A0021F650"
|
||||||
BuildableName = "TabletDemo.app"
|
BuildableName = "TableKit.framework"
|
||||||
BlueprintName = "TabletDemo"
|
BlueprintName = "TableKit"
|
||||||
ReferencedContainer = "container:TabletDemo.xcodeproj">
|
ReferencedContainer = "container:TableKit.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</MacroExpansion>
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
Pod::Spec.new do |s|
|
|
||||||
s.name = 'Tablet'
|
|
||||||
s.version = '0.2.6'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,238 +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!
|
|
||||||
private var sections = [TableSectionBuilder]()
|
|
||||||
public weak var scrollDelegate: UIScrollViewDelegate?
|
|
||||||
|
|
||||||
public init(tableView: UITableView) {
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.tableView = tableView
|
|
||||||
self.tableView.delegate = self
|
|
||||||
self.tableView.dataSource = self
|
|
||||||
|
|
||||||
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: kActionPerformedNotificationKey, 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)!
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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: action.userInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 ?? indexPath
|
|
||||||
}
|
|
||||||
|
|
||||||
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(rowBuilders: [right]))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(left: TableDirector, right: [RowBuilder]) {
|
|
||||||
|
|
||||||
left.appendSection(TableSectionBuilder(rowBuilders: right))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(left: TableDirector, right: TableSectionBuilder) {
|
|
||||||
|
|
||||||
left.appendSection(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(left: TableDirector, right: [TableSectionBuilder]) {
|
|
||||||
|
|
||||||
left.appendSections(right)
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +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?
|
|
||||||
|
|
||||||
internal 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
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 var reusableIdentifier: String
|
|
||||||
public var estimatedRowHeight: CGFloat
|
|
||||||
public var numberOfRows: Int {
|
|
||||||
get {
|
|
||||||
return items.count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(item: I, id: String? = nil, estimatedRowHeight: CGFloat = 48) {
|
|
||||||
|
|
||||||
reusableIdentifier = id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? ""
|
|
||||||
self.estimatedRowHeight = estimatedRowHeight
|
|
||||||
items.append(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(items: [I]? = nil, id: String? = nil, estimatedRowHeight: CGFloat = 48) {
|
|
||||||
|
|
||||||
reusableIdentifier = id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? ""
|
|
||||||
self.estimatedRowHeight = estimatedRowHeight
|
|
||||||
|
|
||||||
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: Triggers
|
|
||||||
|
|
||||||
public func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? {
|
|
||||||
|
|
||||||
if let action = actions[actionType.key] {
|
|
||||||
return action.invoke(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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 init(item: I, estimatedRowHeight: CGFloat = 48) {
|
|
||||||
super.init(item: item, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(items: [I]? = nil, estimatedRowHeight: CGFloat = 48) {
|
|
||||||
super.init(items: items, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? {
|
|
||||||
|
|
||||||
switch actionType {
|
|
||||||
case .configure:
|
|
||||||
(cell as? C)?.configureWithItem(items[itemIndex])
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
return super.invokeAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,115 +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 {
|
|
||||||
|
|
||||||
internal 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, rowBuilders: [RowBuilder]? = nil) {
|
|
||||||
|
|
||||||
self.headerTitle = headerTitle
|
|
||||||
self.footerTitle = footerTitle
|
|
||||||
|
|
||||||
if let initialRows = rowBuilders {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal extension TableSectionBuilder {
|
|
||||||
|
|
||||||
internal func builderAtIndex(var index: Int) -> (RowBuilder, Int)? {
|
|
||||||
|
|
||||||
for builder in builders {
|
|
||||||
if index < builder.numberOfRows {
|
|
||||||
return (builder, index)
|
|
||||||
}
|
|
||||||
index -= builder.numberOfRows
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
internal func willMoveToDirector(tableView: UITableView) {
|
|
||||||
self.tableView = tableView
|
|
||||||
self.builders.forEach { $0.registerCell(inTableView: tableView) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension TableSectionBuilder {
|
|
||||||
|
|
||||||
public func clear() {
|
|
||||||
|
|
||||||
builders.removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func appendRowBuilder(rowBuilder: RowBuilder) {
|
|
||||||
|
|
||||||
appendRowBuilders([rowBuilder])
|
|
||||||
}
|
|
||||||
|
|
||||||
public func appendRowBuilders(rowBuilders: [RowBuilder]) {
|
|
||||||
|
|
||||||
if let tableView = tableView { rowBuilders.forEach { $0.registerCell(inTableView: tableView) } }
|
|
||||||
builders.appendContentsOf(rowBuilders)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(left: TableSectionBuilder, right: RowBuilder) {
|
|
||||||
|
|
||||||
left.appendRowBuilder(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func +=(left: TableSectionBuilder, right: [RowBuilder]) {
|
|
||||||
|
|
||||||
left.appendRowBuilders(right)
|
|
||||||
}
|
|
||||||
|
|
@ -1,128 +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
|
|
||||||
|
|
||||||
internal let kActionPerformedNotificationKey = "_action"
|
|
||||||
|
|
||||||
/**
|
|
||||||
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
|
|
||||||
|
|
||||||
init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int) {
|
|
||||||
|
|
||||||
self.cell = cell
|
|
||||||
self.indexPath = indexPath
|
|
||||||
self.item = item
|
|
||||||
self.itemIndex = itemIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
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(kActionPerformedNotificationKey, object: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
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
|
|
||||||
func configureWithItem(item: Item)
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ConfigurableCell where Self: UITableViewCell {
|
|
||||||
|
|
||||||
static func reusableIdentifier() -> String {
|
|
||||||
|
|
||||||
return NSStringFromClass(self).componentsSeparatedByString(".").last ?? ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
A protocol that every row builder should follow.
|
|
||||||
A certain section can only works with row builders that respect this protocol.
|
|
||||||
*/
|
|
||||||
public protocol RowBuilder {
|
|
||||||
|
|
||||||
var numberOfRows: Int { get }
|
|
||||||
var reusableIdentifier: String { get }
|
|
||||||
var estimatedRowHeight: CGFloat { get }
|
|
||||||
|
|
||||||
func registerCell(inTableView tableView: UITableView)
|
|
||||||
func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject?
|
|
||||||
}
|
|
||||||
|
|
@ -1,345 +0,0 @@
|
||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 46;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71831BF48DD300272920 /* TableSectionBuilder.swift */; };
|
|
||||||
508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71851BF48E0D00272920 /* TableRowBuilder.swift */; };
|
|
||||||
DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */; };
|
|
||||||
DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */; };
|
|
||||||
DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */; };
|
|
||||||
DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */; };
|
|
||||||
DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */; };
|
|
||||||
DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB311BEF787300D2AD5E /* Assets.xcassets */; };
|
|
||||||
DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */; };
|
|
||||||
DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */; };
|
|
||||||
DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */; };
|
|
||||||
DAED08F11C14DE7E006C04D8 /* MyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */; };
|
|
||||||
DAF003961C14DC0C0028C3D6 /* MyNibTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */; };
|
|
||||||
DAF003981C14DC250028C3D6 /* MyNibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
508B71831BF48DD300272920 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = "<group>"; };
|
|
||||||
508B71851BF48E0D00272920 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
|
|
||||||
DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
|
|
||||||
DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableActions.swift; sourceTree = "<group>"; };
|
|
||||||
DAB7EB271BEF787300D2AD5E /* TabletDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabletDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
|
||||||
DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
|
||||||
DAB7EB2F1BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
|
||||||
DAB7EB311BEF787300D2AD5E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
|
||||||
DAB7EB341BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
|
||||||
DAB7EB361BEF787300D2AD5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = "<group>"; };
|
|
||||||
DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyNibTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyNibTableViewCell.xib; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
DAB7EB241BEF787300D2AD5E /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
DAB7EB1E1BEF787300D2AD5E = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB3C1BEF789500D2AD5E /* Tablet */,
|
|
||||||
DAB7EB291BEF787300D2AD5E /* TabletDemo */,
|
|
||||||
DAB7EB281BEF787300D2AD5E /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DAB7EB281BEF787300D2AD5E /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB271BEF787300D2AD5E /* TabletDemo.app */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DAB7EB291BEF787300D2AD5E /* TabletDemo */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */,
|
|
||||||
DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */,
|
|
||||||
DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */,
|
|
||||||
DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */,
|
|
||||||
DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */,
|
|
||||||
DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */,
|
|
||||||
DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */,
|
|
||||||
DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */,
|
|
||||||
DAB7EB311BEF787300D2AD5E /* Assets.xcassets */,
|
|
||||||
DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */,
|
|
||||||
DAB7EB361BEF787300D2AD5E /* Info.plist */,
|
|
||||||
);
|
|
||||||
path = TabletDemo;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DAB7EB3C1BEF789500D2AD5E /* Tablet */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */,
|
|
||||||
DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */,
|
|
||||||
508B71851BF48E0D00272920 /* TableRowBuilder.swift */,
|
|
||||||
508B71831BF48DD300272920 /* TableSectionBuilder.swift */,
|
|
||||||
);
|
|
||||||
name = Tablet;
|
|
||||||
path = ../Tablet;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
DAB7EB261BEF787300D2AD5E /* TabletDemo */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */;
|
|
||||||
buildPhases = (
|
|
||||||
DAB7EB231BEF787300D2AD5E /* Sources */,
|
|
||||||
DAB7EB241BEF787300D2AD5E /* Frameworks */,
|
|
||||||
DAB7EB251BEF787300D2AD5E /* Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = TabletDemo;
|
|
||||||
productName = TabletDemo;
|
|
||||||
productReference = DAB7EB271BEF787300D2AD5E /* TabletDemo.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
DAB7EB1F1BEF787300D2AD5E /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 0700;
|
|
||||||
ORGANIZATIONNAME = Tablet;
|
|
||||||
TargetAttributes = {
|
|
||||||
DAB7EB261BEF787300D2AD5E = {
|
|
||||||
CreatedOnToolsVersion = 7.0.1;
|
|
||||||
DevelopmentTeam = Z48R734SJX;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */;
|
|
||||||
compatibilityVersion = "Xcode 3.2";
|
|
||||||
developmentRegion = English;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = DAB7EB1E1BEF787300D2AD5E;
|
|
||||||
productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
DAB7EB261BEF787300D2AD5E /* TabletDemo */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
DAB7EB251BEF787300D2AD5E /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */,
|
|
||||||
DAF003981C14DC250028C3D6 /* MyNibTableViewCell.xib in Resources */,
|
|
||||||
DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */,
|
|
||||||
DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
DAB7EB231BEF787300D2AD5E /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */,
|
|
||||||
DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */,
|
|
||||||
DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */,
|
|
||||||
DAED08F11C14DE7E006C04D8 /* MyTableViewCell.swift in Sources */,
|
|
||||||
DAF003961C14DC0C0028C3D6 /* MyNibTableViewCell.swift in Sources */,
|
|
||||||
508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */,
|
|
||||||
DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */,
|
|
||||||
DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */,
|
|
||||||
DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */,
|
|
||||||
DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
|
||||||
DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB2F1BEF787300D2AD5E /* Base */,
|
|
||||||
);
|
|
||||||
name = Main.storyboard;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
DAB7EB341BEF787300D2AD5E /* Base */,
|
|
||||||
);
|
|
||||||
name = LaunchScreen.storyboard;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXVariantGroup section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
DAB7EB371BEF787300D2AD5E /* 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;
|
|
||||||
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";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
DAB7EB381BEF787300D2AD5E /* 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;
|
|
||||||
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;
|
|
||||||
VALIDATE_PRODUCT = YES;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
DAB7EB3A1BEF787300D2AD5E /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
INFOPLIST_FILE = TabletDemo/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE = "";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
DAB7EB3B1BEF787300D2AD5E /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
|
||||||
INFOPLIST_FILE = TabletDemo/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE = "";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
DAB7EB371BEF787300D2AD5E /* Debug */,
|
|
||||||
DAB7EB381BEF787300D2AD5E /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
DAB7EB3A1BEF787300D2AD5E /* Debug */,
|
|
||||||
DAB7EB3B1BEF787300D2AD5E /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = DAB7EB1F1BEF787300D2AD5E /* Project object */;
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Bucket
|
|
||||||
type = "1"
|
|
||||||
version = "2.0">
|
|
||||||
</Bucket>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Bucket
|
|
||||||
type = "1"
|
|
||||||
version = "2.0">
|
|
||||||
</Bucket>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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?
|
|
||||||
}
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="jN2-c3-xf5">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="tne-QT-ifu">
|
|
||||||
<objects>
|
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<layoutGuides>
|
|
||||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
|
||||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
|
||||||
</layoutGuides>
|
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
|
||||||
<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="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="ovi-uI-31e">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
|
||||||
<animations/>
|
|
||||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" textLabel="Rrx-qY-GXL" style="IBUITableViewCellStyleDefault" id="MhM-yS-XTS">
|
|
||||||
<rect key="frame" x="0.0" y="114" width="600" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MhM-yS-XTS" id="1uR-sU-VS9">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Rrx-qY-GXL">
|
|
||||||
<rect key="frame" x="15" y="0.0" width="570" height="43"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<animations/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<animations/>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<animations/>
|
|
||||||
</tableViewCell>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ConfigurableTableViewCell" id="GPe-Vu-z5d" customClass="ConfigurableTableViewCell" customModule="TabletDemo" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="158" width="600" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="GPe-Vu-z5d" id="GMV-qL-pTv">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C3u-xz-sbM">
|
|
||||||
<rect key="frame" x="14" y="5" width="70" height="34"/>
|
|
||||||
<animations/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="70" id="MSu-D8-6BO"/>
|
|
||||||
</constraints>
|
|
||||||
<state key="normal" title="Button">
|
|
||||||
<color key="titleColor" red="0.090196078430000007" green="0.3411764706" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
</state>
|
|
||||||
<connections>
|
|
||||||
<action selector="buttonClicked:" destination="GPe-Vu-z5d" eventType="touchUpInside" id="IbC-qw-alO"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<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="wWp-hU-I3e">
|
|
||||||
<rect key="frame" x="92" y="12" width="498" height="21"/>
|
|
||||||
<animations/>
|
|
||||||
<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>
|
|
||||||
</subviews>
|
|
||||||
<animations/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="C3u-xz-sbM" firstAttribute="leading" secondItem="GMV-qL-pTv" secondAttribute="leading" constant="14" id="6j5-PX-P9O"/>
|
|
||||||
<constraint firstItem="C3u-xz-sbM" firstAttribute="centerY" secondItem="GMV-qL-pTv" secondAttribute="centerY" id="OU7-rr-jUL"/>
|
|
||||||
<constraint firstItem="wWp-hU-I3e" firstAttribute="top" secondItem="GMV-qL-pTv" secondAttribute="top" constant="12" id="azl-YD-xyA"/>
|
|
||||||
<constraint firstItem="wWp-hU-I3e" firstAttribute="leading" secondItem="C3u-xz-sbM" secondAttribute="trailing" constant="8" id="tDZ-be-hlD"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="wWp-hU-I3e" secondAttribute="bottom" constant="10" id="w4b-rY-Vf2"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="wWp-hU-I3e" secondAttribute="trailing" constant="10" id="wlJ-Ga-0f7"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<animations/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="button" destination="C3u-xz-sbM" id="7KC-FU-lBA"/>
|
|
||||||
<outlet property="contentLabel" destination="wWp-hU-I3e" id="uo4-ez-ea8"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
</tableView>
|
|
||||||
</subviews>
|
|
||||||
<animations/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="ovi-uI-31e" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="J1O-uy-ywm"/>
|
|
||||||
<constraint firstItem="ovi-uI-31e" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="N0t-Cn-F6e"/>
|
|
||||||
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="ovi-uI-31e" secondAttribute="bottom" id="hgJ-9n-Rwk"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="ovi-uI-31e" secondAttribute="trailing" id="wbG-7U-YPc"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<navigationItem key="navigationItem" id="8uO-wD-KyX"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="tableView" destination="ovi-uI-31e" id="zpd-iM-qe3"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1185" y="-317"/>
|
|
||||||
</scene>
|
|
||||||
<!--Navigation Controller-->
|
|
||||||
<scene sceneID="2aE-PW-F7u">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="jN2-c3-xf5" sceneMemberID="viewController">
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="4L0-SI-HVz">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<animations/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="N0v-UF-dbb"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="jlp-Hv-yz1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="444" y="-317"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// ConfigurableTableViewCell.swift
|
|
||||||
// TabletDemo
|
|
||||||
//
|
|
||||||
// Created by Max Sokolov on 08/11/15.
|
|
||||||
// Copyright © 2015 Tablet. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
let kConfigurableTableViewCellButtonClickedAction = "button_clicked"
|
|
||||||
|
|
||||||
class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell {
|
|
||||||
|
|
||||||
typealias Item = String
|
|
||||||
|
|
||||||
@IBOutlet weak var button: UIButton!
|
|
||||||
@IBOutlet weak var contentLabel: UILabel!
|
|
||||||
|
|
||||||
static func reusableIdentifier() -> String {
|
|
||||||
|
|
||||||
return "ConfigurableTableViewCell"
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureWithItem(item: Item) {
|
|
||||||
|
|
||||||
button.setTitle("Button \(item)", forState: .Normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func buttonClicked(sender: UIButton) {
|
|
||||||
|
|
||||||
Action(key: kConfigurableTableViewCellButtonClickedAction, sender: self).invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// CustomTableActions.swift
|
|
||||||
// TabletDemo
|
|
||||||
//
|
|
||||||
// Created by Max Sokolov on 14/11/15.
|
|
||||||
// Copyright © 2015 Tablet. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
let kTableDirectorDidEndDisplayingCell = "enddisplaycell"
|
|
||||||
|
|
||||||
extension TableDirector {
|
|
||||||
|
|
||||||
public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
|
|
||||||
|
|
||||||
invokeAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
//
|
|
||||||
// MyNibTableViewCell.swift
|
|
||||||
// TabletDemo
|
|
||||||
//
|
|
||||||
// Created by Max Sokolov on 07/12/15.
|
|
||||||
// Copyright © 2015 Tablet. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class MyNibTableViewCell : UITableViewCell {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
|
|
||||||
</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="MyNibTableViewCell" id="ien-1l-Zfq" customClass="MyNibTableViewCell" customModule="TabletDemo" 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="ien-1l-Zfq" id="Ddg-Z7-Dq5">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<animations/>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<animations/>
|
|
||||||
<color key="backgroundColor" red="1" green="0.54996175169999995" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<point key="canvasLocation" x="521" y="285"/>
|
|
||||||
</tableViewCell>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
//
|
|
||||||
// MyTableViewCell.swift
|
|
||||||
// TabletDemo
|
|
||||||
//
|
|
||||||
// Created by Max Sokolov on 07/12/15.
|
|
||||||
// Copyright © 2015 Tablet. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class MyTableViewCell : UITableViewCell {
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
backgroundColor = UIColor.redColor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
//
|
|
||||||
// ViewController.swift
|
|
||||||
// TabletDemo
|
|
||||||
//
|
|
||||||
// Created by Max Sokolov on 08/11/15.
|
|
||||||
// Copyright © 2015 Tablet. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class ViewController: UIViewController, UIScrollViewDelegate {
|
|
||||||
|
|
||||||
@IBOutlet weak var tableView: UITableView!
|
|
||||||
var tableDirector: TableDirector!
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
tableDirector = TableDirector(tableView: tableView)
|
|
||||||
tableDirector.scrollDelegate = self
|
|
||||||
|
|
||||||
let rowBuilder = TableRowBuilder<Int, UITableViewCell>(items: [1, 2, 3, 4], id: "cell", estimatedRowHeight: 44)
|
|
||||||
.action(.configure) { data in
|
|
||||||
|
|
||||||
data.cell?.textLabel?.text = "\(data.item)"
|
|
||||||
}
|
|
||||||
.action(.shouldHighlight) { data in
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in
|
|
||||||
|
|
||||||
print("end display: \(data.indexPath)")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let configurableRowBuilder = TableConfigurableRowBuilder<String, ConfigurableTableViewCell>(items: ["5", "6", "7", "8"], estimatedRowHeight: 300)
|
|
||||||
.action(.click) { data -> Void in
|
|
||||||
|
|
||||||
print("click action indexPath: \(data.indexPath), item: \(data.item)")
|
|
||||||
}
|
|
||||||
.action(kConfigurableTableViewCellButtonClickedAction) { data -> Void in
|
|
||||||
|
|
||||||
print("custom action indexPath: \(data.indexPath), item: \(data.item)")
|
|
||||||
}
|
|
||||||
.action(.height) { data -> ReturnValue in
|
|
||||||
|
|
||||||
if data.item == "5" {
|
|
||||||
return 70
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
.action(.configure) { (data) -> Void in
|
|
||||||
|
|
||||||
data.cell!.contentLabel.text = "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."
|
|
||||||
}
|
|
||||||
|
|
||||||
let myRowBuilder = TableRowBuilder<Int, MyTableViewCell>(item: 0, id: "cellll", estimatedRowHeight: 44)
|
|
||||||
|
|
||||||
let sectionBuilder = TableSectionBuilder(headerTitle: "Tablet", footerTitle: "Deal with table view like a boss.", rowBuilders: [rowBuilder, configurableRowBuilder, myRowBuilder])
|
|
||||||
|
|
||||||
tableDirector += sectionBuilder
|
|
||||||
|
|
||||||
sectionBuilder.appendRowBuilder(TableRowBuilder<Int, MyNibTableViewCell>(item: 0, estimatedRowHeight: 44))
|
|
||||||
}
|
|
||||||
|
|
||||||
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
|
|
||||||
print("begin dragging")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue