Compare commits

...

103 Commits

Author SHA1 Message Date
Ivan Smolin fec9537745 up swift version to 5.7 2024-01-30 23:09:39 +03:00
Ivan Smolin 246c0d06c0 Merge pull request 'remove outdated actions and swift tools version' (#2) from feature/remote_outdated_code into master
Reviewed-on: #2
2024-01-30 22:27:14 +03:00
Ivan Smolin 54bf141aff remove outdated actions and swift tools version 2024-01-30 22:24:27 +03:00
Ivan Smolin 01134b83b4 update source url 2024-01-30 22:13:02 +03:00
Ivan Smolin 3fefa09c3a Merge pull request 'fix warnings' (#1) from fix/xcode_15 into master
Reviewed-on: #1
2024-01-30 22:08:00 +03:00
Ivan Smolin 4886e43d20 fix warnings 2024-01-30 22:01:31 +03:00
Loupehope 41826e18db
Merge pull request #6 from TouchInstinct/feature/actionRows
Replace UITableViewRowAction with UIContextualAction
2020-12-26 15:37:30 +03:00
Vlad caec2dd10e Return deprecated 2020-12-26 15:22:00 +03:00
Vlad efc03d7c22 Add performsFirstActionWithFullSwipe 2020-12-26 11:24:59 +03:00
Vlad 94c40faa63 Code correction 2020-12-26 11:05:20 +03:00
Vlad d87a23587e Add backward capability to editingActions 2020-12-26 10:52:06 +03:00
Vlad 49b3f868f3 Return IPHONEOS_DEPLOYMENT_TARGET 2020-12-26 10:36:32 +03:00
Vlad 37482d3b69 Replace UITableViewRowAction with UIContextualAction 2020-12-26 10:31:38 +03:00
Loupehope 2cc161f0c0
Merge pull request #5 from maxsokolov/master
Merge source master
2020-12-26 10:16:07 +03:00
Max Sokolov 8bf4840d9d bump to 2.11.0 2020-05-04 08:11:57 -04:00
Max Sokolov 3b266fb7c7
Merge pull request #98 from mrtokii/feature/ios13_actions
Add support for iOS 13 actions
2020-05-04 08:06:36 -04:00
mrtokii eb93fe253c Add multiple selection actions & context menu action 2020-05-03 16:25:30 +03:00
Max Sokolov 44c55d2f05
Merge pull request #96 from DmitryFrishbuter/readme-patch
Fix typo in README.md
2019-10-05 15:10:11 -04:00
Dmitry Frishbuter efe99eeb46 Fix typo in README.md 2019-10-05 21:57:35 +06:00
Max Sokolov a0658f0b2e fix travis-ci 2019-09-29 10:03:45 -04:00
Max Sokolov 065cd9ace3 fix travis-ci 2019-09-29 09:56:11 -04:00
Max Sokolov c3652eec6f bump podspec 2019-09-29 09:51:33 -04:00
Max Sokolov 01bf1e01e3 support swift 5.1 and xcode 11 2019-09-29 09:50:40 -04:00
Max Sokolov 45aee4522d
Merge pull request #95 from bellebethcooper/master
Updated Swift tools version and added missing requirements
2019-09-19 16:45:06 -04:00
bellebethcooper f39e7ac602
Updated Swift tools version and added missing requirements 2019-09-07 15:49:07 +10:00
Max Sokolov dc1a924a80 drop iOS 9 checks on ci 2019-06-25 14:45:02 +03:00
Max Sokolov 402757c41f drop iOS 9 checks on ci 2019-06-25 14:36:47 +03:00
Max Sokolov d6520346c3
Merge pull request #94 from dmertsalov/master
Add accessoryButton action support
2019-05-29 09:34:05 +03:00
Denis 959032a03f Add accessoryButton action support 2019-05-27 11:01:35 +03:00
Max Sokolov 00de0cd809
Merge pull request #93 from IntelBohdan/master
added: willDeselect
2019-05-10 19:35:09 +03:00
Bogdan Kurpakov 337175c507 added: willDeselect 2019-05-09 11:10:31 +02:00
Max Sokolov a4bacd2c16 fix travis ci 2019-04-04 23:40:25 +03:00
Max Sokolov 603264a6d1 fix travis ci 2019-04-04 23:31:40 +03:00
Max Sokolov 1d4fdaad0d update readme 2019-04-04 23:27:25 +03:00
Max Sokolov f5ad500ba4 support swift 5.0, bump to 2.9.0 2019-04-04 23:26:15 +03:00
Ivan Zinovyev 1b4a988b35
Merge pull request #4 from TouchInstinct/expandable_states
Expandable states
2019-01-22 13:06:16 +03:00
Ivan Zinovyev f8f2ca6852 Fix indentation 2019-01-22 03:16:40 +03:00
Ivan Zinovyev ff18a4d8b8 Add Expandable States 2019-01-22 03:00:41 +03:00
Ivan Zinovyev d098691621 Merge remote-tracking branch 'maxsokolov/master' into expandable_states 2019-01-22 02:51:16 +03:00
Max Sokolov 8b6319d510 bump to 2.8.1 2019-01-10 18:35:53 +03:00
Max Sokolov b049864758 fix crash on iOS 10.3.1 2019-01-10 18:35:35 +03:00
Ivan Zinovyev 7f349c6944
Merge pull request #2 from TouchInstinct/feature/expandable
Feature/expandable
2019-01-09 19:13:15 +03:00
Ivan Zinovyev a768352b47 Some refactoring 2019-01-09 18:59:29 +03:00
Ivan Zinovyev 65cf31717b Remove deprecated attribute for estimatedHeight 2019-01-09 16:08:25 +03:00
Ivan Zinovyev 9d10bc18bf Remove manual height calculation functionality 2019-01-09 16:05:07 +03:00
Ivan Zinovyev 497b4e009c Make ExpandableCellHeightCalculator optional, not default 2019-01-09 16:04:32 +03:00
Ivan Zinovyev 6ef5ad504e Add initState for Expandable 2018-12-12 19:06:50 +03:00
Ivan Zinovyev e22ec03990 Add layout type 2018-12-12 19:03:16 +03:00
Ivan Zinovyev 5982d5db3a Use ExpandableCellHeightCalculator by default 2018-12-11 12:16:32 +03:00
Ivan Zinovyev 6eaf2cf3a2 Add Expandable protocol to expand/collapse cells 2018-12-10 17:15:43 +03:00
Ivan Zinovyev 921e2b42b3
Merge pull request #1 from iznv/height_for_item
Height for item
2018-11-27 12:30:04 +03:00
Ivan Zinovyev 70e6addcd0 Make AccurateCellHeightCalculator public 2018-11-04 08:31:46 +03:00
Ivan Zinovyev 86f07b8e7c Add AccurateCellHeightCalculator 2018-11-04 08:28:50 +03:00
Ivan Zinovyev 1c92c14a1a Fix for new swift 2018-11-04 08:20:28 +03:00
Ivan Zinovyev 7416271076 Fix type 2018-11-04 08:18:06 +03:00
Ivan Zinovyev 0812293813 Merge remote-tracking branch 'maxsokolov/master' into height_for_item 2018-11-04 08:16:15 +03:00
Max Sokolov f16a5a29c9 fix travis ci 2018-09-30 12:49:21 +03:00
Max Sokolov b379ac6272 fix travis ci 2018-09-30 12:42:46 +03:00
Max Sokolov 0c87f3ee88 update changelog 2018-09-30 12:37:38 +03:00
Max Sokolov ce24369557 bump to swift 4.2, support xcode10 2018-09-30 12:35:48 +03:00
Max Sokolov 85a45c33f2
Merge pull request #86 from petropavel13/swift_4_2
Swift 4.2, Xcode 10
2018-09-19 23:52:23 +03:00
Ivan Smolin 92b723b8ca perform automatic migration with warning suggestions 2018-09-19 12:03:51 +03:00
Ivan Smolin 12883a33de update project to recommended settings 2018-09-19 12:01:11 +03:00
Max Sokolov 1d28233ebe meaningful associatedtype name for ConfigurableCell 2018-06-26 22:37:40 +03:00
Max Sokolov dd8e5d0625
Merge pull request #81 from oxview/master
added: canDelete, editingStyle
2018-05-09 03:51:42 +09:00
into 21aab6256f added: canMoveTo 2018-05-08 16:54:13 +02:00
into cc02531b27 added: canDelete, editingStyle 2018-05-08 13:24:52 +02:00
Ivan Zinovyev b62dea8702 Add default implementation for height(for:) 2018-04-22 01:59:58 +03:00
Ivan Zinovyev 279fdd4854 Add height for item 2018-04-22 01:50:04 +03:00
Max Sokolov 02f77ed699
Merge pull request #80 from desyatov/master
Implement tableView:canMoveRowAt:indexPath method
2018-04-07 04:10:03 +09:00
Alexander Desyatov 74af45f3cd Implement tableView:canMoveRowAt:indexPath method 2018-04-06 20:22:29 +03:00
Max Sokolov f05511cfba
Merge pull request #79 from maxsokolov/bugfixes-and-improvements
Bugfixes and improvements
2018-03-17 16:25:58 +09:00
Max Sokolov de5593f6a9 bump pod version 2018-03-17 00:06:32 +03:00
Max Sokolov a1e1e9d908 add bounds check 2018-03-17 00:03:53 +03:00
Max Sokolov 70d83fc5b0 some codestyle improvements 2018-03-17 00:01:20 +03:00
Max Sokolov 23b51227fd add clear button 2018-03-16 23:54:33 +03:00
Max Sokolov d8ae329118
Merge pull request #77 from mrojas/master
Added support for moving rows
2018-03-13 14:00:07 +09:00
Matias Rojas fd50b732c9 Added support for moving rows 2018-03-13 00:43:00 +03:00
Matias Rojas 709f739ab6 Added support to move rows 2018-03-13 00:16:17 +03:00
Max Sokolov 9fff8c861c
Merge pull request #74 from astrokin/master
add swap rows func. usefull for table row reordering while editing
2018-02-23 12:23:56 +03:00
astrokin da76188afd add swap rows func. usefull for table row reordering while editing 2018-02-23 01:16:39 +03:00
Max Sokolov e3870d5ce8
Merge pull request #73 from sc0rch/master
Add new action .didEndDisplaying
2018-01-18 22:36:06 +03:00
Anton Aleshkevich a7488b1d4f Add new action .didEndDisplaying 2018-01-18 15:39:36 +03:00
Max Sokolov 4712406618 add example with autolayout section header view 2017-12-13 23:12:23 +03:00
Max Sokolov d595a5d2df update travis config 2017-09-24 21:14:58 +03:00
Max Sokolov 8463401b94 update travis config 2017-09-24 21:11:58 +03:00
Max Sokolov 05119aae43 change log update 2017-09-24 21:02:51 +03:00
Max Sokolov f3c7ea9e43 fix readme 2017-09-24 21:00:45 +03:00
Max Sokolov ae24e5c7c0 bump version 2017-09-24 20:58:16 +03:00
Max Sokolov 0c96110ca2 fix test naming 2017-09-24 20:55:37 +03:00
Max Sokolov ad979f4376 fix warnings 2017-09-24 20:54:06 +03:00
Max Sokolov 6f849e4cae bump xcode proj to swift 4.0 2017-09-24 20:52:22 +03:00
Max Sokolov 293caeebdb Merge pull request #65 from jesster2k10/patch-1
Update README.md
2017-07-07 18:14:21 +03:00
Jesse Onolememen cc69951856 Update README.md
fixed formatting
2017-07-07 13:48:05 +01:00
Maxim Sokolov 898191b0fa remove didEndDisplaying
provides problematic behaviour
2017-06-08 16:17:58 +03:00
Max Sokolov bd8ac5a28b Merge pull request #64 from motylevm/master
didEndDisplaying cell action support
2017-06-07 17:46:38 +03:00
Mikhail Motylev 9ef7a7a8bb version up 2017-06-07 17:45:23 +03:00
Mikhail Motylev ce2874c58d didEndDisplay support 2017-06-07 17:36:02 +03:00
motylevm c9bd52797d Merge pull request #1 from maxsokolov/master
Merge from source repo
2017-06-07 17:20:59 +03:00
Max Sokolov 06becd8624 Merge pull request #62 from pegurov/master
Added support for table section index
2017-04-17 12:00:33 +03:00
Pavel Gurov c277b6529c Renamed to sectionsIndexTitlesIndexes, and it is now nil if not used 2017-04-13 14:13:44 +03:00
Pavel Gurov 39b4df6ee9 moved indexTitle to TableSection 2017-04-13 11:37:08 +03:00
Pavel Gurov b49b01ff59 Added support for table section index 2017-04-13 10:52:29 +03:00
36 changed files with 821 additions and 214 deletions

View File

@ -1 +1 @@
3.0 5.7

View File

@ -1,5 +1,5 @@
language: objective-c language: objective-c
osx_image: xcode8 osx_image: xcode11
branches: branches:
only: only:
- master - master
@ -7,20 +7,18 @@ env:
global: global:
- LC_CTYPE=en_US.UTF-8 - LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8 - LANG=en_US.UTF-8
- IOS_SDK=iphonesimulator10.0 - IOS_SDK=iphonesimulator13.0
- SCHEME_IOS="TableKit" - SCHEME_IOS="TableKit"
- PROJECT_FRAMEWORK="TableKit.xcodeproj" - PROJECT_FRAMEWORK="TableKit.xcodeproj"
matrix: matrix:
- DESTINATION="OS=9.0,name=iPhone 6" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK" - DESTINATION="OS=10.3.1,name=iPhone 5" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.1,name=iPhone 6 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK" - DESTINATION="OS=11.1,name=iPhone 6" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.2,name=iPhone 6S" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK" - DESTINATION="OS=12.0,name=iPhone 7 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6S Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK" - DESTINATION="OS=13.0,name=iPhone 11" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=10.0,name=iPhone 5" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=10.0,name=iPhone 7 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
script: script:
- set -o pipefail - set -o pipefail
- xcodebuild -version - xcodebuild -version
- xcodebuild -showsdks - xcodebuild -showsdks
- xcodebuild -project "$PROJECT_FRAMEWORK" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty; - 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

View File

@ -2,6 +2,22 @@
All notable changes to this project will be documented in this file. 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) ## [2.3.0](https://github.com/maxsokolov/TableKit/releases/tag/2.3.0)
Released on 2016-11-16. Released on 2016-11-16.
- `shouldUsePrototypeCellHeightCalculation` moved to `TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)` - `shouldUsePrototypeCellHeightCalculation` moved to `TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)`

View File

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

View File

@ -1,11 +1,3 @@
//
// AutolayoutCellsController.swift
// TableKitDemo
//
// Created by Max Sokolov on 18/06/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit
@ -43,7 +35,9 @@ class AutolayoutCellsController: UIViewController {
title = "Autolayout cells" title = "Autolayout cells"
let section = TableSection() let header = AutolayoutSectionHeaderView.loadFromNib()
let section = TableSection(headerView: header, footerView: nil)
section.headerHeight = getViewHeight(view: header, width: UIScreen.main.bounds.width)
var rows = 0 var rows = 0
while rows <= 20 { while rows <= 20 {
@ -54,5 +48,23 @@ class AutolayoutCellsController: UIViewController {
} }
tableDirector += section tableDirector += section
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Clear", style: .done, target: self, action: #selector(clear))
}
@objc
func clear() {
tableDirector.clear()
tableDirector.reload()
}
func getViewHeight(view: UIView, width: CGFloat) -> CGFloat {
view.frame = CGRect(x: 0, y: 0, width: width, height: view.frame.size.height)
view.setNeedsLayout()
view.layoutIfNeeded()
return view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
} }
} }

View File

@ -1,11 +1,3 @@
//
// MainController.swift
// TabletDemo
//
// Created by Max Sokolov on 16/04/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit

View File

@ -1,11 +1,3 @@
//
// NibCellsController.swift
// TableKitDemo
//
// Created by Max Sokolov on 18/06/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit

View File

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

View File

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

View File

@ -1,11 +1,3 @@
//
// AutolayoutTableViewCell.swift
// TabletDemo
//
// Created by Max Sokolov on 24/05/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit

View File

@ -1,11 +1,3 @@
//
// ConfigurableTableViewCell.swift
// TableKitDemo
//
// Created by Max Sokolov on 18/06/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit

View File

@ -1,11 +1,3 @@
//
// NibTableViewCell.swift
// TableKitDemo
//
// Created by Max Sokolov on 18/06/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit import UIKit
import TableKit import TableKit
@ -20,4 +12,4 @@ class NibTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with number: Int) { func configure(with number: Int) {
titleLabel.text = "\(number)" titleLabel.text = "\(number)"
} }
} }

View File

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
5079ADE31FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079ADE21FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift */; };
5079ADE61FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5079ADE51FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib */; };
DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */; }; DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */; };
DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465C1D1569CC00AA83EE /* AutolayoutCellsController.swift */; }; DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465C1D1569CC00AA83EE /* AutolayoutCellsController.swift */; };
DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */; }; DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */; };
@ -61,6 +63,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference 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>"; }; 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>"; }; 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>"; }; DA55465F1D156A4F00AA83EE /* ConfigurableTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableTableViewCell.swift; sourceTree = "<group>"; };
@ -194,6 +198,8 @@
DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */, DA08A0521CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift */,
DA5546631D15762000AA83EE /* NibTableViewCell.swift */, DA5546631D15762000AA83EE /* NibTableViewCell.swift */,
DA5546651D15765900AA83EE /* NibTableViewCell.xib */, DA5546651D15765900AA83EE /* NibTableViewCell.xib */,
5079ADE21FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift */,
5079ADE51FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -227,13 +233,13 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0720; LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0800; LastUpgradeCheck = 1000;
ORGANIZATIONNAME = Tablet; ORGANIZATIONNAME = Tablet;
TargetAttributes = { TargetAttributes = {
DAB7EB261BEF787300D2AD5E = { DAB7EB261BEF787300D2AD5E = {
CreatedOnToolsVersion = 7.0.1; CreatedOnToolsVersion = 7.0.1;
DevelopmentTeam = Z48R734SJX; DevelopmentTeam = Z48R734SJX;
LastSwiftMigration = 0800; LastSwiftMigration = 1000;
}; };
}; };
}; };
@ -242,6 +248,7 @@
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
Base, Base,
); );
@ -284,6 +291,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */, DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */,
5079ADE61FE1BF65000CC345 /* AutolayoutSectionHeaderView.xib in Resources */,
DA5546661D15765900AA83EE /* NibTableViewCell.xib in Resources */, DA5546661D15765900AA83EE /* NibTableViewCell.xib in Resources */,
DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */, DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */,
DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */, DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */,
@ -301,6 +309,7 @@
DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */, DA55465D1D1569CC00AA83EE /* AutolayoutCellsController.swift in Sources */,
DA5546681D15771D00AA83EE /* NibCellsController.swift in Sources */, DA5546681D15771D00AA83EE /* NibCellsController.swift in Sources */,
DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */, DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */,
5079ADE31FE1BF1B000CC345 /* AutolayoutSectionHeaderView.swift in Sources */,
DA5546641D15762000AA83EE /* NibTableViewCell.swift in Sources */, DA5546641D15762000AA83EE /* NibTableViewCell.swift in Sources */,
DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */, DA5546601D156A4F00AA83EE /* ConfigurableTableViewCell.swift in Sources */,
DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */, DA08A0531CF4E9B500BBF1F8 /* AutolayoutTableViewCell.swift in Sources */,
@ -326,14 +335,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -361,6 +378,7 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
@ -372,14 +390,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -400,6 +426,7 @@
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
@ -417,7 +444,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = TableKitDemo; PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
@ -434,7 +461,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = TableKitDemo; PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Release; name = Release;
}; };

View File

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

View File

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

View File

@ -1,10 +1,10 @@
#TableKit # TableKit
<p align="left"> <p align="left">
<a href="https://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></a> <a href="https://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></a>
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_3.0-compatible-4BC51D.svg?style=flat" alt="Swift 3.0 compatible" /></a> <a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_5.1-compatible-4BC51D.svg?style=flat" alt="Swift 5.1 compatible" /></a>
<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://github.com/Carthage/Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage compatible" /></a>
<a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-2.3.1-blue.svg" alt="CocoaPods compatible" /></a> <a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-2.11.0-blue.svg" alt="CocoaPods compatible" /></a>
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" /> <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> <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>
@ -53,29 +53,29 @@ Done. Your table is ready. Your cells have to conform to `ConfigurableCell` prot
```swift ```swift
class StringTableViewCell: UITableViewCell, ConfigurableCell { class StringTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with string: String) { func configure(with string: String) {
textLabel?.text = string textLabel?.text = string
} }
} }
class UserTableViewCell: UITableViewCell, ConfigurableCell { class UserTableViewCell: UITableViewCell, ConfigurableCell {
static var estimatedHeight: CGFloat? { static var estimatedHeight: CGFloat? {
return 100 return 100
}
// is not required to be implemented
// by default reuse id is equal to cell's class name
static var reuseIdentifier: String {
return "my id"
} }
func configure(with user: User) { // is not required to be implemented
// 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 textLabel?.text = user.name
detailTextLabel?.text = "Rating: \(user.rating)" detailTextLabel?.text = "Rating: \(user.rating)"
} }
} }
``` ```
You could have as many rows and sections as you need. You could have as many rows and sections as you need.
@ -86,12 +86,12 @@ It nice to have some actions that related to your cells:
```swift ```swift
let action = TableRowAction<StringTableViewCell>(.click) { (options) in let action = TableRowAction<StringTableViewCell>(.click) { (options) in
// you could access any useful information that relates to the action // you could access any useful information that relates to the action
// options.cell - StringTableViewCell? // options.cell - StringTableViewCell?
// options.item - String // options.item - String
// options.indexPath - IndexPath // options.indexPath - IndexPath
// options.userInfo - [AnyHashable: Any]? // options.userInfo - [AnyHashable: Any]?
} }
let row = TableRow<StringTableViewCell>(item: "some", actions: [action]) let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
@ -99,12 +99,12 @@ let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
Or, using nice chaining approach: Or, using nice chaining approach:
```swift ```swift
let row = TableRow<StringTableViewCell>(item: "some") let row = TableRow<StringTableViewCell>(item: "some")
.on(.click) { (options) in .on(.click) { (options) in
} }
.on(.shouldHighlight) { (options) -> Bool in .on(.shouldHighlight) { (options) -> Bool in
return false return false
} }
``` ```
You could find all available actions [here](Sources/TableRowAction.swift). You could find all available actions [here](Sources/TableRowAction.swift).
@ -119,10 +119,10 @@ struct MyActions {
class MyTableViewCell: UITableViewCell, ConfigurableCell { class MyTableViewCell: UITableViewCell, ConfigurableCell {
@IBAction func myButtonClicked(sender: UIButton) { @IBAction func myButtonClicked(sender: UIButton) {
TableCellAction(key: MyActions.ButtonClicked, sender: self).invoke() TableCellAction(key: MyActions.ButtonClicked, sender: self).invoke()
} }
} }
``` ```
And handle them accordingly: And handle them accordingly:
@ -158,9 +158,9 @@ By default TableKit relies on <a href="https://developer.apple.com/library/ios/d
```swift ```swift
class StringTableViewCell: UITableViewCell, ConfigurableCell { class StringTableViewCell: UITableViewCell, ConfigurableCell {
// ... // ...
static var estimatedHeight: CGFloat? { static var estimatedHeight: CGFloat? {
return 255 return 255
} }
} }
@ -173,16 +173,16 @@ It does all dirty work with prototypes for you [behind the scene](Sources/TableP
```swift ```swift
class ImageTableViewCell: UITableViewCell, ConfigurableCell { class ImageTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with url: NSURL) { func configure(with url: NSURL) {
loadImageAsync(url: url, imageView: imageView) loadImageAsync(url: url, imageView: imageView)
} }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
contentView.layoutIfNeeded() contentView.layoutIfNeeded()
multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width multilineLabel.preferredMaxLayoutWidth = multilineLabel.bounds.size.width
} }
} }
``` ```
@ -204,7 +204,7 @@ tableDirector += rows
Done, your table is ready. Done, your table is ready.
## Automatic cell registration ## Automatic cell registration
TableKit can register your cells in a table view automatically. In case if your reusable cell id mathces cell's xib name: TableKit can register your cells in a table view automatically. In case if your reusable cell id matches cell's xib name:
```ruby ```ruby
MyTableViewCell.swift MyTableViewCell.swift
@ -233,12 +233,12 @@ Clone the repo and drag files from `Sources` folder into your Xcode project.
# Requirements # Requirements
- iOS 8.0 - iOS 8.0
- Xcode 8.0 - Xcode 9.0
# Changelog # Changelog
Keep eye on [changes](CHANGELOG.md). Keep an eye on [changes](CHANGELOG.md).
# License # License
TableKit is available under the MIT license. See LICENSE for details. TableKit is available under the MIT license. See LICENSE for details.

View File

@ -22,13 +22,18 @@ import UIKit
public protocol ConfigurableCell { public protocol ConfigurableCell {
associatedtype T associatedtype CellData
static var reuseIdentifier: String { get } static var reuseIdentifier: String { get }
static var estimatedHeight: CGFloat? { get } static var estimatedHeight: CGFloat? { get }
static var defaultHeight: CGFloat? { get } static var defaultHeight: CGFloat? { get }
func configure(with _: T) static var layoutType: LayoutType { get }
func configure(with _: CellData)
} }
public extension ConfigurableCell where Self: UITableViewCell { public extension ConfigurableCell where Self: UITableViewCell {
@ -44,4 +49,9 @@ public extension ConfigurableCell where Self: UITableViewCell {
static var defaultHeight: CGFloat? { static var defaultHeight: CGFloat? {
return nil return nil
} }
static var layoutType: LayoutType {
return .auto
}
} }

96
Sources/Expandable.swift Normal file
View File

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

View File

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

View File

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

View File

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

7
Sources/LayoutType.swift Normal file
View File

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

View File

@ -27,13 +27,13 @@ import UIKit
open class TableCellAction { open class TableCellAction {
/// The cell that triggers an action. /// The cell that triggers an action.
open let cell: UITableViewCell public let cell: UITableViewCell
/// The action unique key. /// The action unique key.
open let key: String public let key: String
/// The custom user info. /// The custom user info.
open let userInfo: [AnyHashable: Any]? public let userInfo: [AnyHashable: Any]?
public init(key: String, sender: UITableViewCell, userInfo: [AnyHashable: Any]? = nil) { public init(key: String, sender: UITableViewCell, userInfo: [AnyHashable: Any]? = nil) {

View File

@ -48,7 +48,7 @@ class TableCellRegisterer {
// in that case we could register nib // in that case we could register nib
if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") { if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") {
tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier) tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier)
// otherwise, register cell class // otherwise, register cell class
} else { } else {
tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier) tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier)
} }

View File

@ -31,7 +31,8 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
private weak var scrollDelegate: UIScrollViewDelegate? private weak var scrollDelegate: UIScrollViewDelegate?
private var cellRegisterer: TableCellRegisterer? private var cellRegisterer: TableCellRegisterer?
public private(set) var rowHeightCalculator: RowHeightCalculator? public private(set) var rowHeightCalculator: RowHeightCalculator?
private var sectionsIndexTitlesIndexes: [Int]?
@available(*, deprecated, message: "Produced incorrect behaviour") @available(*, deprecated, message: "Produced incorrect behaviour")
open var shouldUsePrototypeCellHeightCalculation: Bool = false { open var shouldUsePrototypeCellHeightCalculation: Bool = false {
didSet { didSet {
@ -47,7 +48,12 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
return sections.isEmpty return sections.isEmpty
} }
public init(tableView: UITableView, scrollDelegate: UIScrollViewDelegate? = nil, shouldUseAutomaticCellRegistration: Bool = true, cellHeightCalculator: RowHeightCalculator?) { public init(
tableView: UITableView,
scrollDelegate: UIScrollViewDelegate? = nil,
shouldUseAutomaticCellRegistration: Bool = true,
cellHeightCalculator: RowHeightCalculator?)
{
super.init() super.init()
if shouldUseAutomaticCellRegistration { if shouldUseAutomaticCellRegistration {
@ -63,11 +69,22 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveAction), name: NSNotification.Name(rawValue: TableKitNotifications.CellAction), object: nil) 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) { public convenience init(
tableView: UITableView,
let heightCalculator: TablePrototypeCellHeightCalculator? = shouldUsePrototypeCellHeightCalculation ? TablePrototypeCellHeightCalculator(tableView: tableView) : nil 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) self.init(
tableView: tableView,
scrollDelegate: scrollDelegate,
shouldUseAutomaticCellRegistration: shouldUseAutomaticCellRegistration,
cellHeightCalculator: heightCalculator
)
} }
deinit { deinit {
@ -78,11 +95,28 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
tableView?.reloadData() tableView?.reloadData()
} }
// MARK: Public // 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 @discardableResult
open func invoke(action: TableRowActionType, cell: UITableViewCell?, indexPath: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? { open func invoke(
return sections[indexPath.section].rows[indexPath.row].invoke(action: action, cell: cell, path: indexPath, userInfo: userInfo) 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 { open override func responds(to selector: Selector) -> Bool {
@ -90,15 +124,18 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
} }
open override func forwardingTarget(for selector: Selector) -> Any? { open override func forwardingTarget(for selector: Selector) -> Any? {
return scrollDelegate?.responds(to: selector) == true ? scrollDelegate : super.forwardingTarget(for: selector) return scrollDelegate?.responds(to: selector) == true
? scrollDelegate
: super.forwardingTarget(for: selector)
} }
// MARK: - Internal // MARK: - Internal
func hasAction(_ action: TableRowActionType, atIndexPath indexPath: IndexPath) -> Bool { func hasAction(_ action: TableRowActionType, atIndexPath indexPath: IndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].has(action: action) guard let row = row(at: indexPath) else { return false }
return row.has(action: action)
} }
@objc
func didReceiveAction(_ notification: Notification) { func didReceiveAction(_ notification: Notification) {
guard let action = notification.object as? TableCellAction, let indexPath = tableView?.indexPath(for: action.cell) else { return } guard let action = notification.object as? TableCellAction, let indexPath = tableView?.indexPath(for: action.cell) else { return }
@ -106,7 +143,6 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
} }
// MARK: - Height // MARK: - Height
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row] let row = sections[indexPath.section].rows[indexPath.row]
@ -115,7 +151,10 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier) cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
} }
return row.defaultHeight ?? row.estimatedHeight ?? rowHeightCalculator?.estimatedHeight(forRow: row, at: indexPath) ?? UITableViewAutomaticDimension return row.defaultHeight
?? row.estimatedHeight
?? rowHeightCalculator?.estimatedHeight(forRow: row, at: indexPath)
?? UITableView.automaticDimension
} }
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
@ -128,16 +167,20 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
let rowHeight = invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat let rowHeight = invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat
return rowHeight ?? row.defaultHeight ?? rowHeightCalculator?.height(forRow: row, at: indexPath) ?? UITableViewAutomaticDimension return rowHeight
?? row.defaultHeight
?? rowHeightCalculator?.height(forRow: row, at: indexPath)
?? UITableView.automaticDimension
} }
// MARK: UITableViewDataSource - configuration // MARK: UITableViewDataSource - configuration
open func numberOfSections(in tableView: UITableView) -> Int { open func numberOfSections(in tableView: UITableView) -> Int {
return sections.count return sections.count
} }
open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section < sections.count else { return 0 }
return sections[section].numberOfRows return sections[section].numberOfRows
} }
@ -161,41 +204,78 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
} }
// MARK: UITableViewDataSource - section setup // MARK: UITableViewDataSource - section setup
open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard section < sections.count else { return nil }
return sections[section].headerTitle return sections[section].headerTitle
} }
open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
guard section < sections.count else { return nil }
return sections[section].footerTitle return sections[section].footerTitle
} }
// MARK: UITableViewDelegate - section setup // MARK: UITableViewDelegate - section setup
open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard section < sections.count else { return nil }
return sections[section].headerView return sections[section].headerView
} }
open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard section < sections.count else { return nil }
return sections[section].footerView return sections[section].footerView
} }
open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard section < sections.count else { return 0 }
let section = sections[section] let section = sections[section]
return section.headerHeight ?? section.headerView?.frame.size.height ?? UITableViewAutomaticDimension return section.headerHeight ?? section.headerView?.frame.size.height ?? UITableView.automaticDimension
} }
open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
guard section < sections.count else { return 0 }
let section = sections[section] let section = sections[section]
return section.footerHeight ?? section.footerView?.frame.size.height ?? UITableViewAutomaticDimension 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 // MARK: UITableViewDelegate - actions
open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) let cell = tableView.cellForRow(at: indexPath)
if invoke(action: .click, cell: cell, indexPath: indexPath) != nil { if invoke(action: .click, cell: cell, indexPath: indexPath) != nil {
@ -212,71 +292,151 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
invoke(action: .willDisplay, cell: cell, 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 { open func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return invoke(action: .shouldHighlight, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? true return invoke(action: .shouldHighlight, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? true
} }
open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if hasAction(.willSelect, atIndexPath: indexPath) { if hasAction(.willSelect, atIndexPath: indexPath) {
return invoke(action: .willSelect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? IndexPath return invoke(action: .willSelect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? IndexPath
} }
return 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 // MARK: - Row editing
open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath) return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath)
} }
open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { open func tableView(_ tableView: UITableView,
return sections[indexPath.section].rows[indexPath.row].editingActions 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, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 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 { if editingStyle == .delete {
invoke(action: .clickDelete, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) 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 // MARK: - Sections manipulation
extension TableDirector { extension TableDirector {
@discardableResult @discardableResult
open func append(section: TableSection) -> Self { public func append(section: TableSection) -> Self {
append(sections: [section]) append(sections: [section])
return self return self
} }
@discardableResult @discardableResult
open func append(sections: [TableSection]) -> Self { public func append(sections: [TableSection]) -> Self {
self.sections.append(contentsOf: sections) self.sections.append(contentsOf: sections)
return self return self
} }
@discardableResult @discardableResult
open func append(rows: [Row]) -> Self { public func append(rows: [Row]) -> Self {
append(section: TableSection(rows: rows)) append(section: TableSection(rows: rows))
return self return self
} }
@discardableResult @discardableResult
open func insert(section: TableSection, atIndex index: Int) -> Self { public func insert(section: TableSection, atIndex index: Int) -> Self {
sections.insert(section, at: index) sections.insert(section, at: index)
return self return self
} }
@discardableResult @discardableResult
open func replaceSection(at index: Int, with section: TableSection) -> Self { public func replaceSection(at index: Int, with section: TableSection) -> Self {
if index < sections.count { if index < sections.count {
sections[index] = section sections[index] = section
} }
@ -284,20 +444,20 @@ extension TableDirector {
} }
@discardableResult @discardableResult
open func delete(sectionAt index: Int) -> Self { public func delete(sectionAt index: Int) -> Self {
sections.remove(at: index) sections.remove(at: index)
return self return self
} }
@discardableResult @discardableResult
open func remove(sectionAt index: Int) -> Self { public func remove(sectionAt index: Int) -> Self {
return delete(sectionAt: index) return delete(sectionAt: index)
} }
@discardableResult @discardableResult
open func clear() -> Self { public func clear() -> Self {
rowHeightCalculator?.invalidate() rowHeightCalculator?.invalidate()
sections.removeAll() sections.removeAll()
@ -305,10 +465,9 @@ extension TableDirector {
} }
// MARK: - deprecated methods // MARK: - deprecated methods
@available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead") @available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead")
@discardableResult @discardableResult
open func delete(index: Int) -> Self { public func delete(index: Int) -> Self {
sections.remove(at: index) sections.remove(at: index)
return self return self

View File

@ -24,17 +24,31 @@ struct TableKitNotifications {
static let CellAction = "TableKitNotificationsCellAction" 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 { public protocol RowConfigurable {
func configure(_ cell: UITableViewCell) func configure(_ cell: UITableViewCell)
} }
public protocol RowActionable { public protocol RowActionable {
var leadingContextualActions: [UIContextualAction] { get }
var editingActions: [UITableViewRowAction]? { get } var trailingContextualActions: [UIContextualAction] { get }
var performsFirstActionWithFullSwipe: Bool { get }
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? func invoke(
action: TableRowActionType,
cell: UITableViewCell?,
path: IndexPath,
userInfo: [AnyHashable: Any]?) -> Any?
func has(action: TableRowActionType) -> Bool func has(action: TableRowActionType) -> Bool
} }
@ -47,7 +61,8 @@ public protocol Row: RowConfigurable, RowActionable, RowHashable {
var reuseIdentifier: String { get } var reuseIdentifier: String { get }
var cellType: AnyClass { get } var cellType: AnyClass { get }
var layoutType: LayoutType { get }
var estimatedHeight: CGFloat? { get } var estimatedHeight: CGFloat? { get }
var defaultHeight: CGFloat? { get } var defaultHeight: CGFloat? { get }
} }
@ -59,11 +74,21 @@ public enum TableRowActionType {
case select case select
case deselect case deselect
case willSelect case willSelect
case willDeselect
case willDisplay case willDisplay
case didEndDisplaying
case shouldHighlight case shouldHighlight
case shouldBeginMultipleSelection
case didBeginMultipleSelection
case height case height
case canEdit case canEdit
case configure case configure
case canDelete
case canMove
case canMoveTo
case move
case showContextMenu
case accessoryButtonTap
case custom(String) case custom(String)
var key: String { var key: String {

View File

@ -57,7 +57,7 @@ open class TablePrototypeCellHeightCalculator: RowHeightCalculator {
cell.setNeedsLayout() cell.setNeedsLayout()
cell.layoutIfNeeded() cell.layoutIfNeeded()
let height = cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height + (tableView.separatorStyle != .none ? separatorHeight : 0) let height = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height + (tableView.separatorStyle != .none ? separatorHeight : 0)
cachedHeights[hash] = height cachedHeights[hash] = height
@ -78,7 +78,7 @@ open class TablePrototypeCellHeightCalculator: RowHeightCalculator {
return estimatedHeight return estimatedHeight
} }
return UITableViewAutomaticDimension return UITableView.automaticDimension
} }
open func invalidate() { open func invalidate() {

View File

@ -21,10 +21,21 @@
import UIKit import UIKit
open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell { open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell {
open let item: CellType.T public let item: CellType.CellData
private lazy var actions = [String: [TableRowAction<CellType>]]() private lazy var actions = [String: [TableRowAction<CellType>]]()
private(set) open var editingActions: [UITableViewRowAction]?
open var leadingContextualActions: [UIContextualAction] {
[]
}
open var trailingContextualActions: [UIContextualAction] {
[]
}
open var performsFirstActionWithFullSwipe: Bool {
false
}
open var hashValue: Int { open var hashValue: Int {
return ObjectIdentifier(self).hashValue return ObjectIdentifier(self).hashValue
@ -41,15 +52,20 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
open var defaultHeight: CGFloat? { open var defaultHeight: CGFloat? {
return CellType.defaultHeight return CellType.defaultHeight
} }
open var layoutType: LayoutType {
return CellType.layoutType
}
open var cellType: AnyClass { open var cellType: AnyClass {
return CellType.self return CellType.self
} }
public init(item: CellType.T, actions: [TableRowAction<CellType>]? = nil, editingActions: [UITableViewRowAction]? = nil) { public init(item: CellType.CellData,
actions: [TableRowAction<CellType>]? = nil) {
self.item = item self.item = item
self.editingActions = editingActions
actions?.forEach { on($0) } actions?.forEach { on($0) }
} }
@ -59,12 +75,12 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
(cell as? CellType)?.configure(with: item) (cell as? CellType)?.configure(with: item)
} }
// MARK: - RowActionable - // MARK: - RowActionable -
open func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? { open func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? {
return actions[action.key]?.flatMap({ $0.invokeActionOn(cell: cell, item: item, path: path, userInfo: userInfo) }).last return actions[action.key]?.compactMap({ $0.invokeActionOn(cell: cell, item: item, path: path, userInfo: userInfo) }).last
} }
open func has(action: TableRowActionType) -> Bool { open func has(action: TableRowActionType) -> Bool {
@ -77,7 +93,10 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
if actions[TableRowActionType.canEdit.key] != nil { if actions[TableRowActionType.canEdit.key] != nil {
return invoke(action: .canEdit, cell: nil, path: indexPath) as? Bool ?? false return invoke(action: .canEdit, cell: nil, path: indexPath) as? Bool ?? false
} }
return editingActions?.isEmpty == false || actions[TableRowActionType.clickDelete.key] != nil
return !leadingContextualActions.isEmpty
|| !trailingContextualActions.isEmpty
|| actions[TableRowActionType.clickDelete.key] != nil
} }
// MARK: - actions - // MARK: - actions -
@ -113,7 +132,7 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
open func removeAction(forActionId actionId: String) { open func removeAction(forActionId actionId: String) {
for (key, value) in actions { for (key, value) in actions {
if let actionIndex = value.index(where: { $0.id == actionId }) { if let actionIndex = value.firstIndex(where: { $0.id == actionId }) {
actions[key]?.remove(at: actionIndex) actions[key]?.remove(at: actionIndex)
} }
} }

View File

@ -22,12 +22,12 @@ import UIKit
open class TableRowActionOptions<CellType: ConfigurableCell> where CellType: UITableViewCell { open class TableRowActionOptions<CellType: ConfigurableCell> where CellType: UITableViewCell {
open let item: CellType.T public let item: CellType.CellData
open let cell: CellType? public let cell: CellType?
open let indexPath: IndexPath public let indexPath: IndexPath
open let userInfo: [AnyHashable: Any]? public let userInfo: [AnyHashable: Any]?
init(item: CellType.T, cell: CellType?, path: IndexPath, userInfo: [AnyHashable: Any]?) { init(item: CellType.CellData, cell: CellType?, path: IndexPath, userInfo: [AnyHashable: Any]?) {
self.item = item self.item = item
self.cell = cell self.cell = cell
@ -55,7 +55,7 @@ private enum TableRowActionHandler<CellType: ConfigurableCell> where CellType: U
open class TableRowAction<CellType: ConfigurableCell> where CellType: UITableViewCell { open class TableRowAction<CellType: ConfigurableCell> where CellType: UITableViewCell {
open var id: String? open var id: String?
open let type: TableRowActionType public let type: TableRowActionType
private let handler: TableRowActionHandler<CellType> private let handler: TableRowActionHandler<CellType>
public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) { public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) {
@ -76,7 +76,7 @@ open class TableRowAction<CellType: ConfigurableCell> where CellType: UITableVie
self.handler = .action(handler) self.handler = .action(handler)
} }
public func invokeActionOn(cell: UITableViewCell?, item: CellType.T, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? { 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)) return handler.invoke(withOptions: TableRowActionOptions(item: item, cell: cell as? CellType, path: path, userInfo: userInfo))
} }

View File

@ -26,6 +26,7 @@ open class TableSection {
open var headerTitle: String? open var headerTitle: String?
open var footerTitle: String? open var footerTitle: String?
open var indexTitle: String?
open var headerView: UIView? open var headerView: UIView?
open var footerView: UIView? open var footerView: UIView?
@ -88,6 +89,10 @@ open class TableSection {
rows[index] = row rows[index] = row
} }
open func swap(from: Int, to: Int) {
rows.swapAt(from, to)
}
open func delete(rowAt index: Int) { open func delete(rowAt index: Int) {
rows.remove(at: index) rows.remove(at: index)
} }

View File

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

View File

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

View File

@ -7,6 +7,12 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */; };
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E858571DB153F500A9AA55 /* TableKit.swift */; }; 50E858581DB153F500A9AA55 /* TableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E858571DB153F500A9AA55 /* TableKit.swift */; };
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */; }; DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */; };
@ -32,6 +38,12 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference 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>"; }; 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>"; }; 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; }; DA9EA7561D0B679A0021F650 /* TableKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -90,16 +102,22 @@
DA9EA7A51D0EC2B90021F650 /* Sources */ = { DA9EA7A51D0EC2B90021F650 /* Sources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
50E858571DB153F500A9AA55 /* TableKit.swift */, DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */,
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */, 3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */,
DA9EA7A81D0EC2C90021F650 /* Operators.swift */,
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */,
50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */, 50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */,
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */,
50E858571DB153F500A9AA55 /* TableKit.swift */,
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */,
DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */, DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */,
DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */, DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */,
DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */, DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */,
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */, 3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */,
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */, 3201E78721BE9EB2001DF9E7 /* Expandable.swift */,
DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */, 3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */,
DA9EA7A81D0EC2C90021F650 /* Operators.swift */, 32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */,
2CBFA2F421F692F100147B56 /* ExpandableState.swift */,
); );
path = Sources; path = Sources;
sourceTree = "<group>"; sourceTree = "<group>";
@ -177,16 +195,16 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0730; LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0800; LastUpgradeCheck = 1000;
ORGANIZATIONNAME = "Max Sokolov"; ORGANIZATIONNAME = "Max Sokolov";
TargetAttributes = { TargetAttributes = {
DA9EA7551D0B679A0021F650 = { DA9EA7551D0B679A0021F650 = {
CreatedOnToolsVersion = 7.3; CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800; LastSwiftMigration = 1000;
}; };
DA9EA7C31D0EC45F0021F650 = { DA9EA7C31D0EC45F0021F650 = {
CreatedOnToolsVersion = 7.3; CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800; LastSwiftMigration = 1000;
}; };
}; };
}; };
@ -195,6 +213,7 @@
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
); );
mainGroup = DA9EA74C1D0B679A0021F650; mainGroup = DA9EA74C1D0B679A0021F650;
@ -230,13 +249,19 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3201E78A21BE9ED4001DF9E7 /* ExpandableCellViewModel.swift in Sources */,
50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */, 50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */,
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */, DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */,
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */, DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */,
3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */,
2CBFA2F521F692F100147B56 /* ExpandableState.swift in Sources */,
DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */, DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */,
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */, DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */,
3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */,
DA9EA7B51D0EC2C90021F650 /* TableRowAction.swift in Sources */, DA9EA7B51D0EC2C90021F650 /* TableRowAction.swift in Sources */,
DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */, DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */,
32BDFE9F21C167F400D0BBB4 /* LayoutType.swift in Sources */,
3201E78621BE9E25001DF9E7 /* UITableViewCell+Extensions.swift in Sources */,
DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */, DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */,
DA9EA7B41D0EC2C90021F650 /* TableRow.swift in Sources */, DA9EA7B41D0EC2C90021F650 /* TableRow.swift in Sources */,
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */, 50E858581DB153F500A9AA55 /* TableKit.swift in Sources */,
@ -271,14 +296,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -307,6 +340,7 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = ""; VERSION_INFO_PREFIX = "";
@ -322,14 +356,22 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@ -351,6 +393,7 @@
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
@ -375,7 +418,7 @@
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
@ -395,7 +438,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKit; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKit;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Release; name = Release;
}; };
@ -407,7 +450,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Debug; name = Debug;
}; };
@ -419,7 +462,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests; PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 5.0;
}; };
name = Release; name = Release;
}; };

View File

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

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "1000"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View File

@ -66,7 +66,7 @@ class TestTableViewCell: UITableViewCell, ConfigurableCell {
} }
} }
class TabletTests: XCTestCase { class TableKitTests: XCTestCase {
var testController: TestController! var testController: TestController!
@ -74,7 +74,10 @@ class TabletTests: XCTestCase {
super.setUp() super.setUp()
testController = TestController() testController = TestController()
testController.view.isHidden = false testController.tableView.frame = UIScreen.main.bounds
testController.tableView.isHidden = false
testController.tableView.setNeedsLayout()
testController.tableView.layoutIfNeeded()
} }
override func tearDown() { override func tearDown() {
@ -215,7 +218,6 @@ class TabletTests: XCTestCase {
XCTAssertTrue(cell1?.textLabel?.text == "title2") XCTAssertTrue(cell1?.textLabel?.text == "title2")
} }
func testReplaceSectionOnWrongIndex() { func testReplaceSectionOnWrongIndex() {
let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1")) let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1"))