iOSReact Native

ReactNative 嵌入现有iOS原生应用和原生如何跳转到R

2016-10-20  本文已影响433人  心至靜行至遠

如果你打算在新项目中使用当下最热门的ReactNative混合开发框架,可以参考ReactNative官网或者ReactNative中文网,其中有详细的教程讲解如何初始化一个ReactNative工程。这篇文章讲的是如何在已有项目中集成ReactNative(下文简称为RN)。

已有项目我们使用一个新建工程NativeAPP来代替。首先,我们使用Xcode创建一个新工程命名为NativeAPP。因为我是通过CocoaPods来添加RN链接库的,相信从事苹果应用开发的程序猿们都使用过CocoaPods这个简单方便的第三方库管理工具吧!这里就不赘述了,不熟悉的可以参照这篇文章CocoaPods的安装和使用

安装React Native

1. 配置package.json安装依赖包

在NativeAPP的跟目录下创建一个package.json的配置文件,package.json我们也可以通过react-native init 创建一个新工程拷贝过来。

$ touch package.json
{
  "name": "NativeAPP",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "react-native start",
  },
  "dependencies": {
    "react": "15.3.2",
    "react-native": "^0.35.0"
  }
}

通过npm(Node Package Manager)安装React 和 React Native 到工程根目录的node_modules文件夹中:

$ nam install

2. 创建编辑RN入口index.ios.js文件

既然要集成RN,肯定需要加载RN的组件,安装成功后在NativeAPP的根目录下创建RN要加载的入口js文件index.ios.js:

$ touch index.ios.js
'use strict'

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

export default class NativeAPP extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

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

原生应用

1. 使用Cocoapods添加RN依赖库

使用终端在NativeAPP工程中创建Podfile:

$ pod init

使用vim打开Podfile编辑,添加RN所需要的链接库:


platform :ios, “8.0”
target 'NativeAPP' do
# 取决于你的工程如何组织,你的node_modules文件夹可能会在别的地方。
# 请将:path后面的内容修改为正确的路径(一定要确保正确~~)。
pod 'React', :path => ‘../node_modules/react-native', :subspecs => [
 'Core',
  'ART',
  'RCTActionSheet',
  'RCTAdSupport',
  'RCTGeolocation',
  'RCTImage',
  'RCTNetwork',
  'RCTPushNotification',
  'RCTSettings',
  'RCTText',
  'RCTVibration',
  'RCTWebSocket',
  'RCTLinkingIOS',
]
end

可以根据需求添加工程中依赖的模块,例子中是都添加有,多添加是不会出现问题的,但是少添加是一定会出现问题的,会报“Native module cannot be null”的错误。

注意需要等到npm install安装完成后使用 pod install 来安装链接库,从它的加载路径我们可以看到是从本地进行加载的,这个时候我们本地如果还没有完成对应的Package的安装会报错。所以我们需要安装完成后再在再执行:

$ pod install

2. 配置HTTP请求白名单

在iOS 9以上的系统中,需要配置白名单,否则应用无法通过http协议连接到localhost主机。 如果不这样做,在尝试通过http连接到服务器时,会遭遇这个错误 - Could not connect to development server.
打开工程中的 Info.list 文件,添加下面配置即可:

<key>NSAppTransportSecurity</key> 
<dict> 
  <key>NSExceptionDomains</key>
   <dict> 
      <key>localhost</key> 
        <dict> 
          <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key> 
          <true/>
        </dict>
     </dict>
 </dict>

3. Native代码跳转RN

打开main.storyboard添加一个导航控制器,设置它的根视图控制器为ViewController,在根视图控制器的View上添加一个Button,并且绑定事件到VC中。


原生跳转到RN本质上是通过RCTRootView来加载jsbundle文件的,因此需要在ViewController导入RCTRootView的头文件和创建RCTRootView对象。

#import "RCTRootView.h"

按钮点击事件代码:

NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
RCTRootView *rootView = [[RCTRootView alloc] 
                         initWithBundleURL : jsCodeLocation
                                  moduleName : @"NativeAPP"
                               initialProperties: nil
                                launchOptions : nil];
 UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self.navigationController pushViewController:vc animated:YES];

到这里在iOS原生应用中嵌入RN的工作已经完成了,接下来就是运行查看效果的时候了,在运行工程之前我们需要先将service服务运行起来,在项目的根目录下:

$ npm start

然后再使用Xcode运行我们的原生工程,运行效果图如下:

原生如何跳转到RN不同模块

原生嵌入RN一般会遇到有多个跳转RN的地方,而且跳转到不同模块中去,细心的同学是不是已经发现RCTRootView的:

- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions;

方法中的字典类型** initialProperties**参数,没错我就是通过它来传递参数到js的props属性链上,然后在js根据参数来进行相应模块加载的(如果有其他更好的方法欢迎留言交流)。

按照同样的方法在ViewController再添加一个按钮,按钮的点击事件也一样,区别是在初期化RCTRootView时:

按钮一:

RCTRootView *rootView =
    [[RCTRootView alloc] initWithBundleURL : jsCodeLocation
                         moduleName        : @"NativeAPP"
                         initialProperties : @{
                                               
                                               @"launchOptions":@{
                                                       
                                                        @"componentName":@"PageOne"
                                                       }
                                               }
                          launchOptions    : nil];

按钮二:

RCTRootView *rootView =
    [[RCTRootView alloc] initWithBundleURL : jsCodeLocation
                         moduleName        : @"NativeAPP"
                         initialProperties : @{
                                               
                                               @"launchOptions":@{
                                                       
                                                        @"componentName":@"PageTwo"
                                                       }
                                               }
                          launchOptions    : nil];

接下来修改RN入口文件index.ios.js的渲染逻辑:

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import CompontNameView from './modules/test'

class PageOne extends Component {

  render() {

    return(
      <View style={styles.container}>
        <Text style={styles.welcome}>这是页面一!</Text>
      </View>
    );
  }
}


class PageTwo extends Component {

  render() {

    return(
      <View style={styles.container}>
        <Text style={styles.welcome}>这是页面二!</Text>
      </View>
    );
  }
}


class NativeAPP extends Component {


  render() {

    var  { launchOptions }  = this.props;

    if (launchOptions && launchOptions.componentName) {

        switch (launchOptions.componentName) {
          case 'PageOne':
              return(
                <PageOne/>
              );
            break;
            case 'PageTwo':
              return(
                <PageTwo/>
              );
              break;
          default:

        }
    }

    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

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

因为只是展示效果,所以页面都很简单,下面是展示效果:

上一篇下一篇

猜你喜欢

热点阅读