Skip to content

Commit eb25634

Browse files
committed
Initial
0 parents  commit eb25634

File tree

7 files changed

+383
-0
lines changed

7 files changed

+383
-0
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.DS_Store
2+
*.[a|o]
3+
*.pyc
4+
build/*
5+
xcuserdata
6+
*.xcworkspace
7+
!default.xcworkspace
8+
project.xcworkspace
9+
Pods
10+
Podfile.lock

ASPTableViewController.podspec

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Pod::Spec.new do |s|
2+
3+
s.name = "ASPTableViewController"
4+
s.version = "0.1.0"
5+
s.summary = "A UITableViewController subclass that moves cells to their own controller classes, and makes the tree's data source data-driven."
6+
s.description = <<-DESC
7+
A UITableViewController subclass that:
8+
9+
* moves cells to their own controller classes
10+
* makes the tree's data source data-driven
11+
12+
This is especially useful when
13+
* the collection structure is variable, or unknown at build time
14+
* you have shared a cell style across several collection views.
15+
DESC
16+
17+
s.homepage = "https://github.com/gperks/ASPTableViewController"
18+
s.license = 'MIT'
19+
s.author = { "A Single Pixel, LLC" => "[email protected]" }
20+
s.source = { :git => "https://github.com/gperks/ASPTableViewController.git", :tag => s.version.to_s }
21+
s.platform = :ios, '7.0'
22+
s.ios.deployment_target = '7.0'
23+
24+
s.source_files = 'Classes/*.{m,h}'
25+
s.public_header_files = 'Classes/*.h'
26+
27+
s.requires_arc = true
28+
29+
end

Classes/ASPTableViewCell.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// ASPTableViewCell.h
3+
//
4+
// Created by Graham Perks on 4/21/14.
5+
// Copyright (c) 2014 A Single Pixel, LLC. All rights reserved.
6+
//
7+
8+
#import <UIKit/UIKit.h>
9+
#import "ASPTableViewController.h"
10+
11+
12+
@interface ASPTableViewCell : UITableViewCell
13+
14+
// Subclasses should implement this method.
15+
+(void)registerCellForTableViewController:(ASPTableViewController*)tableViewController;
16+
17+
-(void) configureInViewController:(UITableViewController*)viewController fromRowDefinition:(NSMutableDictionary*)rowDefinition;
18+
19+
@end

Classes/ASPTableViewCell.m

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// ASPTableViewCell.m
3+
//
4+
// Created by Graham Perks on 4/21/14.
5+
// Copyright (c) 2014 A Single Pixel, LLC. All rights reserved.
6+
//
7+
8+
#import "ASPTableViewCell.h"
9+
10+
@implementation ASPTableViewCell
11+
12+
13+
- (void)configureAccessoryTypeFrom:(NSMutableDictionary *)rowDefinition
14+
{
15+
NSString *accessory = rowDefinition[@"accessory"];
16+
17+
if (accessory) {
18+
NSDictionary *accessorySwitch = @{
19+
@"disclosure": ^{ self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; },
20+
@"detailDisclosure": ^{ self.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; },
21+
@"detail": ^{ self.accessoryType = UITableViewCellAccessoryDetailButton; }
22+
};
23+
24+
typedef void (^CaseBlock)();
25+
((CaseBlock)accessorySwitch[accessory])();
26+
}
27+
}
28+
29+
- (void)configureAccessoryImageFrom:(NSMutableDictionary *)rowDefinition
30+
{
31+
NSString *accessoryImage = rowDefinition[@"accessoryImage"];
32+
33+
if (accessoryImage) {
34+
UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:accessoryImage]];
35+
self.accessoryView = iv;
36+
}
37+
}
38+
39+
- (void)configureSelectionStyleFrom:(NSMutableDictionary *)rowDefinition
40+
{
41+
NSString *accessory = rowDefinition[@"selectionStyle"];
42+
43+
if (accessory) {
44+
NSDictionary *accessorySwitch = @{
45+
@"default": ^{ self.selectionStyle = UITableViewCellSelectionStyleDefault; },
46+
@"none": ^{ self.selectionStyle = UITableViewCellSelectionStyleNone; },
47+
};
48+
49+
typedef void (^CaseBlock)();
50+
((CaseBlock)accessorySwitch[accessory])();
51+
}
52+
}
53+
54+
-(void) configureInViewController:(UITableViewController*)viewController fromRowDefinition:(NSMutableDictionary*)rowDefinition
55+
{
56+
[self configureAccessoryTypeFrom:rowDefinition];
57+
[self configureAccessoryImageFrom:rowDefinition];
58+
[self configureSelectionStyleFrom:rowDefinition];
59+
}
60+
61+
+(void)registerCellForTableViewController:(ASPTableViewController*)tableViewController
62+
{
63+
// Override me
64+
}
65+
66+
//- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
67+
//{
68+
// self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
69+
// if (self) {
70+
// // Initialization code
71+
// }
72+
// return self;
73+
//}
74+
//
75+
//- (void)awakeFromNib
76+
//{
77+
// // Initialization code
78+
//}
79+
//
80+
//- (void)setSelected:(BOOL)selected animated:(BOOL)animated
81+
//{
82+
// [super setSelected:selected animated:animated];
83+
//
84+
// // Configure the view for the selected state
85+
//}
86+
87+
@end

Classes/ASPTableViewController.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// ASPTableViewController.h
3+
//
4+
// Created by Graham Perks on 4/21/14.
5+
// Copyright (c) 2014 A Single Pixel, LLC. All rights reserved.
6+
//
7+
8+
#import <UIKit/UIKit.h>
9+
10+
@interface ASPTableViewController : UITableViewController
11+
12+
@property (nonatomic, strong) NSURL *jsonDefinitionURL;
13+
14+
-(void)registerClass:(Class)cellClass forCellType:(NSString *)identifier;
15+
16+
@end

Classes/ASPTableViewController.m

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//
2+
// ASPTableViewController.m
3+
//
4+
// Created by Graham Perks on 4/21/14.
5+
// Copyright (c) 2014 A Single Pixel, LLC. All rights reserved.
6+
//
7+
8+
// Your viewDidLoad, in a ASPTableViewController subclass, could look ike this:
9+
// - (void)viewDidLoad
10+
// {
11+
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"shoes.json" withExtension:nil];
12+
// self.jsonDefinitionURL = url;
13+
//
14+
// [super viewDidLoad];
15+
// ...
16+
17+
#import "ASPTableViewController.h"
18+
#import "ASPTableViewCell.h"
19+
20+
21+
@interface ASPTableViewController ()
22+
@property (nonatomic, strong) NSMutableArray *sections;
23+
@property (nonatomic, strong) NSMutableDictionary *rowClasses;
24+
@end
25+
26+
27+
@implementation ASPTableViewController
28+
29+
//- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
30+
//{
31+
// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
32+
// if (self) {
33+
// // Custom initialization
34+
// }
35+
// return self;
36+
//}
37+
38+
39+
- (void)viewDidLoad
40+
{
41+
[super viewDidLoad];
42+
self.rowClasses = [NSMutableDictionary dictionary];
43+
}
44+
45+
46+
-(void)setJsonDefinitionURL:(NSURL *)url
47+
{
48+
_jsonDefinitionURL = url;
49+
50+
if (url) {
51+
52+
NSError *error = nil;
53+
NSInputStream *stream = [[NSInputStream alloc] initWithURL:url];
54+
[stream open];
55+
self.sections = [NSJSONSerialization JSONObjectWithStream:stream
56+
options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves
57+
error:&error];
58+
59+
if (error) {
60+
NSLog(@"Failed to read table definition %@", url);
61+
}
62+
}
63+
else {
64+
self.sections = [NSMutableArray array];
65+
}
66+
}
67+
68+
69+
-(NSMutableDictionary*)cellInfoFromIndexPath:(NSIndexPath*)indexPath
70+
{
71+
NSMutableDictionary *section = self.sections[indexPath.section];
72+
NSMutableArray *cells = section[@"rows"];
73+
NSMutableDictionary *cellInfo = cells[indexPath.row];
74+
75+
return cellInfo;
76+
}
77+
78+
79+
#pragma mark - Table view delegate
80+
81+
-(void)performActionForCell:(NSMutableDictionary*)cellInfo
82+
{
83+
// Built in actions; we invoke one of these:
84+
// 1. Perform a selector
85+
// 2. Push a storyboard's view controller
86+
// 3. Perform a segue
87+
NSString *selector = cellInfo[@"selector"];
88+
NSString *segue = cellInfo[@"segue"];
89+
NSString *storyboardName = cellInfo[@"storyboard"];
90+
NSString *viewControllerId = cellInfo[@"viewControllerId"];
91+
92+
if (selector) {
93+
SEL sel = NSSelectorFromString(selector);
94+
IMP imp = [self methodForSelector:sel];
95+
void (*func)(id, SEL, NSMutableDictionary*) = (void *)imp;
96+
func(self, sel, cellInfo);
97+
}
98+
else if (storyboardName) {
99+
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
100+
UIViewController *vc;
101+
if (viewControllerId) {
102+
vc = [storyboard instantiateViewControllerWithIdentifier:viewControllerId];
103+
}
104+
else {
105+
vc = [storyboard instantiateInitialViewController];
106+
}
107+
108+
[self prepareForStoryboard:storyboardName viewControllerId:viewControllerId fromCell:cellInfo destination:vc];
109+
[self.navigationController pushViewController:vc animated:YES];
110+
}
111+
else if (segue) {
112+
[self performSegueWithIdentifier:segue sender:self];
113+
}
114+
}
115+
116+
-(void)prepareForStoryboard:(NSString *)storyboardName
117+
viewControllerId:(NSString*)viewControllerId
118+
fromCell:(NSDictionary*)cellInfo
119+
destination:(UIViewController*)viewController
120+
{
121+
// Can be overridden to prepare a destination viewController, cf prepareForSegue.
122+
}
123+
124+
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
125+
{
126+
NSMutableDictionary *cellInfo = [self cellInfoFromIndexPath:indexPath];
127+
128+
[self performActionForCell:cellInfo];
129+
}
130+
131+
#pragma mark - Table View data source
132+
133+
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
134+
{
135+
return self.sections.count;
136+
}
137+
138+
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
139+
{
140+
NSDictionary *sectionDefinition = self.sections[section];
141+
NSArray *rows = sectionDefinition[@"rows"];
142+
return rows.count;
143+
}
144+
145+
146+
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
147+
{
148+
NSMutableDictionary *rowDefinition = [self cellInfoFromIndexPath:indexPath];
149+
NSString *cellId = rowDefinition[@"type"];
150+
151+
ASPTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellId forIndexPath:indexPath];
152+
if ( ! [cell isKindOfClass:[ASPTableViewCell class]]) {
153+
NSLog(@"Cell must be a subclass of ASPTableViewCell!");
154+
}
155+
156+
[cell configureInViewController:self fromRowDefinition:rowDefinition ];
157+
158+
return cell;
159+
}
160+
161+
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
162+
{
163+
NSMutableDictionary *rowDefinition = [self cellInfoFromIndexPath:indexPath];
164+
165+
CGFloat height = 44;
166+
Class class = self.rowClasses[rowDefinition[@"type"]];
167+
168+
SEL heightSelector = NSSelectorFromString(@"heightForRowDefinition:inViewController:");
169+
170+
171+
if (class && [class respondsToSelector:heightSelector]) {
172+
173+
NSNumber *heightNumber;
174+
175+
// 2 ways to invoke..
176+
// 1:
177+
IMP imp = [class methodForSelector:heightSelector];
178+
NSNumber* (*func)(id, SEL, NSMutableDictionary*, UIViewController*) = (void *)imp;
179+
heightNumber = func(class, heightSelector, rowDefinition, self);
180+
181+
// // 2:
182+
// NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature methodSignatureForSelector:heightSelector]];
183+
// [invocation invokeWithTarget:class];
184+
// [invocation getReturnValue:&heightNumber];
185+
186+
height = [heightNumber doubleValue];
187+
}
188+
189+
return height;
190+
}
191+
192+
#pragma mark - Cell class registration
193+
194+
// Rows must register their classes with us independently from the table view's
195+
// registration, as we need to call class methods on them before the table view
196+
// instatiates them.
197+
-(void)registerClass:(Class)cellClass forCellType:(NSString *)identifier
198+
{
199+
self.rowClasses[identifier] = cellClass;
200+
}
201+
202+
203+
@end

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2014 A Single Pixel, LLC <[email protected]>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

0 commit comments

Comments
 (0)