前言
我的天终于接到一个面试通知,内牛满面。
一、今日计划
我觉得光看不练可不行。今天我们就用前两天学到的知识实操一个页面出来。加油!
二、开始!
1.目标:写一个简单的RN页面,接到原生的iOS项目中,并使用原生的某个类完成某些操作。
我在网上找到一个免费的api微信热门精选,我们就来写一个获取微信热门精选的页面,具备网络请求,下拉刷新的列表页面。
2.封装网络类!
我寻思什么类能用原生写,然后RN用,想来想去这个小项目也就网络类放在原生写比较靠谱,技术不达标的情况下贸然使用JS的Fetch来写我是慌慌的下不了手。
新建一个NetWork
类
#import <Foundation/Foundation.h>
@interface Network : NSObject
@end
//
// Network.m
// RNTest
//
// Created by fanfangliang on 16/10/29.
// Copyright © 2016年 fanfangliang. All rights reserved.
//
#import "Network.h"
#import <AFNetworking/AFNetworking.h>//这里我们用AFN
#import "RCTBridgeModule.h"//记得import这个
//然后遵循RCTBridgeModule协议
@interface Network ()<RCTBridgeModule>
@end
@implementation Network
//加上这个宏
RCT_EXPORT_MODULE();
#pragma mark - RN method
//获取微信热门精选 暴露给RN的接口 接受一个param{@"num":@(1),@"page":@(10)} 然后回调callback
RCT_EXPORT_METHOD(wxListWithParam:(NSDictionary *)param Callback:(RCTResponseSenderBlock)callback)
{
[[Network sharedInstance] getFromUrl:@"http://apis.baidu.com/txapi/weixin/wxhot" params:param callback:^(NSDictionary *dicResponse, NSError *error) {
//这里我们先直接返回dicResponse(假装接口没有问题,有需要的同学加上错误处理)
callback(@[dicResponse]);
}];
}
//以下是对AFN最基础的调用
#pragma mark - common method
+ (instancetype)sharedInstance
{
static Network *worker = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
worker = [[Network alloc] init];
});
return worker;
}
- (AFHTTPSessionManager *)mgr
{
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"text/plain", nil];
manager.requestSerializer.timeoutInterval = 30.0;
//这里,因为api要求将apikey放在header里,所以需要设置以下,你们可以改成自己的key
[manager.requestSerializer setValue:@"4ff218a1fd6*********984497d8" forHTTPHeaderField:@"apikey"];
});
return manager;
}
- (NSURLSessionDataTask *)getFromUrl:(NSString *)url params:(NSDictionary *)params callback:(void (^)(NSDictionary* dicResponse, NSError* error))callback
{
return [self.mgr GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
callback(responseObject,nil);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
callback(nil,error);
}];
}
- (NSURLSessionDataTask *)postFromUrl:(NSString *)url params:(NSDictionary *)params callback:(void (^)(NSDictionary* dicResponse, NSError* error))callback
{
return [self.mgr POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
callback(responseObject,nil);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
callback(nil,error);
}];
}
@end
3.viewcontroller其实和昨天一样,我就改了个模块名称
//
// ViewController.m
// RNTest
//
// Created by fanfangliang on 16/10/28.
// Copyright © 2016年 fanfangliang. All rights reserved.
//
#import "ViewController.h"
#import "RCTRootView.h"
#import "Network.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)highScoreButtonPressed:(id)sender {
NSLog(@"High Score Button Pressed");
//这里设置了主入口文件的请求路径
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
//RCTRootView是一个UIView
//传入请求路径、模块名称、初始属性(是一个字典)
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL : jsCodeLocation
moduleName : @"Index"
initialProperties :nil
launchOptions : nil];
UIViewController *vc = [[UIViewController alloc] init];
//然后创建一个控制器,将rootView赋给控制器的view,就能看到
vc.view = rootView;
[self presentViewController:vc animated:YES completion:nil];
}
@end
4.Podfile增加这次需要引入的库
pod 'React', :path => 'node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTImage',# 天坑,我以为<Image>标签应该在Core库里,再不济也应该在'RCTImageView'里,结果他叫'RCTImage',浪费我感情
'RCTWebSocket', # needed for debugging
]
pod 'AFNetworking'
end
5.重点来了,index.ios.js
'use strict'
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
RefreshControl,
NativeModules,//这里需要引入NativeModules模块
} from 'react-native';
//把一些公有的属性定义下
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
var page = 1;
var datas = [];
class Index extends React.Component {
constructor(props){
super(props);
this.state = {
isRefreshing:false,//这里RefreshControl需要用到isRefreshing,不然会黄条提示,虽然我还不知道它有卵用现在
dataSource: ds.cloneWithRows([])//这里,是listview的数据源
};
}
//当模块被加载的时候,我们请求一把数据
componentWillMount(){
this.loadData();
}
loadData(){
console.log('调用Loaddata');
this.setState({isRefreshing: true});
//获取到Native的Network类,就是我们上面写的网络请求类
var Network = NativeModules.Network;
//调用微信数据接口,传过去{'num':'10','page':String(page)}和回调方法
Network.wxListWithParam({'num':'10','page':String(page)},(response) => {
//当获取到数据,page+1
page = page + 1;
//把老数据和新数据合并成一个新的数组并存起来
var newslist = response['newslist'];
datas = newslist.concat(datas);
//设置state,主要是改datasource.
this.setState({isRefreshing: false,dataSource:ds.cloneWithRows(datas)});
});
}
render() {
return (
<View style = {styles.main}>
<View style = {styles.navBar}>
<Text style = {styles.titleView} onPress={this.loadData}>微信热门精选</Text>
</View>
<ListView
enableEmptySections = {true}
style={styles.listView}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
refreshControl={
//RefreshControl是一个新控件
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._onrefresh.bind(this)}//当下拉刷新的时候,去调用_onrefresh方法。这里需要bind(this)坑了我半天,具体原因我觉得我要再回去看看JS语法,先不误导你们了
tintColor="#ff0000"
title="Loading..."
titleColor="#00ff00"
colors={['#ff0000', '#00ff00', '#0000ff']}
progressBackgroundColor="#ffff00"
/>
}
/>
</View>
);
}
//这里去刷新
_onrefresh(){
console.log('下拉');
this.loadData();
}
//这里是把数据赋给视图,就像我们把数据赋给cell一样
renderRow(rowData){
return(
<View style={styles.cell}>
<Image source={{uri: rowData['picUrl']}}
style={styles.imageview} />
<View style={styles.textArea}>
<Text style={styles.titleText}>{rowData['title']}</Text>
<Text style={styles.timeText}>{rowData['ctime']}</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
main:{
flex:1,
backgroundColor:'white'
},
navBar:{
backgroundColor:'gray',
height:64,
},
titleView:{
marginTop:32,
textAlign:'center',
fontSize:20,
color:'white'
},
listView:{
backgroundColor:'green',
},
cell:{
height:60,
backgroundColor:'white',
marginBottom:0.5,
flexDirection:'row'
},
imageview:{
marginTop:10,
marginLeft:10,
width:40,
height:40,
backgroundColor:'yellow'
},
textArea:{
marginTop:10,
marginLeft:10,
height:40,
flexDirection:'column',
justifyContent:'space-between',
},
titleText:{
fontWeight:'bold'
},
timeText:{
color:'darkgray'
}
});
AppRegistry.registerComponent('Index', () => Index);
最后秀下成果:
![](https://img.haomeiwen.com/i1642566/4e6ea0b8a3c7a50b.png)
丑是丑点,勿喷勿喷啦
网友评论