facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.81k stars 24.29k forks source link

[Bridge] Using requireNativeComponent for a custom iOS View #1118

Closed Quanatee closed 9 years ago

Quanatee commented 9 years ago

I'm trying to pass a custom iOS view via RCTViewManager, which implements a -view method and returns an RCTView through the brief example listed on react-native.

So far I am getting this error that I am not understanding and am unsure if I am using it incorrectly:

2015-05-03 19:23:40.003 [error][tid:com.facebook.React.JavaScript] "Error: stack:

index.ios.bundle:39053 require index.ios.bundle:245 index.ios.bundle:1127 require index.ios.bundle:245 applyWithGuard index.ios.bundle:873 require index.ios.bundle:196 URL: http://localhost:8081/index.ios.bundle line: 39053 message: undefined is not a function (evaluating 'requireNativeComponent('DrawViewEngine', DrawViewEngine)')"
brentvatne commented 9 years ago

@rymdluo - do you have a component defined in JS called DrawViewEngine? Check out this example: https://github.com/brentvatne/react-native-dashed-border-example/blob/master/index.ios.js - BVDashedBorderView is the native class name, DashedBorderView is the React class that I will use to wrap this component so I can specify customer propTypes etc.

Quanatee commented 9 years ago

@brentvatne Thanks for coming in. I did, I've reworked my code to follow closely to yours and now I'm not getting any errors and I think I got a good result(?)

As DrawViewEngine is actually a drawing view, I am wondering of the native touch handling from my custom view is at fault now or otherwise (i am initialising the view via "initWithFrame:[[UIScreen mainScreen] bounds]"

brentvatne commented 9 years ago

@rymdluo - hey there! are you still having this issue? if so, could you explain it a bit more? unclear on exactly what the problem is :smile:

voronianski commented 9 years ago

@brentvatne I have similar problem but your suggest is not working.

 message: undefined is not a function (evaluating 'requireNativeComponent('DVEffectsView', DVEffectsView)')"
var DVEffectsView = React.createClass({
  render() {
    return <DVEffectsView {...this.props} />;
  }
});
var EffectsView = requireNativeComponent('DVEffectsView', DVEffectsView);

module.exports = EffectsView;

DVEffectsView is placed inside:

And content of obj-c files:

// ... EffectsView.h
#import <UIKit/UIKit.h>
#import <RCTView.h>

@interface DVEffectsView : RCTView

@property (nonatomic, strong) NSString *blurStyle;
@property (nonatomic) BOOL vibrant;

@end

// ... EffectsView.m
#import "DVEffectsView.h"

@interface DVEffectsView ()

@property (nonatomic, strong) UIVisualEffectView *effectsView;

@end

@implementation DVEffectsView

- (instancetype)init
{
    self = [super init];
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    //...
}
@end

// ... DVEffectsViewManager.h
#import <RCTViewManager.h>
#import "DVEffectsView.h"

@interface DVEffectsViewManager : RCTViewManager

@end

// ... DVEffectsViewManager.m
#import "DVEffectsViewManager.h"

@implementation DVEffectsViewManager

RCT_EXPORT_MODULE();

- (UIView *)view
{
    return [[DVEffectsView alloc] init];
}

RCT_EXPORT_VIEW_PROPERTY(blurStyle, NSString);
RCT_EXPORT_VIEW_PROPERTY(vibrant, BOOL);

@end
alinz commented 9 years ago

@voronianski, I think your component setup in javascript is incorrect. I think it should be like this:

var EffectsView = React.createClass({
  render() {
    return <DVEffectsView {...this.props} />;
  }
});

var DVEffectsView = requireNativeComponent('DVEffectsView', EffectsView);

module.exports = EffectsView;
voronianski commented 9 years ago

@alinz sorry, it's just a typo in code example, originally it looks the same as yours:

var EffectsView = React.createClass({
  render() {
    return <DVEffectsView {...this.props} />;
  }
});
var DVEffectsView = requireNativeComponent('DVEffectsView', EffectsView);

Error is still thrown.

alinz commented 9 years ago

@voronianski, which component did you export as a module? EffectView or DVEffectsView? Also, you have to set propTypes as an empty object as well.

voronianski commented 9 years ago

@alinz I've added propTypes and using component as follows:

'use strict';

var React = require('react-native');
var StyleSheet = require('StyleSheet');
var flattenStyle = require('flattenStyle');
var merge = require('merge');
var { View, PropTypes, requireNativeComponent } = React;

var EffectsView = React.createClass({
    propTypes: {
        blurStyle: PropTypes.string,
        vibrantContent: PropTypes.node
    },

    render() {
        return <DVEffectsView {...this.props} />;
    }
});
var DVEffectsView = requireNativeComponent('DVEffectsView', EffectsView);

var EffectsViewComponent = React.createClass({
    render() {
        var { children, vibrantContent } = this.props;
        var style = flattenStyle([styles.base, this.props.style]);
        var nativeProps = merge(this.props, { style });

        if (vibrantContent) {
            nativeProps.vibrant = true;
        }
        var vibrantNode = vibrantContent ? vibrantContent : <View />;

        return (
            <EffectsView {...nativeProps}>
                {vibrantNode}
                {React.Children.map(children, React.addons.cloneWithProps)}
            </EffectsView>
        );
    }
});

var styles = StyleSheet.create({
    base: {
        backgroundColor: 'transparent'
    }
});

module.exports = EffectsViewComponent;
voronianski commented 9 years ago

@alinz Ideally I don't need a wrapper at all:

'use strict';

var React = require('react-native');
var StyleSheet = require('StyleSheet');
var flattenStyle = require('flattenStyle');
var merge = require('merge');
var { View, PropTypes, requireNativeComponent } = React;

// withour wrapper (https://facebook.github.io/react-native/docs/nativecomponentsios.html#ios-mapview-example)
var DVEffectsView = requireNativeComponent('DVEffectsView', null);

var EffectsViewComponent = React.createClass({
    render() {
        var { children, vibrantContent } = this.props;
        var style = flattenStyle([styles.base, this.props.style]);
        var nativeProps = merge(this.props, { style });

        if (vibrantContent) {
            nativeProps.vibrant = true;
        }
        var vibrantNode = vibrantContent ? vibrantContent : <View />;

        return (
            <DVEffectsView {...nativeProps}>
                {vibrantNode}
                {React.Children.map(children, React.addons.cloneWithProps)}
            </DVEffectsView>
        );
    }
});

var styles = StyleSheet.create({
    base: {
        backgroundColor: 'transparent'
    }
});

module.exports = EffectsViewComponent;
However in both examples I receive:
undefined is not a function (evaluating 'requireNativeComponent('DVEffectsView', EffectsView)')"

or

undefined is not a function (evaluating 'requireNativeComponent('DVEffectsView', null)')"

This is the code that worked well on ReactNative version 0.3.x - https://github.com/voronianski/react-native-effects-view/blob/master/index.ios.js

alinz commented 9 years ago

@voronianski I will get back to you.

brentvatne commented 9 years ago

@voronianski - hi there! Are you sure that the backing DVEffectsView class is linked to your project? You can verify it by doing var NativeModules = require('NativeModules') and checking for it on that object.

voronianski commented 9 years ago

@brentvatne you're right there's no DVEffectsView inside NativeModules object though the folder with files is inside XCode project and view manager is calling RCT_EXPORT_MODULE();.

brentvatne commented 9 years ago

@voronianski - interesting, can you push a project up to Github?

ccheever commented 9 years ago

Hi- just checking in. is this still an issue? Have you made any progress in solving this for yourself?

ccheever commented 9 years ago

@voronianski @rymdluo ^^^