先來看一下完整效果

集成 RN 的步驟請(qǐng)參照 官方文檔。
這里注意一下項(xiàng)目的目錄結(jié)構(gòu),所有 RN 相關(guān)均在 ReactNative 目錄下。

相應(yīng)地,podfile 和 package.json 要做一下相應(yīng)調(diào)整。
podfile
platform:ios,'8.0'
def native_pods
pod 'JLRoutes', '2.1'
end
def react_pods
pod 'React', :path => './ReactNative/node_modules/react-native', :subspecs => [
'Core',
'ART',
'RCTActionSheet',
'RCTAdSupport',
'RCTGeolocation',
'RCTImage',
'RCTNetwork',
'RCTPushNotification',
'RCTSettings',
'RCTText',
'RCTVibration',
'RCTWebSocket',
'RCTLinkingIOS',
'RCTAnimation',
'DevSupport'
]
pod "Yoga", :path => "./ReactNative/node_modules/react-native/ReactCommon/yoga"
end
target 'CCDemo' do
native_pods
react_pods
end
如果運(yùn)行報(bào)錯(cuò) 如 'RCTAnimation/RCTValueAnimatedNode.h' file not found.,
則將#import <RCTAnimation/RCTValueAnimatedNode.h> 改成 #import "RCTValueAnimatedNode.h"
package.json
{
"name": "CCDemo",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"bundle": "^2.1.0",
"react": "16.0.0-alpha.6",
"react-native": "0.44.3",
"react-navigation": "^1.0.0-beta.26"
}
}
好了,下面來寫兩個(gè) RN 頁(yè)面
新建 RN1Scene.js RN2Scene.js App.js
export default class RN1Scene extends Component {
constructor(props) {
super(props);
const {
navigation,
} = this.props;
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Text style={styles.instructions} onPress={()=>navigate('RN2Scene')}>To RN2Scene</Text>
</View>
);
}
}
export default class RN2Scene extends Component {
handleOnPress() {
console.log('To Native2')
}
render() {
return (
<View style={styles.container}>
<Text style={styles.instructions} onPress={this.handleOnPress}>To Native2</Text>
</View>
);
}
}
由 App.js 統(tǒng)一管理 RN Scene
先來測(cè)試一下RN部分代碼是否可行,
ViewController中
- (IBAction)ToRN1Scene:(id)sender {
NSURL *jsCodeLocation = [NSURL
URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"DemoRN"
initialProperties : nil
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self.navigationController pushViewController:vc animated:YES];
}
現(xiàn)在已經(jīng)完成了 Native -> RN1 -> RN2。同時(shí)也會(huì)出現(xiàn)幾個(gè)問題,
- 導(dǎo)航欄
- 首頁(yè)的返回按鈕
- 返回手勢(shì)
對(duì)于導(dǎo)航欄,由于RN使用的NavigationBar 與原生同時(shí)存在,有可能原生導(dǎo)航欄會(huì)覆蓋在 RN 上,有部分應(yīng)用可能隱藏了系統(tǒng)導(dǎo)航欄,使用自定義的 View,則不存在這個(gè)問題,這里只需要在 RN 頁(yè)面隱藏原生導(dǎo)航欄即可。至于 RN 部分是繼續(xù)自定義還是使用 StackNavigator 就看大家選擇了。
隱藏了原生NavigationBar會(huì)帶來第二個(gè)問題,進(jìn)入RN1Scene時(shí),返回按鈕沒有了。這時(shí)需要自己實(shí)現(xiàn)pop功能。
我們看到呈現(xiàn)的 RN1Scene 和 RN2Scene 都承載到一個(gè) ViewController 中,無論在哪個(gè) Scene 使用返回手勢(shì),都會(huì)返回到原生頁(yè)面。
下面需要進(jìn)行原生部分的工作
新建 bridge 文件,
這里寫一個(gè)原生返回方法,一個(gè)原生跳轉(zhuǎn)方法供 RN 使用
@implementation CCRNBridge
RCT_EXPORT_MODULE(RNBridge)
/// UI 相關(guān) 需寫在主線程
RCT_EXPORT_METHOD(pushToVC:(NSString *)vc paramters:(NSDictionary *)para)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[CCRouterManager sharedInstance] pushToVC:vc paramters:para];
});
}
RCT_EXPORT_METHOD(popViewController:(BOOL)animated)
{
dispatch_async(dispatch_get_main_queue(), ^{
[[CCRouterManager sharedInstance] popViewController:animated];
});
}
/// RN 使用的參數(shù),可以傳方法或者常量
- (NSDictionary *)constantsToExport {
return @{
@"baseurl" : @"someurl"
};
}
@end
在 RN2Scene 加入如下代碼:
import {
NativeModules
} from 'react-native';
var Native = NativeModules.RNBridge;
'''
handleOnPress() {
Native.pushToVC('CCOneController',{labelText:'From RN2'})
}
'''
到這里已經(jīng)完成了 已經(jīng)完成了 Native -> RN1 -> RN2 -> Native。
現(xiàn)在總結(jié)一下現(xiàn)有的跳轉(zhuǎn)方式
| -- | -- |
|---|---|
| Native -> Native | Router |
| RN -> RN | StackNavigator |
| RN -> Native | Router |
還有一種 Native -> RN 這種還是使用比較原始的方法,我們可以再封裝一層,統(tǒng)一使用 Router 的方式跳轉(zhuǎn)。
新建 統(tǒng)一的 Container
@interface CCRNContainer : CCBaseViewController
@property (nonatomic, strong) NSString *moduleName;
@property (nonatomic, strong) NSString *bundlePath;
@property (nonatomic, strong) NSDictionary *paramters;
@end
@implementation CCRNContainer
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *jsCodeLocation = [NSURL
URLWithString:self.bundlePath];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : self.moduleName
initialProperties : self.paramters
launchOptions : nil];
self.view = rootView;
}
@end
ViewController 中
- (IBAction)ToRN1Scene:(id)sender {
NSDictionary *para = @{
@"moduleName" : @"DemoRN",
@"bundlePath" : @"http://localhost:8081/index.ios.bundle?platform=ios",
};
[[CCRouterManager sharedInstance] pushToVC:@"CCRNContainer" paramters:para];
}
至此,我們的幾種跳轉(zhuǎn)方式已經(jīng)統(tǒng)一了。