├── .github
├── funding.yml
└── workflows
│ └── build.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Package.swift
├── README.md
├── TOInsetGroupedTableView.podspec
├── TOInsetGroupedTableView.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── TOInsetGroupedTableView.xcscheme
│ └── TOInsetGroupedTableViewTests.xcscheme
├── TOInsetGroupedTableView
├── TOInsetGroupedTableView.h
├── TOInsetGroupedTableView.m
└── include
│ ├── TOInsetGroupedTableView.h
│ └── module.modulemap
├── TOInsetGroupedTableViewExample
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
├── TOAppDelegate.h
├── TOAppDelegate.m
├── TOViewController.h
├── TOViewController.m
└── main.m
├── TOInsetGroupedTableViewTests
├── Info.plist
└── TOInsetGroupedTableViewTests.m
└── screenshot.jpg
/.github/funding.yml:
--------------------------------------------------------------------------------
1 | github: timoliver
2 | custom: https://tim.dev/paypal
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: macos-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: Run all of the library unit tests.
13 | run: '(curl -s -L http://tim.dev/install_ios_oss_ci | bash -s arg1 arg2) && bundle exec fastlane test'
14 | env:
15 | TEST_SCHEME: "TOInsetGroupedTableViewTests"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | # CocoaPods
34 | #
35 | # We recommend against adding the Pods directory to your .gitignore. However
36 | # you should judge for yourself, the pros and cons are mentioned at:
37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
38 | #
39 | # Pods/
40 | #
41 | # Add this line if you want to avoid checking in source code from the Xcode workspace
42 | # *.xcworkspace
43 |
44 | # Carthage
45 | #
46 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
47 | # Carthage/Checkouts
48 |
49 | Carthage/Build/
50 |
51 | # fastlane
52 | #
53 | # It is recommended to not store the screenshots in the git repo.
54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
55 | # For more information about the recommended setup visit:
56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
57 |
58 | fastlane/report.xml
59 | fastlane/Preview.html
60 | fastlane/screenshots/**/*.png
61 | fastlane/test_output
62 |
63 | # Code Injection
64 | #
65 | # After new code Injection tools there's a generated folder /iOSInjectionProject
66 | # https://github.com/johnno1962/injectionforxcode
67 |
68 | iOSInjectionProject/
69 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | x.y.z Release Notes (yyyy-MM-dd)
2 | =============================================================
3 |
4 | 1.0.1 Release Notes (2020-04-11)
5 | =============================================================
6 |
7 | ### Added
8 |
9 | * An API override for `UITableView(frame:style)` to ensure instances created with that method have the style value set properly.
10 |
11 | 1.0.0 Release Notes (2020-04-10)
12 | =============================================================
13 |
14 | * Initial Release! 🎉
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Tim Oliver
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "TOInsetGroupedTableView",
8 | platforms: [
9 | .iOS(.v11),
10 | ],
11 | products: [
12 | .library(name: "TOInsetGroupedTableView", targets: ["TOInsetGroupedTableView"]),
13 | ],
14 | targets: [
15 | .target(
16 | name: "TOInsetGroupedTableView",
17 | dependencies: [],
18 | path: "./TOInsetGroupedTableView/",
19 | publicHeadersPath: "include")
20 | ]
21 | )
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TOInsetGroupedTableView
2 |
3 |
4 |
5 |
6 |
7 | [](https://github.com/TimOliver/TOInsetGroupedTableView/actions?query=workflow%3ACI)
8 | [](http://cocoadocs.org/docsets/TOInsetGroupedTableView)
9 | [](https://raw.githubusercontent.com/TimOliver/TOInsetGroupedTableView/master/LICENSE)
10 | [](http://cocoadocs.org/docsets/TOInsetGroupedTableView)
11 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M4RKULAVKV7K8)
12 | [](http://twitch.tv/timXD)
13 |
14 | `TOInsetGroupedTableView` is a subclass of `UITableView` that back-ports the new "inset grouped" visual style introduced in iOS 13 to older versions of iOS.
15 |
16 | On iOS 13 and above, it defers back to using the system implementation, meaning absolutely no extra configuration code is required.
17 |
18 | This library is fantastic for developers adopting the new rounded corners style of table views in their apps, but are still supporting iOS 11 or iOS 12.
19 |
20 | ## Features
21 | * Brings the modern rounded corners look to table views in previous versions of iOS.
22 | * All override behaviour is contained in the table view. No modifications of the cell views themselves is required.
23 | * Defers back to the system implementation on iOS 13 and higher.
24 | * As the code is only ever executed below iOS 13, there is no chance of future iOS releases breaking the build.
25 |
26 | ## Requirements
27 | * Xcode 11.0 or higher.
28 | * iOS 11.0 or higher.
29 |
30 | ## Installation
31 |
32 | ### Manual Installation
33 |
34 | 1. [Download](https://github.com/TimOliver/TOInsetGroupedTableView/archive/master.zip) the latest version of the `TOInsetGroupedTableView` repository.
35 | 2. Inside the repository, copy the `TOInsetGroupedTableView` folder to your own Xcode project.
36 | 3. Optionally, in Swift, make sure to add the header file to your Swift bridging header.
37 |
38 | ### CocoaPods
39 |
40 | In your app's Podfile, add:
41 |
42 | ```ruby
43 | pod 'TOInsetGroupedTableView'
44 | ```
45 |
46 | ## Usage
47 |
48 | Integrating `TOInsetGroupedTableView` is extremely simple as it introduces no new APIs or changes any external inputs. All that is needed is to replace `UITableView()` instantiations with `TOInsetGroupedTableView()`.
49 |
50 | ### Swift
51 |
52 | In Swift, the class is renamed to `InsetGroupedTableView`. In order to integrate it, simply replace any instances of
53 |
54 | ```swift
55 | self.tableView = UITableView(frame: .zero, style: .insetGrouped)
56 | ```
57 |
58 | with
59 |
60 | ```swift
61 | self.tableView = InsetGroupedTableView(frame: .zero)
62 | ```
63 |
64 | No other changes are needed.
65 |
66 | ### Objective-C
67 |
68 | Just like in Swift, all that is required is to rename any instantiations of `UITableView` with `TOInsetGroupedTableView`.
69 |
70 | For example, simply replace any instances of:
71 |
72 | ```objc
73 | self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
74 | ```
75 |
76 | with
77 |
78 | ```objc
79 | self.tableView = [[TOInsetGroupedTableView alloc] initWithFrame:CGRectZero];
80 | ```
81 |
82 | ## Credits
83 |
84 | `TOInsetGroupedTableView` was created by [Tim Oliver](http://twitter.com/TimOliverAU).
85 |
86 |
87 | ## How is this library different to [`TORoundedTableView`](https://github.com/TimOliver/TORoundedTableView)?
88 |
89 | `TORoundedTableView` is a library with a similar goal: replicating the rounded corner table view style that has been present in Settings.app since iOS 7.
90 |
91 | `TORoundedTableView` was originally released in late 2016 with the explicit goal of supporting iOS versions 8.0 and above.
92 |
93 | Due to the APIs available on iOS at the time, as well as the relative graphics performance of the hardware of that era, `TORoundedTableView` required far more modification of `UITableView` and its components to achieve the effect, and maintain high FPS.
94 |
95 | Most notably, in order to have the rounded caps on the cells, it was also necessary to create subclasses of `UITableViewCell` as well, which increased the complexity of the implementation, and meant it couldn't really be simply 'dropped in' to existing implementations.
96 |
97 | By focusing on just the most recent iOS versions, where OpenGL has been completely removed, and there are now more Core Animation APIs, `TOInsetGroupedTableView` is able to achieve the same effect as `TORoundedTableView` but without needing to subclass any of the cells.
98 |
99 | Additionally, by observing how `.insetGrouped` behaves in iOS 13, it was also possible to configure `TOInsetGroupedTableView` to work alongside it, allowing for the same code to default back to the iOS 13 implementation when possible.
100 |
101 | # License
102 |
103 | `TOInsetGroupedTableView` is available under the MIT license. Please see the [LICENSE](LICENSE) file for more information.
104 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'TOInsetGroupedTableView'
3 | s.version = '1.1.0'
4 | s.license = { :type => 'MIT', :file => 'LICENSE' }
5 | s.summary = 'An iOS 11 back-port of the grouped inset table view style in iOS 13.'
6 | s.homepage = 'https://github.com/TimOliver/TOInsetGroupedTableView'
7 | s.author = 'Tim Oliver'
8 | s.source = { :git => 'https://github.com/TimOliver/TOInsetGroupedTableView.git', :tag => s.version }
9 | s.platform = :ios, '11.0'
10 | s.source_files = 'TOInsetGroupedTableView/**/*.{h,m}'
11 | s.requires_arc = true
12 | end
13 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 223CA690243DB72400640EC2 /* TOAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 223CA68F243DB72400640EC2 /* TOAppDelegate.m */; };
11 | 223CA696243DB72400640EC2 /* TOViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 223CA695243DB72400640EC2 /* TOViewController.m */; };
12 | 223CA69B243DB72500640EC2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 223CA69A243DB72500640EC2 /* Assets.xcassets */; };
13 | 223CA69E243DB72500640EC2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 223CA69C243DB72500640EC2 /* LaunchScreen.storyboard */; };
14 | 223CA6AA243DB9A600640EC2 /* TOInsetGroupedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 223CA6A9243DB9A600640EC2 /* TOInsetGroupedTableView.m */; };
15 | 22DA8E23244063BC003341C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 22DA8E22244063BC003341C3 /* main.m */; };
16 | 22DA8E3124417361003341C3 /* TOInsetGroupedTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 22DA8E3024417361003341C3 /* TOInsetGroupedTableViewTests.m */; };
17 | 22DA8E382441737B003341C3 /* TOInsetGroupedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 223CA6A9243DB9A600640EC2 /* TOInsetGroupedTableView.m */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXContainerItemProxy section */
21 | 22DA8E3324417361003341C3 /* PBXContainerItemProxy */ = {
22 | isa = PBXContainerItemProxy;
23 | containerPortal = 223CA683243DB72400640EC2 /* Project object */;
24 | proxyType = 1;
25 | remoteGlobalIDString = 223CA68A243DB72400640EC2;
26 | remoteInfo = TOInsetGroupedTableView;
27 | };
28 | /* End PBXContainerItemProxy section */
29 |
30 | /* Begin PBXFileReference section */
31 | 223CA68B243DB72400640EC2 /* TOInsetGroupedTableView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TOInsetGroupedTableView.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 223CA68E243DB72400640EC2 /* TOAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOAppDelegate.h; sourceTree = ""; };
33 | 223CA68F243DB72400640EC2 /* TOAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOAppDelegate.m; sourceTree = ""; };
34 | 223CA694243DB72400640EC2 /* TOViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOViewController.h; sourceTree = ""; };
35 | 223CA695243DB72400640EC2 /* TOViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOViewController.m; sourceTree = ""; };
36 | 223CA69A243DB72500640EC2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
37 | 223CA69D243DB72500640EC2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
38 | 223CA69F243DB72500640EC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
39 | 223CA6A8243DB9A600640EC2 /* TOInsetGroupedTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOInsetGroupedTableView.h; sourceTree = ""; };
40 | 223CA6A9243DB9A600640EC2 /* TOInsetGroupedTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOInsetGroupedTableView.m; sourceTree = ""; };
41 | 22DA8E22244063BC003341C3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
42 | 22DA8E2E24417361003341C3 /* TOInsetGroupedTableViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TOInsetGroupedTableViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 22DA8E3024417361003341C3 /* TOInsetGroupedTableViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOInsetGroupedTableViewTests.m; sourceTree = ""; };
44 | 22DA8E3224417361003341C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 223CA688243DB72400640EC2 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | 22DA8E2B24417361003341C3 /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXFrameworksBuildPhase section */
63 |
64 | /* Begin PBXGroup section */
65 | 223CA682243DB72400640EC2 = {
66 | isa = PBXGroup;
67 | children = (
68 | 223CA6A7243DB77F00640EC2 /* TOInsetGroupedTableView */,
69 | 223CA68D243DB72400640EC2 /* TOInsetGroupedTableViewExample */,
70 | 22DA8E2F24417361003341C3 /* TOInsetGroupedTableViewTests */,
71 | 223CA68C243DB72400640EC2 /* Products */,
72 | );
73 | sourceTree = "";
74 | };
75 | 223CA68C243DB72400640EC2 /* Products */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 223CA68B243DB72400640EC2 /* TOInsetGroupedTableView.app */,
79 | 22DA8E2E24417361003341C3 /* TOInsetGroupedTableViewTests.xctest */,
80 | );
81 | name = Products;
82 | sourceTree = "";
83 | };
84 | 223CA68D243DB72400640EC2 /* TOInsetGroupedTableViewExample */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 223CA68E243DB72400640EC2 /* TOAppDelegate.h */,
88 | 223CA68F243DB72400640EC2 /* TOAppDelegate.m */,
89 | 223CA694243DB72400640EC2 /* TOViewController.h */,
90 | 223CA695243DB72400640EC2 /* TOViewController.m */,
91 | 223CA69A243DB72500640EC2 /* Assets.xcassets */,
92 | 223CA69C243DB72500640EC2 /* LaunchScreen.storyboard */,
93 | 223CA69F243DB72500640EC2 /* Info.plist */,
94 | 22DA8E22244063BC003341C3 /* main.m */,
95 | );
96 | path = TOInsetGroupedTableViewExample;
97 | sourceTree = "";
98 | };
99 | 223CA6A7243DB77F00640EC2 /* TOInsetGroupedTableView */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 223CA6A8243DB9A600640EC2 /* TOInsetGroupedTableView.h */,
103 | 223CA6A9243DB9A600640EC2 /* TOInsetGroupedTableView.m */,
104 | );
105 | path = TOInsetGroupedTableView;
106 | sourceTree = "";
107 | };
108 | 22DA8E2F24417361003341C3 /* TOInsetGroupedTableViewTests */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 22DA8E3024417361003341C3 /* TOInsetGroupedTableViewTests.m */,
112 | 22DA8E3224417361003341C3 /* Info.plist */,
113 | );
114 | path = TOInsetGroupedTableViewTests;
115 | sourceTree = "";
116 | };
117 | /* End PBXGroup section */
118 |
119 | /* Begin PBXNativeTarget section */
120 | 223CA68A243DB72400640EC2 /* TOInsetGroupedTableView */ = {
121 | isa = PBXNativeTarget;
122 | buildConfigurationList = 223CA6A4243DB72500640EC2 /* Build configuration list for PBXNativeTarget "TOInsetGroupedTableView" */;
123 | buildPhases = (
124 | 223CA687243DB72400640EC2 /* Sources */,
125 | 223CA688243DB72400640EC2 /* Frameworks */,
126 | 223CA689243DB72400640EC2 /* Resources */,
127 | 223CA6AD243DE07500640EC2 /* ShellScript */,
128 | );
129 | buildRules = (
130 | );
131 | dependencies = (
132 | );
133 | name = TOInsetGroupedTableView;
134 | productName = TOGroupInsetTableView;
135 | productReference = 223CA68B243DB72400640EC2 /* TOInsetGroupedTableView.app */;
136 | productType = "com.apple.product-type.application";
137 | };
138 | 22DA8E2D24417361003341C3 /* TOInsetGroupedTableViewTests */ = {
139 | isa = PBXNativeTarget;
140 | buildConfigurationList = 22DA8E3524417361003341C3 /* Build configuration list for PBXNativeTarget "TOInsetGroupedTableViewTests" */;
141 | buildPhases = (
142 | 22DA8E2A24417361003341C3 /* Sources */,
143 | 22DA8E2B24417361003341C3 /* Frameworks */,
144 | 22DA8E2C24417361003341C3 /* Resources */,
145 | );
146 | buildRules = (
147 | );
148 | dependencies = (
149 | 22DA8E3424417361003341C3 /* PBXTargetDependency */,
150 | );
151 | name = TOInsetGroupedTableViewTests;
152 | productName = TOInsetGroupedTableViewTests;
153 | productReference = 22DA8E2E24417361003341C3 /* TOInsetGroupedTableViewTests.xctest */;
154 | productType = "com.apple.product-type.bundle.unit-test";
155 | };
156 | /* End PBXNativeTarget section */
157 |
158 | /* Begin PBXProject section */
159 | 223CA683243DB72400640EC2 /* Project object */ = {
160 | isa = PBXProject;
161 | attributes = {
162 | LastUpgradeCheck = 1140;
163 | ORGANIZATIONNAME = "Tim Oliver";
164 | TargetAttributes = {
165 | 223CA68A243DB72400640EC2 = {
166 | CreatedOnToolsVersion = 11.4;
167 | };
168 | 22DA8E2D24417361003341C3 = {
169 | CreatedOnToolsVersion = 11.4;
170 | TestTargetID = 223CA68A243DB72400640EC2;
171 | };
172 | };
173 | };
174 | buildConfigurationList = 223CA686243DB72400640EC2 /* Build configuration list for PBXProject "TOInsetGroupedTableView" */;
175 | compatibilityVersion = "Xcode 9.3";
176 | developmentRegion = en;
177 | hasScannedForEncodings = 0;
178 | knownRegions = (
179 | en,
180 | Base,
181 | );
182 | mainGroup = 223CA682243DB72400640EC2;
183 | productRefGroup = 223CA68C243DB72400640EC2 /* Products */;
184 | projectDirPath = "";
185 | projectRoot = "";
186 | targets = (
187 | 223CA68A243DB72400640EC2 /* TOInsetGroupedTableView */,
188 | 22DA8E2D24417361003341C3 /* TOInsetGroupedTableViewTests */,
189 | );
190 | };
191 | /* End PBXProject section */
192 |
193 | /* Begin PBXResourcesBuildPhase section */
194 | 223CA689243DB72400640EC2 /* Resources */ = {
195 | isa = PBXResourcesBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | 223CA69E243DB72500640EC2 /* LaunchScreen.storyboard in Resources */,
199 | 223CA69B243DB72500640EC2 /* Assets.xcassets in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | 22DA8E2C24417361003341C3 /* Resources */ = {
204 | isa = PBXResourcesBuildPhase;
205 | buildActionMask = 2147483647;
206 | files = (
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | };
210 | /* End PBXResourcesBuildPhase section */
211 |
212 | /* Begin PBXShellScriptBuildPhase section */
213 | 223CA6AD243DE07500640EC2 /* ShellScript */ = {
214 | isa = PBXShellScriptBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | inputFileListPaths = (
219 | );
220 | inputPaths = (
221 | );
222 | outputFileListPaths = (
223 | );
224 | outputPaths = (
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | shellPath = /bin/sh;
228 | shellScript = "export REVEAL_SERVER_FILENAME=\"RevealServer.framework\"\n\n# Update this path to point to the location of RevealServer.framework in your project.\nexport REVEAL_SERVER_PATH=\"${SRCROOT}/${REVEAL_SERVER_FILENAME}\"\n\n# If configuration is not Debug, skip this script.\n[ \"${CONFIGURATION}\" != \"Debug\" ] && exit 0\n\n# If RevealServer.framework exists at the specified path, run code signing script.\nif [ -d \"${REVEAL_SERVER_PATH}\" ]; then\n \"${REVEAL_SERVER_PATH}/Scripts/copy_and_codesign_revealserver.sh\"\nelse\n echo \"Reveal Server not loaded: RevealServer.framework could not be found.\"\nfi\n";
229 | };
230 | /* End PBXShellScriptBuildPhase section */
231 |
232 | /* Begin PBXSourcesBuildPhase section */
233 | 223CA687243DB72400640EC2 /* Sources */ = {
234 | isa = PBXSourcesBuildPhase;
235 | buildActionMask = 2147483647;
236 | files = (
237 | 223CA696243DB72400640EC2 /* TOViewController.m in Sources */,
238 | 223CA690243DB72400640EC2 /* TOAppDelegate.m in Sources */,
239 | 22DA8E23244063BC003341C3 /* main.m in Sources */,
240 | 223CA6AA243DB9A600640EC2 /* TOInsetGroupedTableView.m in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | 22DA8E2A24417361003341C3 /* Sources */ = {
245 | isa = PBXSourcesBuildPhase;
246 | buildActionMask = 2147483647;
247 | files = (
248 | 22DA8E3124417361003341C3 /* TOInsetGroupedTableViewTests.m in Sources */,
249 | 22DA8E382441737B003341C3 /* TOInsetGroupedTableView.m in Sources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXSourcesBuildPhase section */
254 |
255 | /* Begin PBXTargetDependency section */
256 | 22DA8E3424417361003341C3 /* PBXTargetDependency */ = {
257 | isa = PBXTargetDependency;
258 | target = 223CA68A243DB72400640EC2 /* TOInsetGroupedTableView */;
259 | targetProxy = 22DA8E3324417361003341C3 /* PBXContainerItemProxy */;
260 | };
261 | /* End PBXTargetDependency section */
262 |
263 | /* Begin PBXVariantGroup section */
264 | 223CA69C243DB72500640EC2 /* LaunchScreen.storyboard */ = {
265 | isa = PBXVariantGroup;
266 | children = (
267 | 223CA69D243DB72500640EC2 /* Base */,
268 | );
269 | name = LaunchScreen.storyboard;
270 | sourceTree = "";
271 | };
272 | /* End PBXVariantGroup section */
273 |
274 | /* Begin XCBuildConfiguration section */
275 | 223CA6A2243DB72500640EC2 /* Debug */ = {
276 | isa = XCBuildConfiguration;
277 | buildSettings = {
278 | ALWAYS_SEARCH_USER_PATHS = NO;
279 | CLANG_ANALYZER_NONNULL = YES;
280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
282 | CLANG_CXX_LIBRARY = "libc++";
283 | CLANG_ENABLE_MODULES = YES;
284 | CLANG_ENABLE_OBJC_ARC = YES;
285 | CLANG_ENABLE_OBJC_WEAK = YES;
286 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
287 | CLANG_WARN_BOOL_CONVERSION = YES;
288 | CLANG_WARN_COMMA = YES;
289 | CLANG_WARN_CONSTANT_CONVERSION = YES;
290 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
291 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
292 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
293 | CLANG_WARN_EMPTY_BODY = YES;
294 | CLANG_WARN_ENUM_CONVERSION = YES;
295 | CLANG_WARN_INFINITE_RECURSION = YES;
296 | CLANG_WARN_INT_CONVERSION = YES;
297 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
298 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
299 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
300 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
301 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
302 | CLANG_WARN_STRICT_PROTOTYPES = YES;
303 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
304 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
305 | CLANG_WARN_UNREACHABLE_CODE = YES;
306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
307 | COPY_PHASE_STRIP = NO;
308 | DEBUG_INFORMATION_FORMAT = dwarf;
309 | ENABLE_STRICT_OBJC_MSGSEND = YES;
310 | ENABLE_TESTABILITY = YES;
311 | GCC_C_LANGUAGE_STANDARD = gnu11;
312 | GCC_DYNAMIC_NO_PIC = NO;
313 | GCC_NO_COMMON_BLOCKS = YES;
314 | GCC_OPTIMIZATION_LEVEL = 0;
315 | GCC_PREPROCESSOR_DEFINITIONS = (
316 | "DEBUG=1",
317 | "$(inherited)",
318 | );
319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
321 | GCC_WARN_UNDECLARED_SELECTOR = YES;
322 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
323 | GCC_WARN_UNUSED_FUNCTION = YES;
324 | GCC_WARN_UNUSED_VARIABLE = YES;
325 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
326 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
327 | MTL_FAST_MATH = YES;
328 | ONLY_ACTIVE_ARCH = YES;
329 | SDKROOT = iphoneos;
330 | };
331 | name = Debug;
332 | };
333 | 223CA6A3243DB72500640EC2 /* Release */ = {
334 | isa = XCBuildConfiguration;
335 | buildSettings = {
336 | ALWAYS_SEARCH_USER_PATHS = NO;
337 | CLANG_ANALYZER_NONNULL = YES;
338 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
339 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
340 | CLANG_CXX_LIBRARY = "libc++";
341 | CLANG_ENABLE_MODULES = YES;
342 | CLANG_ENABLE_OBJC_ARC = YES;
343 | CLANG_ENABLE_OBJC_WEAK = YES;
344 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
345 | CLANG_WARN_BOOL_CONVERSION = YES;
346 | CLANG_WARN_COMMA = YES;
347 | CLANG_WARN_CONSTANT_CONVERSION = YES;
348 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
349 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
350 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
351 | CLANG_WARN_EMPTY_BODY = YES;
352 | CLANG_WARN_ENUM_CONVERSION = YES;
353 | CLANG_WARN_INFINITE_RECURSION = YES;
354 | CLANG_WARN_INT_CONVERSION = YES;
355 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
356 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
357 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
359 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
360 | CLANG_WARN_STRICT_PROTOTYPES = YES;
361 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
362 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
363 | CLANG_WARN_UNREACHABLE_CODE = YES;
364 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
365 | COPY_PHASE_STRIP = NO;
366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
367 | ENABLE_NS_ASSERTIONS = NO;
368 | ENABLE_STRICT_OBJC_MSGSEND = YES;
369 | GCC_C_LANGUAGE_STANDARD = gnu11;
370 | GCC_NO_COMMON_BLOCKS = YES;
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
378 | MTL_ENABLE_DEBUG_INFO = NO;
379 | MTL_FAST_MATH = YES;
380 | SDKROOT = iphoneos;
381 | VALIDATE_PRODUCT = YES;
382 | };
383 | name = Release;
384 | };
385 | 223CA6A5243DB72500640EC2 /* Debug */ = {
386 | isa = XCBuildConfiguration;
387 | buildSettings = {
388 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
389 | CODE_SIGN_STYLE = Automatic;
390 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
391 | FRAMEWORK_SEARCH_PATHS = (
392 | "$(inherited)",
393 | "$(PROJECT_DIR)",
394 | "$(SRCROOT)",
395 | );
396 | INFOPLIST_FILE = "$(SRCROOT)/TOInsetGroupedTableViewExample/Info.plist";
397 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
398 | LD_RUNPATH_SEARCH_PATHS = (
399 | "$(inherited)",
400 | "@executable_path/Frameworks",
401 | );
402 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.TOInsetGroupedTableView;
403 | PRODUCT_NAME = "$(TARGET_NAME)";
404 | TARGETED_DEVICE_FAMILY = "1,2";
405 | };
406 | name = Debug;
407 | };
408 | 223CA6A6243DB72500640EC2 /* Release */ = {
409 | isa = XCBuildConfiguration;
410 | buildSettings = {
411 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
412 | CODE_SIGN_STYLE = Automatic;
413 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
414 | FRAMEWORK_SEARCH_PATHS = (
415 | "$(inherited)",
416 | "$(PROJECT_DIR)",
417 | "$(SRCROOT)",
418 | );
419 | INFOPLIST_FILE = "$(SRCROOT)/TOInsetGroupedTableViewExample/Info.plist";
420 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
421 | LD_RUNPATH_SEARCH_PATHS = (
422 | "$(inherited)",
423 | "@executable_path/Frameworks",
424 | );
425 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.TOInsetGroupedTableView;
426 | PRODUCT_NAME = "$(TARGET_NAME)";
427 | TARGETED_DEVICE_FAMILY = "1,2";
428 | };
429 | name = Release;
430 | };
431 | 22DA8E3624417361003341C3 /* Debug */ = {
432 | isa = XCBuildConfiguration;
433 | buildSettings = {
434 | BUNDLE_LOADER = "$(TEST_HOST)";
435 | CODE_SIGN_STYLE = Automatic;
436 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
437 | INFOPLIST_FILE = TOInsetGroupedTableViewTests/Info.plist;
438 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
439 | LD_RUNPATH_SEARCH_PATHS = (
440 | "$(inherited)",
441 | "@executable_path/Frameworks",
442 | "@loader_path/Frameworks",
443 | );
444 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.TOInsetGroupedTableViewTests;
445 | PRODUCT_NAME = "$(TARGET_NAME)";
446 | TARGETED_DEVICE_FAMILY = "1,2";
447 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TOInsetGroupedTableView.app/TOInsetGroupedTableView";
448 | TVOS_DEPLOYMENT_TARGET = 11.0;
449 | };
450 | name = Debug;
451 | };
452 | 22DA8E3724417361003341C3 /* Release */ = {
453 | isa = XCBuildConfiguration;
454 | buildSettings = {
455 | BUNDLE_LOADER = "$(TEST_HOST)";
456 | CODE_SIGN_STYLE = Automatic;
457 | DEVELOPMENT_TEAM = 6LF3GMKZAB;
458 | INFOPLIST_FILE = TOInsetGroupedTableViewTests/Info.plist;
459 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
460 | LD_RUNPATH_SEARCH_PATHS = (
461 | "$(inherited)",
462 | "@executable_path/Frameworks",
463 | "@loader_path/Frameworks",
464 | );
465 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.TOInsetGroupedTableViewTests;
466 | PRODUCT_NAME = "$(TARGET_NAME)";
467 | TARGETED_DEVICE_FAMILY = "1,2";
468 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TOInsetGroupedTableView.app/TOInsetGroupedTableView";
469 | TVOS_DEPLOYMENT_TARGET = 11.0;
470 | };
471 | name = Release;
472 | };
473 | /* End XCBuildConfiguration section */
474 |
475 | /* Begin XCConfigurationList section */
476 | 223CA686243DB72400640EC2 /* Build configuration list for PBXProject "TOInsetGroupedTableView" */ = {
477 | isa = XCConfigurationList;
478 | buildConfigurations = (
479 | 223CA6A2243DB72500640EC2 /* Debug */,
480 | 223CA6A3243DB72500640EC2 /* Release */,
481 | );
482 | defaultConfigurationIsVisible = 0;
483 | defaultConfigurationName = Release;
484 | };
485 | 223CA6A4243DB72500640EC2 /* Build configuration list for PBXNativeTarget "TOInsetGroupedTableView" */ = {
486 | isa = XCConfigurationList;
487 | buildConfigurations = (
488 | 223CA6A5243DB72500640EC2 /* Debug */,
489 | 223CA6A6243DB72500640EC2 /* Release */,
490 | );
491 | defaultConfigurationIsVisible = 0;
492 | defaultConfigurationName = Release;
493 | };
494 | 22DA8E3524417361003341C3 /* Build configuration list for PBXNativeTarget "TOInsetGroupedTableViewTests" */ = {
495 | isa = XCConfigurationList;
496 | buildConfigurations = (
497 | 22DA8E3624417361003341C3 /* Debug */,
498 | 22DA8E3724417361003341C3 /* Release */,
499 | );
500 | defaultConfigurationIsVisible = 0;
501 | defaultConfigurationName = Release;
502 | };
503 | /* End XCConfigurationList section */
504 | };
505 | rootObject = 223CA683243DB72400640EC2 /* Project object */;
506 | }
507 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.xcodeproj/xcshareddata/xcschemes/TOInsetGroupedTableView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView.xcodeproj/xcshareddata/xcschemes/TOInsetGroupedTableViewTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView/TOInsetGroupedTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TOInsetGroupedTableView.h
3 | //
4 | // Copyright 2020 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import
24 |
25 | /**
26 | A subclass of UITableView that back-ports the "inset grouped" style
27 | that was introduced in iOS 13 to previous versions of iOS.
28 | Defaults back to the native system implementation on iOS 13 and above.
29 | */
30 | NS_SWIFT_NAME(InsetGroupedTableView)
31 | @interface TOInsetGroupedTableView : UITableView
32 | @end
33 |
34 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView/TOInsetGroupedTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TOInsetGroupedTableView.m
3 | //
4 | // Copyright 2020 Timothy Oliver. All rights reserved.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to
8 | // deal in the Software without restriction, including without limitation the
9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | // sell copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #import "TOInsetGroupedTableView.h"
24 |
25 | // Un-comment when testing this on iOS 13 and up
26 | // #define DEBUG_TOINSETGROUPEDTABLEVIEW 1
27 |
28 | /**
29 | The KVO key we'll be using to detect when the table view
30 | manipulates the shape of any of the subviews
31 | */
32 | static NSString * const kTOInsetGroupedTableViewFrameKey = @"frame";
33 |
34 | /**
35 | The KVO key we'll be using to detect when the table view
36 | has been set to selected or not (which is the best time to calculate rounded corners)
37 | */
38 | static NSString * const kTOInsetGroupedTableViewSelectedKey = @"selected";
39 |
40 | /** The corner radius of the top and bottom cells.
41 | This is hard-coded with the same value as in iOS 13.
42 | */
43 | static CGFloat const kTOInsetGroupedTableViewCornerRadius = 10.0f;
44 |
45 | @interface TOInsetGroupedTableView ()
46 |
47 | /**
48 | A set to store a reference to each view that we attached
49 | a KVO observer to.
50 | */
51 | @property (nonatomic, strong) NSMutableSet *observedViews;
52 |
53 | @property (nonatomic, assign) int realSeparatorStyle;
54 |
55 | @end
56 |
57 | @implementation TOInsetGroupedTableView
58 |
59 | #pragma mark - View Life-cycle -
60 |
61 | - (instancetype)init
62 | {
63 | // Set a non-zero default frame value
64 | CGRect frame = (CGRect){0,0,320,480};
65 |
66 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
67 | // On iOS 13, cancel out to simply using the official grouped inset style
68 | if (@available(iOS 13.0, *)) {
69 | return [super initWithFrame:frame style:UITableViewStyleInsetGrouped];
70 | }
71 | #endif
72 |
73 | // On iOS 12 and below, force the grouped style, and perform common setup
74 | if (self = [super initWithFrame:frame style:UITableViewStyleGrouped]) {
75 | [self commonInit];
76 | }
77 |
78 | return self;
79 | }
80 |
81 | - (instancetype)initWithFrame:(CGRect)frame
82 | {
83 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
84 | // On iOS 13 and above, cancel out as we can simply use the official
85 | // grouped inset style.
86 | if (@available(iOS 13.0, *)) {
87 | return [super initWithFrame:frame style:UITableViewStyleInsetGrouped];
88 | }
89 | #endif
90 |
91 | // On iOS 12 and below, make sure we explicitly force the grouped style
92 | if (self = [super initWithFrame:frame style:UITableViewStyleGrouped]) {
93 | [self commonInit];
94 | }
95 |
96 | return self;
97 | }
98 |
99 | - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
100 | {
101 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
102 | // On iOS 13 and up, override with the standard system implementation
103 | if (@available(iOS 13.0, *)) {
104 | return [super initWithFrame:frame style:UITableViewStyleInsetGrouped];
105 | }
106 | #endif
107 |
108 | // On iOS 12 and below, make sure we explicitly force the grouped style
109 | if (self = [super initWithFrame:frame style:UITableViewStyleGrouped]) {
110 | [self commonInit];
111 | }
112 |
113 | return self;
114 | }
115 |
116 | - (instancetype)initWithCoder:(NSCoder *)coder
117 | {
118 | if (self = [super initWithCoder:coder]) {
119 | // If the user left the style as "Plain" in IB, since we can't
120 | // override it here, throw an exception.
121 | // (Thankfully on iOS 12, IB will gracefully default it back to "Grouped")
122 | if (self.style < UITableViewStyleGrouped) {
123 | NSString *reason = @"TOInsetGroupedTableView: Make sure the table view style "
124 | "is set to \"Inset Grouped\" in Interface Builder";
125 | @throw [NSException exceptionWithName:NSInternalInconsistencyException
126 | reason:reason
127 | userInfo:nil];
128 | }
129 |
130 | // On iOS 12 or lower, perform the common set-up
131 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
132 | if (@available(iOS 13.0, *)) { return self; }
133 | #endif
134 |
135 | [self commonInit];
136 | }
137 |
138 | return self;
139 | }
140 |
141 | - (void)commonInit
142 | {
143 | // Create the set to hold our observed views
144 | self.observedViews = [NSMutableSet set];
145 |
146 | // Explicitly disable any magic insetting, as we'll
147 | // be manually calculating the insetting ourselves
148 | self.insetsLayoutMarginsFromSafeArea = NO;
149 | }
150 |
151 | - (void)dealloc
152 | {
153 | [self removeAllObservers];
154 | }
155 |
156 | #pragma mark - Table View Behaviour Overrides -
157 |
158 | - (void)didAddSubview:(UIView *)subview
159 | {
160 | [super didAddSubview:subview];
161 |
162 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
163 | if (@available(iOS 13.0, *)) { return; }
164 | #endif
165 |
166 | // If it's not a section header/footer view, or a table cell, ignore it
167 | if (![subview isKindOfClass:[UITableViewHeaderFooterView class]] &&
168 | ![subview isKindOfClass:[UITableViewCell class]])
169 | {
170 | return;
171 | }
172 |
173 | // Register this view for observation
174 | [self addObserverIfNeeded:subview];
175 | }
176 |
177 | - (void)setSeparatorStyle:(UITableViewCellSeparatorStyle)separatorStyle
178 | {
179 | if (separatorStyle == UITableViewCellSeparatorStyleNone) {
180 | // make sure there will be _UITableViewCellSeparatorView in cell's subViews
181 | self.separatorColor = UIColor.clearColor;
182 | self.realSeparatorStyle = UITableViewCellSeparatorStyleNone;
183 | return;
184 | }
185 | self.realSeparatorStyle = -1;
186 | [super setSeparatorStyle:separatorStyle];
187 | }
188 |
189 | - (UITableViewCellSeparatorStyle)separatorStyle
190 | {
191 | if (self.realSeparatorStyle > -1) {
192 | return self.realSeparatorStyle;
193 | }
194 | return [super separatorStyle];
195 | }
196 |
197 | #pragma mark - Observer Life-cycle -
198 |
199 | - (void)addObserverIfNeeded:(UIView *)view
200 | {
201 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
202 | if (@available(iOS 13.0, *)) { return; }
203 | #endif
204 |
205 | // If the view had already been registered, exit out,
206 | // otherwise a system exception will be thrown
207 | if ([self.observedViews containsObject:view]) {
208 | return;
209 | }
210 |
211 | // Register the view to observe its frame shape
212 | [view addObserver:self
213 | forKeyPath:kTOInsetGroupedTableViewFrameKey
214 | options:0
215 | context:nil];
216 |
217 | // If it's a cell, register for when it's set as selected
218 | // so we can round the corners then
219 | if ([view isKindOfClass:[UITableViewCell class]]) {
220 | [view addObserver:self
221 | forKeyPath:kTOInsetGroupedTableViewSelectedKey
222 | options:0
223 | context:nil];
224 | }
225 |
226 | // Add it to the set
227 | [self.observedViews addObject:view];
228 | }
229 |
230 | - (void)removeAllObservers
231 | {
232 | #ifndef DEBUG_TOINSETGROUPEDTABLEVIEW
233 | if (@available(iOS 13.0, *)) { return; }
234 | #endif
235 |
236 | // Loop through each object in the set, and de-register them
237 | for (UIView *view in self.observedViews) {
238 | // Remove the frame observer
239 | [view removeObserver:self
240 | forKeyPath:kTOInsetGroupedTableViewFrameKey
241 | context:nil];
242 |
243 | // If table cell, remove the selected observer
244 | if ([view isKindOfClass:[UITableViewCell class]]) {
245 | [view removeObserver:self
246 | forKeyPath:kTOInsetGroupedTableViewSelectedKey
247 | context:nil];
248 | }
249 | }
250 |
251 | // Clean out all of the views from the set
252 | [self.observedViews removeAllObjects];
253 | }
254 |
255 | - (void)observeValueForKeyPath:(NSString *)keyPath
256 | ofObject:(id)object
257 | change:(NSDictionary *)change
258 | context:(void *)context
259 | {
260 | // Double check this notification is about an object we care about
261 | if ([object isKindOfClass:[UIView class]] == NO) { return; }
262 | UIView *view = (UIView *)object;
263 |
264 | // If the key was a frame observation, perform the inset
265 | if ([keyPath isEqualToString:kTOInsetGroupedTableViewFrameKey]) {
266 | [self performInsetLayoutForView:view];
267 | }
268 | else if ([keyPath isEqualToString:kTOInsetGroupedTableViewSelectedKey]) {
269 | // If the key was the selection key, apply rounding
270 | [self applyRoundedCornersToTableViewCell:(UITableViewCell *)view];
271 | }
272 | }
273 |
274 | #pragma mark - Behaviour Overrides -
275 |
276 | - (void)performInsetLayoutForView:(UIView *)view
277 | {
278 | CGRect frame = view.frame;
279 | UIEdgeInsets margins = self.layoutMargins;
280 | UIEdgeInsets safeAreaInsets = self.safeAreaInsets;
281 |
282 | // Calculate the left margin.
283 | // If the margin on its own isn't larger than
284 | // the safe area inset, combine the two.
285 | CGFloat leftInset = margins.left;
286 | if (leftInset - safeAreaInsets.left < 0.0f - FLT_EPSILON) {
287 | leftInset += safeAreaInsets.left;
288 | }
289 |
290 | // Calculate the right margin with the same logic.
291 | CGFloat rightInset = margins.right;
292 | if (rightInset - safeAreaInsets.right < 0.0f - FLT_EPSILON) {
293 | rightInset += safeAreaInsets.right;
294 | }
295 |
296 | // Calculate offset and width off the insets
297 | frame.origin.x = leftInset;
298 | frame.size.width = CGRectGetWidth(self.frame) - (leftInset + rightInset);
299 |
300 | // Apply the new frame value to the underlying CALayer
301 | // to avoid triggering the KVO observer into an infinite loop
302 | view.layer.frame = frame;
303 | }
304 |
305 | - (void)applyRoundedCornersToTableViewCell:(UITableViewCell *)cell
306 | {
307 | // Set the cell to always mask its child content
308 | cell.layer.masksToBounds = YES;
309 |
310 | // Set flags for checking both top and bottom
311 | BOOL topRounded = NO;
312 | BOOL bottomRounded = NO;
313 |
314 | // The maximum height a separator might be
315 | CGFloat separatorHeight = 1.0f;
316 |
317 | // Since the cell might still be not laid out yet
318 | // (And the separators aren't in the right places)
319 | // force a re-layout beforehand.
320 | [cell setNeedsLayout];
321 | [cell layoutIfNeeded];
322 |
323 | // Loop through each subview
324 | for (UIView *subview in cell.subviews) {
325 | CGRect frame = subview.frame;
326 |
327 | // Separators will always be less than 1 point high
328 | if (frame.size.height > separatorHeight) { continue; }
329 |
330 | // If the X origin isn't 0, it's a separator we want to keep.
331 | // Since it may have been a border separator we hid before, un-hide it.
332 | if (frame.origin.x > FLT_EPSILON) {
333 | subview.hidden = NO;
334 | continue;
335 | }
336 |
337 | // Check if it's a top or bottom separator
338 | if (frame.origin.y < FLT_EPSILON) {
339 | topRounded = YES;
340 | }
341 | else {
342 | bottomRounded = YES;
343 | }
344 |
345 | // Hide this view to get a clean looking border
346 | subview.hidden = YES;
347 | }
348 |
349 | BOOL needsRounding = (topRounded || bottomRounded);
350 |
351 | // Set the corner radius as needed
352 | cell.layer.cornerRadius = needsRounding ? kTOInsetGroupedTableViewCornerRadius : 0.0f;
353 |
354 | // Set which corners need to be rounded depending on top or bottom
355 | NSUInteger cornerRoundingFlags = 0;
356 | if (topRounded) {
357 | cornerRoundingFlags |= (kCALayerMinXMinYCorner|kCALayerMaxXMinYCorner);
358 | }
359 | if (bottomRounded) {
360 | cornerRoundingFlags |= (kCALayerMinXMaxYCorner|kCALayerMaxXMaxYCorner);
361 | }
362 | cell.layer.maskedCorners = cornerRoundingFlags;
363 | }
364 |
365 | @end
366 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableView/include/TOInsetGroupedTableView.h:
--------------------------------------------------------------------------------
1 | ../TOInsetGroupedTableView.h
--------------------------------------------------------------------------------
/TOInsetGroupedTableView/include/module.modulemap:
--------------------------------------------------------------------------------
1 | module TOInsetGroupedTableView {
2 | umbrella ".."
3 | export *
4 | }
5 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/TOAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // TOGroupInsetTableView
4 | //
5 | // Created by Tim Oliver on 2020/04/08.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TOAppDelegate : UIResponder
12 |
13 | @property (nonatomic, strong) UIWindow *window;
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/TOAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // TOGroupInsetTableView
4 | //
5 | // Created by Tim Oliver on 2020/04/08.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import "TOAppDelegate.h"
10 | #import "TOViewController.h"
11 |
12 | @implementation TOAppDelegate
13 |
14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
15 | {
16 | self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
17 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[TOViewController new]];
18 | [self.window makeKeyAndVisible];
19 |
20 | return YES;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/TOViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // TOGroupInsetTableView
4 | //
5 | // Created by Tim Oliver on 2020/04/08.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TOViewController : UITableViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/TOViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // TOGroupInsetTableView
4 | //
5 | // Created by Tim Oliver on 2020/04/08.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import "TOViewController.h"
10 | #import "TOInsetGroupedTableView.h"
11 |
12 | @interface TOViewController ()
13 |
14 | @end
15 |
16 | @implementation TOViewController
17 |
18 | - (void)loadView
19 | {
20 | self.tableView = [[TOInsetGroupedTableView alloc] init];
21 | }
22 |
23 | - (void)viewDidLoad {
24 | [super viewDidLoad];
25 | self.title = @"TOInsetGroupedTableView";
26 |
27 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Edit"
28 | style:UIBarButtonItemStylePlain
29 | target:self
30 | action:@selector(editButtonTapped)];
31 | }
32 |
33 | - (void)editButtonTapped
34 | {
35 | [self.tableView setEditing:!self.tableView.editing animated:YES];
36 | }
37 |
38 | #pragma mark - Table View Data Source -
39 |
40 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
41 | {
42 | return 5;
43 | }
44 |
45 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
46 | {
47 | NSArray *rows = @[@(3), @(1), @(4), @(5), @(10)];
48 | return [rows[section] integerValue];
49 | }
50 |
51 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
52 | {
53 | return [NSString stringWithFormat:@"Section %ld", (long)section + 1];
54 | }
55 |
56 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
57 | {
58 | static NSString *cellIdentifier = @"Cell";
59 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
60 | if (cell == nil) {
61 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
62 | reuseIdentifier:cellIdentifier];
63 | }
64 |
65 | cell.textLabel.text = [NSString stringWithFormat:@"Cell %ld", (long)indexPath.row];
66 |
67 | return cell;
68 | }
69 |
70 | #pragma mark - Table View Data Source -
71 |
72 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
73 | {
74 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
75 | }
76 |
77 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
78 | {
79 | return YES;
80 | }
81 |
82 | - (void)tableView:(UITableView *)tableView
83 | commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
84 | {
85 | // Enables swipe-to-delete
86 | }
87 |
88 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
89 | {
90 |
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewExample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // TOGroupInsetTableView
4 | //
5 | // Created by Tim Oliver on 2020/04/08.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TOAppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | NSString * appDelegateClassName;
14 | @autoreleasepool {
15 | // Setup code that might create autoreleased objects goes here.
16 | appDelegateClassName = NSStringFromClass([TOAppDelegate class]);
17 | }
18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName);
19 | }
20 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/TOInsetGroupedTableViewTests/TOInsetGroupedTableViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // TOInsetGroupedTableViewTests.m
3 | // TOInsetGroupedTableViewTests
4 | //
5 | // Created by Tim Oliver on 2020/04/11.
6 | // Copyright © 2020 Tim Oliver. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TOInsetGroupedTableView.h"
11 |
12 | @interface TOInsetGroupedTableViewTests : XCTestCase
13 |
14 | @property (nonatomic, strong) TOInsetGroupedTableView *tableView;
15 |
16 | @end
17 |
18 | @implementation TOInsetGroupedTableViewTests
19 |
20 | - (void)setUp
21 | {
22 | UIView *hostView = [[UIView alloc] initWithFrame:(CGRect){0,0,320,480}];
23 | self.tableView = [[TOInsetGroupedTableView alloc] initWithFrame:hostView.bounds];
24 | [hostView addSubview:self.tableView];
25 | }
26 |
27 | - (void)tearDown
28 | {
29 |
30 | }
31 |
32 | - (void)testTableViewCreation
33 | {
34 | // Test that the class builds and can be instantiated without issue
35 | XCTAssertNotNil(self.tableView);
36 |
37 | // Test the style has been set correctly
38 | if (@available(iOS 13.0, *)) {
39 | XCTAssertTrue(self.tableView.style == UITableViewStyleInsetGrouped);
40 | }
41 | else {
42 | XCTAssertTrue(self.tableView.style == UITableViewStyleGrouped);
43 | }
44 | }
45 |
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimOliver/TOInsetGroupedTableView/a17c55acd5faabd7114fb26e908cf1e07b570b1c/screenshot.jpg
--------------------------------------------------------------------------------