React Native 封装iOS原生组件事件点击的理解(一)

      这两天在做一个插件,RN 调用iOS 端封装的一个Vew,需要对这个view 进行点击操作,跳转到横屏进行处理。经实验发现,iOS端,在view 外面包一个<TouchableOpacity>可以实现view 的点击透传,但andriod 端不起作用,为了和andriod 端保持一致,所以学习了下事件的传递。

      下面以一个Demo,进行说明,由于是新手,难免会有理解不当,如有错,望前辈们指点一下。

      该Demo 主要实现的功能是初始根据JS 端给的初值,调用Xcode 端图片,生成view,并旋转90度(这里只是想记忆一下view 旋转90度的方法),然后点击图片,并传message给JS,在JS端重新刷新页面,改变text 值,这个传来的值,也会以warn 的形式显示。

1.首先在Xcode 创建四个文件,并放两个图片名为"one.jpg"和"two.jpg"

  • BridgeView.h代码如下:其中propertyArr初值会从JS端传过来。
#import <UIKit/UIKit.h>
#import <React/RCTComponent.h>

NS_ASSUME_NONNULL_BEGIN

@interface BridgeView : UIImageView

@property (nonatomic,strong)NSArray *propertyArr;        
@property(nonatomic,strong)RCTBubblingEventBlock onClick;

@end

NS_ASSUME_NONNULL_END
  • BridgeView.m代码如下:根据propertyArr设置View 显示哪张图片以及是否旋转90度。在封装的view 里添加了一个点击事件,通过此事件把message信息传给JS。
#import "BridgeView.h"

@implementation BridgeView

- (id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  if (self) {
    UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doClick)];
    [self addGestureRecognizer:gesture];
  }
  
  return self;
}

- (void)doClick {
  if(self.onClick)
  {
    self.onClick(@{@"message":@"我是Native传过来的值"});
  }
}

- (void)setPropertyArr:(NSArray *)propertyArr {
  _propertyArr = propertyArr;
  
  if (propertyArr.count == 2) {
    if ([propertyArr.firstObject isEqualToString:@"one"] && [propertyArr.lastObject isEqualToString:@"nomal"]) {
      UIImage *image = [UIImage imageNamed:@"one"];
      self.image = image;
      
    } else {  //第二张图片旋转90度
      self.image = [UIImage imageNamed:@"two"];
      self.transform = CGAffineTransformMakeRotation(M_PI*90/180);
    }
  }

}

@end
  • BridgeViewManager.h是继承于 RCTViewManager
#import <React/RCTViewManager.h>

NS_ASSUME_NONNULL_BEGIN

@interface BridgeViewManager : RCTViewManager

@end

NS_ASSUME_NONNULL_END
  • 在BridgeViewManager.m里
#import "BridgeViewManager.h"
#import "BridgeView.h"


@implementation BridgeViewManager

RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(propertyArr,NSArray)
RCT_EXPORT_VIEW_PROPERTY(onClick, RCTBubblingEventBlock)


- (UIView *)view {
  return [[BridgeView alloc]init];
}

@end

2.在JS 端创建reactView.js 文件,引入xcode 端创建的BridgeView文件,

需要说明的两点:

(1)为了更直观的说明点击了原生的view,从原生端传过来了一个"message",并赋值给了状态属性isone,重新刷新了页面,原生端传过来的信息就显示在了界面上。

(2)引入这句话import PropTypes from 'prop-types'的前提是进入项目根目录,执行如下代码安装 prop-types 库:

npm install --save prop-types
  • 创建reactView.js 文件: 
import React,{ Component } from 'react';
import PropTypes from 'prop-types';
import { requireNativeComponent,Text,View,StyleSheet } from 'react-native';

var BridgeView = requireNativeComponent('BridgeView',AridgeVie);  //引入原生文件


export default  class AridgeView extends Component {
    constructor(props) {
        super(props);
        this.state = { isone: "one" }; 
    }


    static propTypes = {
        propertyArr:PropTypes.array,
    };

    _onChange = (event) => {
        console.warn(`${event.nativeEvent["message"]}`,);
        this.setState({
            isone :`${event.nativeEvent["message"]}`
          });
    }

    render() { 
        return (
            <View>
             <BridgeView style = {styles.bridgeViewStyle} {...this.props} onClick={this._onChange}/>
            <Text style = {styles.textStyle}>{this.state.isone}</Text>
           </View>
        );
     }
}

const styles = StyleSheet.create({
    bridgeViewStyle: {
        marginHorizontal:50,
        height:400,
        marginTop:200
    },
    textStyle: {
        marginHorizontal:50,
        height:50,
        marginTop:50,
        fontSize:20,
        color:'red',
        fontWeight:'bold',
    },
})

值得一提的是:

var BridgeView = requireNativeComponent('BridgeView',AridgeVie);

第二个参数是封装的组件AridgeVie,官网上说,这使得 React Native 的底层框架可以检查原生属性和包装类的属性是否一致,来减少出现问题的可能。===没理解。

  • 在App.js 引入刚刚创建的reactView.js,并赋初值propertyArr = {["two","nomal"]。
import React, { Component } from 'react';
import ReactView from  "./reactView"
import {
  StyleSheet,
  Dimensions,
} from "react-native";

export default class App extends Component {
  constructor(props) {
    super(props)
  }


  render() {
    return (
        <ReactView style={styles.imageViewStyle} propertyArr = {["two","nomal"]} ></ReactView>
    );
  }
}


const styles = StyleSheet.create({
  imageViewStyle: {
      backgroundColor: "white",
      marginLeft:50,
      marginTop: 100,
      width: Dimensions.get('window').width - 100,
      height: 400
  },
});

3.最后结果为:

初始界面为图片旋转了90度

当点击了图片后,native值传到了JS进行了显示:

在做项目中遇到的坑:

这期间参考了这篇博主的文章:参考Demo

原作者是封装了一个button,通过点击button ,改变button 的标题。button 的点击事件是直接写在CLFButtonManager.m里的,起初,我照着作者的思路,把view 的点击事件也直接写在了BridgeViewManager.m里,可是结果是:我在项目里,点击图片跳转到横屏,再回到这个页面后,再点击图片发现没反应了,andriod 是没问题的,和同事在JS端查代码好久,最后有个同事告诉我我xcode 代码写得有问题,在BridgeViewManager.m写gesture 事件只能点一次,后来改成上面的写法就ok 了。其实,还是没理解,谁叫渣呢。

后来,为了继续弄明白事件的传递机制,我把这个参考Demo的代码改了下,见此下一篇

猜你喜欢

转载自blog.csdn.net/baihailing/article/details/86534104