React Native原生UI封装以及事件处理
2016-09-06 by 正凯

对于目前的移动端原生开发来说,想要完成一个app的开发工作是比较容易的,因为原生代码在网上所提供出来的各种开源的第三方组件已经成千上万了,足以支撑起你的业务需求。但是如果我们想要在React Native上使用第三方组件怎么办?

众所周知,React Native自身框架也提供了一部分基础组件,已经可以基本满足我们的开发需求,但是,当我们需要使用第三方接口的时候该如何?目前市面上基本所有的第三方组件接口都还不提供对React Native的支持,所以,需要使用第三方的时候,还得我们自己来封装处理。

接下来,我们一步一步来封装友盟分享的组件。


1、首先导入友盟分享SDK,然后添加相关的底层依赖文件:
0_1473132391960_屏幕快照 2016-09-06 上午9.05.37.png

底层依赖库的添加:项目 --> build phases --> link binary with libraries
在link binary with libraries中点加号,添加上图中的依赖文件,如下图所示。

0_1473132527796_屏幕快照 2016-09-06 上午9.13.27.png
2、 添加好依赖文件之后,我们接下来完成在原生中的那部分代码。
(1) 在AppDelegate中的application:didFinishLaunchingWithOptions: 方法中设置友盟AppKey:

 // 设置友盟AppKey
[UMSocialData setAppKey:@"57355f3e67e58ed0a50030a1"];

(2) 写一个分享按钮类,继承于UIButton,然后在其中引入友盟分享头文件UMSocial.h,接下来就可以写按钮触发的分享事件了。

#import "MyShareBt.h"
#import "UMSocial.h"

@implementation MyShareBt
//分享按钮初始化
- (instancetype) initWithFrame:(CGRect)frame{
  if ((self = [super initWithFrame:frame])) {
    [self addTarget:self action:@selector(share)
   forControlEvents:UIControlEventTouchUpInside];
  }
  return self;
}

// 按钮分享事件
- (void)share {
  [UMSocialSnsService presentSnsIconSheetView:[UIApplication sharedApplication].keyWindow.rootViewController appKey:@”yourAppKey” shareText:@”test” shareImage:[UIImage imageNamed:@”yourImageName”] shareToSnsNames:[NSArray arrayWithObjects:UMShareToWechatSession,UMShareToWechatTimeline,UMShareToQzone,UMShareToSina,UMShareToTencent,nil]  delegate:nil];
}

@end

(3)穿件分享组件Manager类,该类继承于RCTViewManager。创建好之后,添加标记宏RCT_EXPORT_MODULE()将改模块导出作为一个组件。最后实现-(UIView *)view方法。代码如下:

#import "shareButtonManager.h"
#import "RCTViewManager.h"
#import "UMSocial.h"
#import "MyShareBt.h"

@interface shareBt : RCTViewManager

@property (nonatomic) MyShareBt *bt;

@end

@implementation shareBt

RCT_EXPORT_MODULE()

- (UIView *)view
{
  _bt = [[MyShareBt alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
  return _bt;
}

@end

至此,原生部分代码最基本的展示部分完成了,接下来就需要在JS中进行进一步封装,以提供给JS调用。

3、接下来实现JS中的组件封装与简单调用。
首先导入原生组件,从导入中取得我们所创建的组件,将其作为默认的组件导出,以供其他JS调用。这里,我们其实可以直接在其他JS中调用了,但是为了进行参数的封装,我们也需要将其封装成一个单独的组件。

import React, { Component, PropTypes } from 'react';
import { requireNativeComponent} from 'react-native';

var ShareBt = requireNativeComponent('shareBt', ZKShareBt);

export default class ZKShareBt extends Component {
  render() {
    return (
      <ShareBt {...this.props} />
    );
  }
}

封装组件的调用。下图所示是前面封装组件的调用,这里我们已经封装了很多JS需要传递给原生的参数,接下来,我们就来说说参数以及事件处理的封装。

<ZKShareBt style={styles.map}
            appKey={'57355f3e67e58ed0a50030a1'}
            shareText={'这是分享内容'}
            imageName={'logo'}
            //myTitle = {this.state.btText}//设置分享按钮标题
            //color={this.state.color}//设置分享按钮标题字体颜色
            btImageName = {'share_icon'} //设置分享按钮图片
   />

4、参数的封装
(1) 定义需要传递的参数

#import <UIKit/UIKit.h>

@interface MyShareBt : UIButton

@property (nonatomic, copy) NSString * appKey;//友盟appkey
@property (nonatomic, copy) NSString * shareText;//分享的文本
@property (nonatomic, copy) NSString * imageName;//分享的图片
@property (nonatomic, copy) NSString * myTitle;//分享按钮标题
@property (nonatomic) UIColor * color;//按钮标题字体颜色
@property (nonatomic, copy) NSString * btImageName;//分享按钮图片

@end

(2)以上参数是我们需要从JS传递给原生的,所以我们首先在原生代码中定义好所需要的参数。定义好之后,我们需要使用RCT_EXPORT_VIEW_PROPERTY宏将其导出给JS。

#import "shareButtonManager.h"
#import "RCTViewManager.h"
#import "UMSocial.h"
#import "MyShareBt.h"

@interface shareBt : RCTViewManager

@property (nonatomic) MyShareBt *bt;

@end

@implementation shareBt

RCT_EXPORT_MODULE()

- (UIView *)view
{
  _bt = [[MyShareBt alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
  return _bt;
}
//将所需参数导出给JS
RCT_EXPORT_VIEW_PROPERTY(appKey, NSString)
RCT_EXPORT_VIEW_PROPERTY(shareText, NSString)
RCT_EXPORT_VIEW_PROPERTY(imageName, NSString)
RCT_EXPORT_VIEW_PROPERTY(myTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
RCT_EXPORT_VIEW_PROPERTY(btImageName, NSString)

@end

(3)重写参数set方法,并给按钮属性赋值,设置UI。

#import "MyShareBt.h"
#import "UMSocial.h"

@implementation MyShareBt

- (instancetype) initWithFrame:(CGRect)frame{
  if ((self = [super initWithFrame:frame])) {
    [self addTarget:self action:@selector(share)
   forControlEvents:UIControlEventTouchUpInside];
  }
  return self;
}
//重写所传递参数的set方法,并将传递过来的参数用于设置UI
- (void)setMyTitle:(NSString *)myTitle{
  [self setTitle:myTitle forState:UIControlStateNormal];
}

- (void)setColor:(UIColor *)color{
  [self setTitleColor:color forState:UIControlStateNormal];
}

- (void)setBtImageName:(NSString *)btImageName{
  [self setBackgroundImage:[UIImage imageNamed:btImageName] forState:UIControlStateNormal];
}

- (void)share {
  [UMSocialSnsService presentSnsIconSheetView:[UIApplication sharedApplication].keyWindow.rootViewController appKey:_appKey shareText:_shareText shareImage:[UIImage imageNamed:_imageName] shareToSnsNames:[NSArray arrayWithObjects:UMShareToWechatSession,UMShareToWechatTimeline,UMShareToQzone,UMShareToSina,UMShareToTencent,nil]  delegate:nil];
}

@end

(4)在JS中将参数封装起来。

import React, { Component, PropTypes } from 'react';
import { requireNativeComponent} from 'react-native';

var ShareBt = requireNativeComponent('shareBt', ZKShareBt);

export default class ZKShareBt extends Component {
  static propTypes = {
    /**
    *
    * 定义组件需要传到原生端的属性
    * 使用React.PropTypes来进行校验
    */
    //使用第三方分享时设置的appKey
    appKey:PropTypes.string,

    //要分享的内容
    shareText:PropTypes.string,

    //需要分享的图片名字(需要事先放在xcode工程中,只需要名字,不需要路径)
    imageName:PropTypes.string,

    //分享按钮标题
    myTitle:PropTypes.string,

    //分享按钮标题颜色
    color:PropTypes.string,

    //分享按钮图片
    btImageName:PropTypes.string,
  };
  render() {
    return (
      <ShareBt {...this.props} />
    );
  }
}

封装好之后,就可以直接调用了。

<ZKShareBt style={styles.map}
            appKey={'57355f3e67e58ed0a50030a1'}
            shareText={'分享内容'}
            imageName={'logo'}
            //myTitle = {this.state.btText}//设置分享按钮标题
            //color={this.state.color}//设置分享按钮标题字体颜色
            btImageName = {'share_icon'} //设置分享按钮图片
   />

运行结果:

0_1473329046795_Simulator Screen Shot 2016年9月6日 上午10.22.09.png

0_1473329073169_Simulator Screen Shot 2016年9月6日 上午10.22.29.png
(第一张图中,一不小心封装了其他App中的视图,这里可以通过fetch请求网络数据,获取流量之后传给原生并根据值的变化动态显示流量所占百分比。请自动忽略。。。。。。)

以上是涉及到UI以及相关参数传递的封装工作,接下来,需要做的是事件的封装。这里事件封装用到的是RCTBubblingEventBlock宏。
封装事件是,首先,我们需要首先在原生中先定义好需要在JS调用的方法模块。
和之前参数定义一样,放在原生UI模块.h文件中

MyShareBt.h

/** button点击事件*/
@property (nonatomic, copy) RCTBubblingEventBlock onButtonClicked;

和参数一样,接下来需要导出:

shareButtonManager.m

RCT_EXPORT_VIEW_PROPERTY(onButtonClicked, RCTBubblingEventBlock)

导出之后,这里我就简单的定义一个分享按钮点击时触发的Delegate方法

MyShareBt.h

@protocol ShareButtonClickedDelegate <NSObject>
@optional
//代理方法
- (void)ButtonClicked;
@end
@property (nonatomic, strong) id <ShareButtonClickedDelegate> ClickDelagate;

该代理方法在按钮点击分享的时候执行:

MyShareBt.m

- (void)share {
  //调用代理方法
  [self.ClickDelagate ButtonClicked];
  //友盟分享
  [UMSocialSnsService presentSnsIconSheetView:[UIApplication sharedApplication].keyWindow.rootViewController appKey:_appKey shareText:_shareText shareImage:[UIImage imageNamed:_imageName] shareToSnsNames:[NSArray arrayWithObjects:UMShareToWechatSession,UMShareToWechatTimeline,UMShareToQzone,UMShareToSina,UMShareToTencent,nil]  delegate:nil];
}

接下来,是实现该代理方法(我在这里设了一个随机数,传到JS中,提供给JS使用,后面JS就可以直接使用传过去的这个随机数):

shareButtonManager.m

#pragma mark ShareButtonClickedDelegate
- (void)ButtonClicked {
  NSInteger x = arc4random() % 100;
  NSLog(@"原生事件%ld",x);
// 将onButtonClicked事件导出
  _bt.onButtonClicked(@{@"randomValue": [NSNumber numberWithInteger:x]});
}

下面我们在js文件中处理:
同样,在参数中添加上onButtonClicked。

ZKShareButton.js

//按钮点击事件
onButtonClicked:PropTypes.func,

添加好之后,就可以调用了,调用如下:

onButtonClicked={(event) => {
            console.log('React事件' + event.nativeEvent.randomValue);
}}

结果显示:
0_1473328977022_屏幕快照 2016-09-08 下午5.55.26.png

[去论坛发表意见]