Aaron
Aaron

Reputation: 7145

Why does my React Native bridged iOS component not work?

I want to create a simple UIView subclass and then make it available as a React Native JavaScript component via bridging to React Native. I have followed these directions and thumbed through lots of the react source code: https://facebook.github.io/react-native/docs/native-components-ios.html

Unfortunately, I don't know where my React Native component is failing. This is my Obj-C manager class:

// header
#import "RCTViewManager.h"
#import "ColorPicker.h"

@interface ColorPickerManager : RCTViewManager

@end

// implementation
#import "ColorPickerManager.h"

@interface ColorPickerManager()

@property (nonatomic) ColorPicker * colorPicker;

@end

@implementation ColorPickerManager

RCT_EXPORT_MODULE()

- (instancetype)init {
    self = [super init];
    if ( self ) {
        NSLog(@"color picker manager init");
    self.colorPicker = [[ColorPicker alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    }
    return self;
}

- (UIView *)view {
    NSLog(@"color picker manager -view method");
    return self.colorPicker;
}

@end

Here is my simple UIView subclass that I vend via the above -view method:

// header
#import <UIKit/UIKit.h>

@interface ColorPicker : UIView

@end

// implementation
#import "ColorPicker.h"

@interface ColorPicker()

@property (nonatomic) NSArray * colors;

@end

@implementation ColorPicker

- (instancetype)init {
    NSLog(@"init");
    self = [super init];
    if ( self ) {
       [self setUp];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    NSLog(@"init with frame: %@", NSStringFromCGRect(frame));
    self = [super initWithFrame:frame];
    if ( self ) {
        [self setUp];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    NSLog(@"init with coder: %@", aDecoder);
   self = [super initWithCoder:aDecoder];
   if ( self ) {
       [self setUp];
   }
   return self;
}

- (void)setUp {
    self.colors = @[[UIColor redColor], [UIColor greenColor], [UIColor blueColor]];
    self.backgroundColor = self.colors[0];
}

- (void)layoutSubviews {
    NSLog(@"layout subviews");
}

@end

Finally, here's my react component being bridged over to JavaScript:

var { requireNativeComponent } = require('react-native');
var ColorPicker = requireNativeComponent('ColorPicker', null);
module.exports = ColorPicker;
debugger;

And it's declaration and render to the screen:

'use strict';
var React = require('react-native');
var ColorPicker = require('./BridgedComponents/ColorPicker.js');


var {
  StyleSheet
} = React;

var iOS = React.createClass({

    render: function() {
        debugger;
        return (
            <ColorPicker style={styles.container} />
        );
    }
});

var styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
});

AppRegistry.registerComponent('iOS', () => iOS);

This results in nothing rendering to the screen when I expect to see a 100x100 red square. What I have tried:

Questions for RN Gurus - What else can I do to verify that my view is correctly bridged? - Is there anything I should look for in the Chrome Debugger when inspecting these instances to verify that the view was set up correctly? - I've tried following some of the source code in the repo but I'm still new to React and I'm not 100% how its all working. - When I create a bridged component am I expected to set the appearance and layout in the Obj-C/Swift class or is it better to do that in JavaScript with CSS. Seems to me the former would be expected.

Any help/advice will be greatly appreciated.

Upvotes: 3

Views: 1963

Answers (1)

Jean Regisser
Jean Regisser

Reputation: 6716

You don't see anything but your ColorPicker view is added.

The problem is ReactNative is managing its backgroundColor property itself and overrides your initial choice.

You can add the following method in your ColorPicker.m and put a breakpoint to see when it happens:

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    NSLog(@"setBackgroundColor %@", backgroundColor);
    [super setBackgroundColor:backgroundColor];
}

You should let React Native handle the size and appearance of your bridged component. But it's totally find to have custom subviews that are entirely controlled from the native side.

Also your ColorPickerManager should return a new instance of the view you're bridging whenever it calls the view method.

i.e. you should use this:

@implementation ColorPickerManager

RCT_EXPORT_MODULE()

- (instancetype)init {
  self = [super init];
  if ( self ) {
    NSLog(@"color picker manager init");
  }
  return self;
}

- (UIView *)view {
  NSLog(@"color picker manager -view method");
  return [[ColorPicker alloc] init];
}

@end

Otherwise you won't be able to display 2 (or more) instances of your ColorPicker component.

Upvotes: 1

Related Questions