iOS【react native】尝试一

#import "React/RCTEventEmitter.h"
@interface FFRNEventEmitter : RCTEventEmitter

// 导出你所有的方法名字
- (NSArray<NSString *> *)supportedEvents;

-(void)iseCallback:(NSString*)code result:(NSString*) result;
-(void)iseVolume:(NSString*)code result:(NSString*) result;
-(void)playCallback:(NSString*)code result:(NSString*) result;

@property (nonatomic, weak) RCTBridge *bridge;

@end


#import "FFRNEventEmitter.h"


@implementation FFRNEventEmitter
@synthesize bridge = _bridge;

RCT_EXPORT_MODULE()

// 导出你所有的方法名字
- (NSArray<NSString *> *)supportedEvents
{
  return @[@"iseCallback", @"iseVolume", @"playCallback"];//有几个就写几个
}

-(void)iseCallback:(NSString*)code result:(NSString*) result
{
  [self sendEventWithName:@"iseCallback"
                     body:@{
                            @"code": code,
                            @"result": result,
                            }];
}

-(void)iseVolume:(NSString*)code result:(NSString*) result
{
  [self sendEventWithName:@"iseCallback"
                     body:@{
                            @"code": code,
                            @"result": result,
                            }];
}

-(void)playCallback:(NSString*)code result:(NSString*) result
{
  [self sendEventWithName:@"iseCallback"
                     body:@{
                            @"code": code,
                            @"result": result,
                            }];
}
@end
#import <Foundation/Foundation.h>
#import "FFRNCatgoryListCtrl.h"

@interface FFRNSington : NSObject

+ (FFRNSington *)sharedInstance;

@property (nonatomic, weak) FFRNCatgoryListCtrl *RNCatgoryListCtrl;
@end


#import "FFRNSington.h"

@implementation FFRNSington

+ (FFRNSington *)sharedInstance
{
  static FFRNSington *instance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    instance = [[self alloc] init];
  });
  return instance;
}

@end
#import <Foundation/Foundation.h>

typedef void (^OperateBlock)(id object);
@interface RNSQLManager : NSObject

@property (nonatomic, copy) OperateBlock operateBlock;
@end


#import "RNSQLManager.h"

@implementation RNSQLManager

- (void)query:(NSString *)queryData successCallback:(OperateBlock)responseSender
{
   
}
@end
#import <UIKit/UIKit.h>
#import "FFEasyLifeCatagoryModel.h"

#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>

#import "React/RCTEventEmitter.h"
#import "FFRNEventEmitter.h"

@interface FFRNCatgoryListCtrl : UIViewController<RCTBridgeModule>
@property (nonatomic, strong) FFEasyLifeCatagoryModel *selectModel;
@end


#import "FFRNCatgoryListCtrl.h"
#import "FFEasyLifeActivityDetailCtrl.h"
#import "FFEasyLifeCataProduListModel.h"

#import "FFRNSington.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTEventEmitter.h>

@interface FFRNCatgoryListCtrl ()

@property (strong, nonatomic) YSTopBarView *topBarView;
@end

@implementation FFRNCatgoryListCtrl


RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(print:(NSString *)text) {
    
    NSLog(@"---- %@",text);
}

// 接收传过来的 NSString + NSDictionary
RCT_EXPORT_METHOD(addEventTwo:(NSString *)name details:(NSDictionary *)details)
{
    RCTLogInfo(@"接收传过来的NSString+NSDictionary: %@ %@", name, details);
    
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
        FFEasyLifeCataProduListModel *model = [[FFEasyLifeCataProduListModel alloc] initWithDic:details];
        model.title2 = @"";
        
        FFEasyLifeActivityDetailCtrl *vc = [[FFEasyLifeActivityDetailCtrl alloc] initWithNibName:kFFEasyLifeActivityDetailCtrl bundle:nil];
        vc.actType = model.type;
        vc.idx = model.goodsId;
        vc.activityId = model.activityId;
        
        [[FFRNSington sharedInstance].RNCatgoryListCtrl.navigationController pushViewController:vc animated:YES];
    }];
    
}

//回调函数,在官方的文档中是有上面的一个警告,不过在使用过程暂时未发现问题。在OC中,我们添加一个getNativeClass方法,将当前模块的类名回调给JS。
RCT_EXPORT_METHOD(getNativeClass:(RCTResponseSenderBlock)callback) {
    callback(@[NSStringFromClass([self class])]);
}


+ (NSArray *)__rct_export__230
{
    return @[ @"", @"addEvent:(NSString *)name location:(NSString *)location" ];
}


//原生模块可以导出一些常量,这些常量在JavaScript端随时都可以访问。用这种方法来传递一些静态数据,可以避免通过bridge进行一次来回交互。
//但是注意这个常量仅仅在初始化的时候导出了一次,所以即使你在运行期间改变constantToExport返回的值,也不会影响到JavaScript环境下所得到的结果。
- (NSDictionary *)constantsToExport {
    return @{ @"BGModuleName" : @"BGNativeModuleExample",
              @"TestName": @"我是从原生定义的~",
              };
}


//  对外提供调用方法,演示Callback
RCT_EXPORT_METHOD(testCallbackEventOne:(NSString *)name callback:(RCTResponseSenderBlock)callback)
{
    NSLog(@"%@",name);
    NSArray *events=@[@"1", @"2", @"3",@"4"]; //准备回调回去的数据
    callback(@[[NSNull null],events]);
}

//  对外提供调用方法,演示Promise使用
RCT_REMAP_METHOD(testCallbackEventTwo,
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
    NSArray *events =@[@"one ",@"two ",@"three"];//准备回调回去的数据
    if (events) {
        resolve(events);
    } else {
        NSError *error=[NSError errorWithDomain:@"我是Promise回调错误信息..." code:101 userInfo:nil];
        reject(@"no_events", @"There were no events", error);
    }
}

/**
 注:不知道为什么RN所在的类中self的内存地址会被修改,意思就是你想要的self跟你在于js交互中用到的self不是同一个self.同时出现的情况就会出现pop不掉我们的RN类的这个界面.
 
 解决办法:使用单例来保存我们原本要用的self,在viewDidLoad方法中做保存
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.topBarView = [[YSTopBarView alloc] initWithFrame:CGRectMake(0, 0, k_SCREEN_WIDTH, 64)];
    self.topBarView.leftLabel.text = @"RN_分类";
    [_topBarView.leftButton addTarget:self action:@selector(actionBack) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.topBarView];
    
//    http://reactnative.cn
    
    
    [FFRNSington sharedInstance].RNCatgoryListCtrl = self;

    NSDictionary *props = @{@"cid" : [NSString limitStringNotEmpty:self.selectModel.idx]};
    NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
    RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                        moduleName:@"EasyLife"
                                                 initialProperties:props
                                                     launchOptions:nil];
    rootView.frame = CGRectMake(0, 64, k_SCREEN_WIDTH, k_SCREEN_HEIGHT - 64);
    [self.view addSubview:rootView];
}

- (void)actionBack {
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end

js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
    SegmentedControlIOS,
    AppRegistry,
    StyleSheet,
    Text,
    Alert,
    FlatList,
    ActivityIndicator,
    Animated,
    ScrollView,
    Image,
    View,
    TouchableOpacity,
} from 'react-native';

var{NativeModules} =require('react-native');
var{NativeEventEmitter} =require('react-native');

var FFRNCatgoryListCtrl=NativeModules.FFRNCatgoryListCtrl;
var FFRNEventEmitter=NativeModules.FFRNEventEmitter;
const myNativeEvt = new NativeEventEmitter(FFRNEventEmitter);  //创建自定义事件接口

var ITEM_HEIGHT = 100;
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
// const REQUEST_URL = 'https://api.github.com/search/repositories?q=javascript&sort=stars';
const REQUEST_URL = 'http://192.168.11.23:8086/app-client-web/commodity/searchCommodity.do?&params=%7BcityId:%22%22,sort:%22sales%22,pageSize:20,cid:%227556cb32899248edb5617a722ab0eb53%22,title:%22%22,client_type:2,sys_user_id:%22%22,page:1,desc:1%7D';


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,
    },
    render: {
        padding: 10,
        paddingTop:20,
        flex: 1,
    },
    icon: {
        marginLeft:10,
        alignItems: 'center',
        width: ITEM_HEIGHT,
        height: ITEM_HEIGHT,
    },
    topicCard: {
        flex: 1,
        flexDirection: 'row',
        padding: 10,
        backgroundColor: '#ffffff',
        borderColor: '#f0f0f0',
        borderStyle: 'solid',
        borderWidth: 1,
        borderRadius: 0
    },
    avatarImg: {
        width: 110,
        height: 110,
        justifyContent:'center',
        borderRadius: 2
    },
    titleMeta: {
        flex: 7,
        marginLeft:15,
        marginRight:10,
        //backgroundColor: 'green',
    },
    topicTitle: {
        marginTop:5,
        fontSize: 16,
        lineHeight: 20,
        fontWeight: 'bold',
        letterSpacing: 1,
        color: '#333333',
        //backgroundColor: '#ff6b13',
    },
    metaarea: {
        flexDirection: 'row',
        marginTop: 10,
        justifyContent: 'flex-start',
        flexWrap: 'wrap',
        //backgroundColor: '#800080',
    },
    metainfo: {
        color: '#999999',
        fontSize: 12,
        lineHeight: 16,
        letterSpacing: 1,
        marginRight: 5
    },
    metaareatag: {
        flexDirection: 'row',
        marginTop: 2,
        justifyContent: 'flex-start',
        flexWrap: 'wrap',
        //backgroundColor: '#556B2F',
    },
    nodename: {
        color: '#999999',
        fontSize: 12,
        lineHeight: 16
    },
    replieCountBg: {
        backgroundColor: '#e74c3c',
        position: 'absolute',
        bottom: 0,
        right: 0,
        paddingBottom: 5,
        paddingTop: 5,
        paddingLeft: 10,
        paddingRight: 10,
        borderRadius: 11
    },
    replieCount: {
        fontSize: 12,
        lineHeight: 12,
        color: '#ffffff',
        fontWeight: 'bold'
    },
});


export default class EasyLife extends Component {

    static navigationOptions = {
        title: 'SegmentedControlIOS',
    };

    constructor(props) {
        super(props);
        this.state = {
            refreshing: true,
            loadMore: false,
            isLoading: true,
            //网络请求状态
            error: false,
            errorInfo: "",
            dataArray: [],
            sort: "sales",
            desc: 1,
            url: REQUEST_URL,
            index: 0,
            cid: "",

        }

        // { (this: any).requestData() = this.requestData().bind(this)}
        // { (this: any)._renderCell() = this._renderCell().bind(this)}
        // { (this: any)._header() = this._header().bind(this)}
        // { (this: any)._footer() = this._footer().bind(this)}

    }

    //渲染完成后调用一次,这个时候DOM结构已经渲染了。这个时候就可以初始化其他框架的设置了,如果利用jQuery绑定事件等等。
    componentDidMount() {
        //请求数据
        this.requestData();
    }


    //在组件中使用
    componentWillMount() {
        this.listener = myNativeEvt.addListener('iseCallback', this.iseCallback.bind(this));  //对应了原生端的名字
    }

    componentWillUnmount() {
        this.listener && this.listener.remove();  //记得remove哦
        this.listener = null;
    }


    /**
     *
     *   一些事件回调
     *
     * */
    iseCallback(data) {//接受原生传过来的数据 data={code:,result:}

        alert('数据');
        if (data.code == CB_CODE_RESULT) {
            //
        }
        else {//..真的是未知的错误
            logf('传回其他参数', data.result);
        }
    }


// 传原生一个字符串 + 回调
    callBackOne = () => {
        FFRNCatgoryListCtrl.testCallbackEventOne(('我是RN给原生的'), (error, events) => {
            if (error) {
                console.error(error);
            } else {
                this.state.cid = events;
                this.renderData();
            }
        })
    }


    callErrorBack = () => {
        // try{
        //     var events=await FFRNCatgoryListCtrl.testCallbackEventTwo();
        //     alert(events)
        // }catch(e){
        //     console.error(e);
        // }
    }


    //加载等待的view
    renderLoadingView() {
        return (
            <View style={styles.container}>
                <ActivityIndicator animating={true} style={[styles.gray, {height: 80}]} color='red' size="large"
                />
            </View>
        );
    }


    //加载失败view
    renderErrorView(error) {
        return (
            <View style={styles.container}>
                <Text> Fail: {error} </Text>
            </View>
        );
    }


    /**
     *
     *  网络请求
     *
     * */

    requestData() {

        let formData = new FormData();
        var test_url = 'http://192.168.11.23:8086/app-client-web/commodity/searchCommodity.do?';

        formData.append("title":"");
        formData.append("cid":"");
        formData.append("cityId":"");
        formData.append("sort":"");
        formData.append("desc":"");
        formData.append("page":"");
        formData.append("pageSize":"");

        let params = '&params={cityId:"",sort:"'
            + this.state.sort
            + '",pageSize:20,cid:"'
            + this.state.cid
            + '",title:"",client_type:2,sys_user_id:"",page:1,desc:'
            + this.state.desc + '}';

        fetch(test_url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: params,
        })

            .then((response) => response.json())
            .then((responseData) => {
                let data = responseData.resultData.items;
                let dataBlob = [];
                let i = 0;

                //遍历data数组中的每个元素,并按照return中的计算方式 形成一个新的元素,放入返回的数组中 如:dataBlob
                //数组判断
                if (Array.isArray(data)) {
                    data.map(function (item) {
                        dataBlob.push({
                            key: i,
                            goodsId: item.goodsId,
                            activityId: item.activityId,
                            type: item.type,
                            name: item.name,
                            imgPath: item.imgPath,
                            discountPrice: item.discountPrice,
                            buycount: item.buycount,
                            tempTest: item.tempTest,
                        })
                        i++;

                    });
                }

                this.setState({
                    //复制数据源
                    dataArray: dataBlob,
                    isLoading: false,
                    refreshing: false,
                    loadMore: false,
                })

                data = null;
                dataBlob = null;
            });
    }


    /**
     *
     *  选择事件
     *
     * */
    changeSegment = (event) => {

        this.state.index = event.nativeEvent.selectedSegmentIndex;

        if (event.nativeEvent.selectedSegmentIndex == 0) {
            this.state.sort = "sales";
            this.state.desc = 1;

        } else if (event.nativeEvent.selectedSegmentIndex == 1) {
            this.state.sort = "price";
            this.state.desc = 1;

        } else {
            this.state.sort = "price";
            this.state.desc = 0;
        }

        //网络请求
        this.requestData();
    }



    /**
     *
     *  按钮事件
     *
     * */
    _onPressItem = (item: Object) => {
        FFRNCatgoryListCtrl.addEventTwo('occ', item);
        //FFRNCatgoryListCtrl.print("Hello World");
    };


    /**
     *
     *  列表单元格
     *
     * */
    _renderCell = (item) => {

        var title = '';
        var discountPrice = '';
        var buycount = '';
        var imgPath = '';
        var tempTest;

        var subData;
        if (this.state.dataArray.length > item.index) {
            subData = this.state.dataArray[item.index];
            title = this.state.dataArray[item.index].name;
            discountPrice = '¥' + this.state.dataArray[item.index].discountPrice;
            buycount = '销售指数' + this.state.dataArray[item.index].buycount;
            imgPath = this.state.dataArray[item.index].imgPath;
            if (imgPath.includes('size')) {
                imgPath = imgPath.replace('size', 'origin');
            }
            tempTest = this.state.dataArray[item.index].tempTest;
        }

        return (
            <TouchableOpacity onPress={()=>{this._onPressItem(subData)}}  activeOpacity={0.8}>
                <View style={styles.topicCard}>
                    <View>
                        <Image style={styles.avatarImg} source={{uri: imgPath}} resizeMode='cover'/>
                    </View>

                    <View style={styles.titleMeta}>
                        <Text style={styles.topicTitle} numberOfLines={2}>{title}</Text>
                        <View style={styles.metaarea}>
                            <Text style={styles.metainfo}>{discountPrice}</Text>
                            <Text style={styles.metainfo}>{tempTest}</Text>
                        </View>

                        <View style={styles.metaareatag}>
                            {/*<Text style={styles.nodename}>{'occ'}</Text>*/}
                            <View style={styles.replieCountBg}>
                                <Text style={styles.replieCount}>{buycount}</Text>
                            </View>
                        </View>
                    </View>

                </View>
            </TouchableOpacity>
        );
    }

    _header = () => {
        return <Text style={[styles.txt, {backgroundColor: 'white'}]}></Text>;
    }

    _footer = () => {
        return <Text style={[styles.txt, {backgroundColor: 'white'}]}></Text>;
    }

    _separator = () => {
        return <View style={{height: 2, backgroundColor: 'yellow'}}/>;
    }

    /**
     *
     *  刷新事件
     *
     * */
    _onRefresh = () => {
        this.setState({refreshing: true})
        this.requestData();
    }

    /**
     *
     * 加载更多 <未实现,有问题>
     *
     * */
    _loadMoreData = () => {

        // alert("-- 加载更多");
        // this.state.loadMore = true;
        // this.requestData();
    }




    renderData() {
        return (
            <View style={{flex: 1}}>
                <SegmentedControlIOS
                    //enabled={false}
                    selectedIndex={this.state.index}
                    //momentary={true}
                    onChange={this.changeSegment}
                    tintColor=''
                    values={['按销量排序', '按价格高到低', '按价格低到高']}/>

                <AnimatedFlatList
                    data={this.state.dataArray}
                    ListHeaderComponent={this._header}
                    ListFooterComponent={this._footer}
                    ItemSeparatorComponent={this._separator}
                    renderItem={this._renderCell}

                    //如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing属性。
                    onRefresh={this._onRefresh}
                    refreshing={this.state.refreshing}
                    //当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。
                    //onEndReached={(info) => { alert("滑动到底部了");}}
                    //onEndReached={this._loadMoreData()}
                    //onEndReachedThreshold={100}
                />
            </View>
        );
    }


    render() {

        //在JS中,我们通过以下方式获取到原生模块的类名
        // FFRNCatgoryListCtrl.getNativeClass(name => {
        //     console.log("nativeClass: ", name);
        //     //alert(name);
        // });

        // console.log("FFRNCatgoryListCtrl value is ", FFRNCatgoryListCtrl.TestName);
        //alert(FFRNCatgoryListCtrl.TestName);

        //alert(this.props.cid);

        this.state.cid = this.props.cid;

        //this.callBackOne();

        //this.callErrorBack();


        //第一次加载等待的view
        if (this.state.isLoading && !this.state.error) {
            return this.renderLoadingView();
        } else if (this.state.error) {
            //请求失败view
            return this.renderErrorView(this.state.errorInfo);
        }
        //加载数据
        return this.renderData();
    }
}

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


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容