Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement - allow custom reusable cells #76

Merged
merged 5 commits into from
Mar 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions RNTableView.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
0808A0331C67E9A60038993A /* RNReactModuleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0808A0321C67E9A60038993A /* RNReactModuleCell.m */; };
084AEF361C6B155900A0A569 /* RNAppGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = 084AEF351C6B155900A0A569 /* RNAppGlobals.m */; };
872545E11BAAC85D00889249 /* JSONDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 872545D61BAAC85D00889249 /* JSONDataSource.m */; };
872545E21BAAC85D00889249 /* RNCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 872545D81BAAC85D00889249 /* RNCellView.m */; };
872545E31BAAC85D00889249 /* RNCellViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 872545DA1BAAC85D00889249 /* RNCellViewManager.m */; };
Expand All @@ -32,6 +34,10 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
0808A0311C67E9A60038993A /* RNReactModuleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNReactModuleCell.h; sourceTree = "<group>"; };
0808A0321C67E9A60038993A /* RNReactModuleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNReactModuleCell.m; sourceTree = "<group>"; };
084AEF341C6B155900A0A569 /* RNAppGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNAppGlobals.h; sourceTree = "<group>"; };
084AEF351C6B155900A0A569 /* RNAppGlobals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNAppGlobals.m; sourceTree = "<group>"; };
872545D51BAAC85D00889249 /* JSONDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONDataSource.h; sourceTree = "<group>"; };
872545D61BAAC85D00889249 /* JSONDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONDataSource.m; sourceTree = "<group>"; };
872545D71BAAC85D00889249 /* RNCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCellView.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -69,6 +75,10 @@
872545D41BAAC85D00889249 /* RNTableView */ = {
isa = PBXGroup;
children = (
084AEF341C6B155900A0A569 /* RNAppGlobals.h */,
084AEF351C6B155900A0A569 /* RNAppGlobals.m */,
0808A0311C67E9A60038993A /* RNReactModuleCell.h */,
0808A0321C67E9A60038993A /* RNReactModuleCell.m */,
872545D51BAAC85D00889249 /* JSONDataSource.h */,
872545D61BAAC85D00889249 /* JSONDataSource.m */,
872545D71BAAC85D00889249 /* RNCellView.h */,
Expand Down Expand Up @@ -177,11 +187,13 @@
872545E31BAAC85D00889249 /* RNCellViewManager.m in Sources */,
8799E1E01BF1152400AF9A67 /* RNTableFooterViewManager.m in Sources */,
872545E51BAAC85D00889249 /* RNTableViewCell.m in Sources */,
0808A0331C67E9A60038993A /* RNReactModuleCell.m in Sources */,
8799E1DD1BF114F900AF9A67 /* RNTableFooterView.m in Sources */,
8799E1E31BF117F600AF9A67 /* RNTableHeaderView.m in Sources */,
8799E1E61BF1181400AF9A67 /* RNTableHeaderViewManager.m in Sources */,
872545E61BAAC85D00889249 /* RNTableViewManager.m in Sources */,
872545E11BAAC85D00889249 /* JSONDataSource.m in Sources */,
084AEF361C6B155900A0A569 /* RNAppGlobals.m in Sources */,
872545E41BAAC85D00889249 /* RNTableView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
13 changes: 13 additions & 0 deletions RNTableView/RNAppGlobals.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

#import <foundation/Foundation.h>
@class RCTBridge;

@interface RNAppGlobals : NSObject {
RCTBridge *appBridge;
}

@property (nonatomic, retain) RCTBridge *appBridge;

+ (id)sharedInstance;

@end
24 changes: 24 additions & 0 deletions RNTableView/RNAppGlobals.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// RNGlobals.m
// RNTableView
//
// Created by Anna Berman on 2/10/16.
// Copyright © 2016 Pavlo Aksonov. All rights reserved.
//

#import "RNAppGlobals.h"

@implementation RNAppGlobals

@synthesize appBridge;

+ (id)sharedInstance {
static RNAppGlobals *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}

@end
19 changes: 19 additions & 0 deletions RNTableView/RNReactModuleCell.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

#import <UIKit/UIKit.h>

//Use react-native root views as reusable cells returned from cellForRowAtIndexPath.

//App must use the code below to store the app bridge, else cells will be blank
//AppDelegate.m, didFinishLaunchingWithOptions:
// #import <RNTableView/RNAppGlobals.h>
//RCTRootView *rootView = ...
//[[RNAppGlobals sharedInstance] setAppBridge:rootView.bridge];

@interface RNReactModuleCell : UITableViewCell {
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier bridge:(RCTBridge*) bridge data:(NSDictionary*)data indexPath:(NSIndexPath*)indexPath reactModule:(NSString*)reactModule;

-(void)setUpAndConfigure:(NSDictionary*)data bridge:(RCTBridge*)bridge indexPath:(NSIndexPath*)indexPath reactModule:(NSString*)reactModule;

@end
54 changes: 54 additions & 0 deletions RNTableView/RNReactModuleCell.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// RNReactModuleCell.m
// RNTableView
//
// Created by Anna Berman on 2/6/16.
// Copyright © 2016 Pavlo Aksonov. All rights reserved.
//

#import <RCTRootView.h>
#import "RNReactModuleCell.h"

@implementation RNReactModuleCell {
RCTRootView *_rootView;
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier bridge:(RCTBridge*) bridge data:(NSDictionary*)data indexPath:(NSIndexPath*)indexPath reactModule:(NSString*)reactModule
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setUpAndConfigure:data bridge:bridge indexPath:indexPath reactModule:reactModule];
}
return self;
}

-(NSDictionary*) toProps:(NSDictionary *)data indexPath:(NSIndexPath*)indexPath {
return @{@"data":data, @"section":[[NSNumber alloc] initWithLong:indexPath.section], @"row":[[NSNumber alloc] initWithLong:indexPath.row]};
}

-(void)setUpAndConfigure:(NSDictionary*)data bridge:(RCTBridge*)bridge indexPath:(NSIndexPath*)indexPath reactModule:(NSString*)reactModule{
NSDictionary *props = [self toProps:data indexPath:indexPath];
if (_rootView == nil) {
//Create the mini react app that will populate our cell. This will be called from cellForRowAtIndexPath
_rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:reactModule initialProperties:props];
if (data[@"width"]) {
CGRect contentViewFrame = self.contentView.frame;
contentViewFrame.size.width = ((NSNumber*)data[@"width"]).floatValue;
self.contentView.frame = contentViewFrame;
}
[self.contentView addSubview:_rootView];
_rootView.frame = self.contentView.frame;
} else {
//Ask react to re-render us with new data
_rootView.appProperties = props;
}

//The application will be unmounted in javascript when the cell/rootview is destroyed
}

-(void)prepareForReuse {
[super prepareForReuse];
//TODO prevent stale data flickering
}

@end
1 change: 1 addition & 0 deletions RNTableView/RNTableView.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@
@property (nonatomic) BOOL autoFocus;
@property (nonatomic) BOOL allowsToggle;
@property (nonatomic) BOOL allowsMultipleSelection;
@property (nonatomic) NSString *reactModuleForCell;

@end
36 changes: 31 additions & 5 deletions RNTableView/RNTableView.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#import "RNCellView.h"
#import "RNTableFooterView.h"
#import "RNTableHeaderView.h"
#import "RNReactModuleCell.h"
#import "RNAppGlobals.h"

@interface RNTableView()<UITableViewDataSource, UITableViewDelegate> {
id<RNTableViewDatasource> datasource;
Expand All @@ -25,9 +27,11 @@ @interface RNTableView()<UITableViewDataSource, UITableViewDelegate> {
@end

@implementation RNTableView {
RCTBridge *_bridge;
RCTEventDispatcher *_eventDispatcher;
NSArray *_items;
NSMutableArray *_cells;
NSString *_reactModuleCellReuseIndentifier;
}

-(void)setEditing:(BOOL)editing {
Expand Down Expand Up @@ -71,6 +75,7 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
RCTAssertParam(eventDispatcher);

if ((self = [super initWithFrame:CGRectZero])) {
_bridge = [[RNAppGlobals sharedInstance] appBridge];
_eventDispatcher = eventDispatcher;
_cellHeight = 44;
_cells = [NSMutableArray array];
Expand Down Expand Up @@ -148,6 +153,8 @@ - (void)createTableView {
_tableView.tableHeaderView = view;
_tableView.tableFooterView = view;
_tableView.separatorStyle = self.separatorStyle;
_reactModuleCellReuseIndentifier = @"ReactModuleCell";
[_tableView registerClass:[RNReactModuleCell class] forCellReuseIdentifier:_reactModuleCellReuseIndentifier];
[self addSubview:_tableView];
}
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(nonnull UIView *)view forSection:(NSInteger)section {
Expand Down Expand Up @@ -312,20 +319,38 @@ -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)
}
return count;
}

-(UITableViewCell*)setupReactModuleCell:(UITableView *)tableView data:(NSDictionary*)data indexPath:(NSIndexPath *)indexPath {
RCTAssert(_bridge, @"Must set global bridge in AppDelegate, e.g. \n\
#import <RNTableView/RNAppGlobals.h>\n\
[[RNAppGlobals sharedInstance] setAppBridge:rootView.bridge]");
RNReactModuleCell *cell = [tableView dequeueReusableCellWithIdentifier:_reactModuleCellReuseIndentifier];
if (cell == nil) {
cell = [[RNReactModuleCell alloc] initWithStyle:self.tableViewCellStyle reuseIdentifier:_reactModuleCellReuseIndentifier bridge: _bridge data:data indexPath:indexPath reactModule:_reactModuleForCell];
} else {
[cell setUpAndConfigure:data bridge:_bridge indexPath:indexPath reactModule:_reactModuleForCell];
}
return cell;
}

-(UITableViewCell* )tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
UITableViewCell *cell = nil;
NSDictionary *item = [self dataForRow:indexPath.item section:indexPath.section];

// check if it is standard cell or user-defined UI
if (![self hasCustomCells:indexPath.section]){
if ([self hasCustomCells:indexPath.section]){
cell = ((RNCellView *)_cells[indexPath.section][indexPath.row]).tableViewCell;
} else if (self.reactModuleForCell != nil && ![self.reactModuleForCell isEqualToString:@""]) {
cell = [self setupReactModuleCell:tableView data:item indexPath:indexPath];
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:self.tableViewCellStyle reuseIdentifier:@"Cell"];
}
cell.textLabel.text = item[@"label"];
cell.detailTextLabel.text = item[@"detail"];
} else {
cell = ((RNCellView *)_cells[indexPath.section][indexPath.row]).tableViewCell;
}

if (item[@"selected"] && [item[@"selected"] intValue]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else if ([item[@"arrow"] intValue]) {
Expand Down Expand Up @@ -354,7 +379,8 @@ -(NSMutableDictionary *)dataForRow:(NSInteger)row section:(NSInteger)section {

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (![self hasCustomCells:indexPath.section]){
return _cellHeight;
NSNumber *styleHeight = _sections[indexPath.section][@"items"][indexPath.row][@"height"];
return styleHeight.floatValue ?: _cellHeight;
} else {
RNCellView *cell = (RNCellView *)_cells[indexPath.section][indexPath.row];
CGFloat height = cell.componentHeight;
Expand Down
5 changes: 5 additions & 0 deletions RNTableView/RNTableViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ - (UIView *)view
[view setTableViewCellEditingStyle:[RCTConvert NSInteger:json]];
}

/*Each cell is a separate app, multiple cells share the app/module name*/
RCT_CUSTOM_VIEW_PROPERTY(reactModuleForCell, NSString*, RNTableView) {
[view setReactModuleForCell:[RCTConvert NSString:json]];
}

RCT_CUSTOM_VIEW_PROPERTY(contentInset, UIEdgeInsets, RNTableView) {
[view setContentInset:[RCTConvert UIEdgeInsets:json]];
}
Expand Down
8 changes: 7 additions & 1 deletion examples/TableViewDemo/iOS/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "AppDelegate.h"

#import "RCTRootView.h"
#import <RNTableView/RNAppGlobals.h>

@implementation AppDelegate

Expand Down Expand Up @@ -43,10 +44,15 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
* see http://facebook.github.io/react-native/docs/runningondevice.html
*/

// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"TableViewExample" initialProperties:@{} launchOptions:launchOptions];

//Save main bridge so that RNTableView could access our bridge to create its RNReactModuleCells
[[RNAppGlobals sharedInstance] setAppBridge:rootView.bridge];


self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
Expand Down
Loading