Skip to content

Commit

Permalink
Merge pull request #76 from nyura123/master
Browse files Browse the repository at this point in the history
enhancement - allow custom reusable cells
  • Loading branch information
Pavlo Aksonov committed Mar 4, 2016
2 parents 6cedfcf + eb1679b commit c636e43
Show file tree
Hide file tree
Showing 12 changed files with 603 additions and 25 deletions.
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 @@ -357,7 +382,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

0 comments on commit c636e43

Please sign in to comment.