日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

React Native使用指南-原生UI组件

發(fā)布時(shí)間:2024/7/23 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 React Native使用指南-原生UI组件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在如今的App中,已經(jīng)有成千上萬(wàn)的原生UI部件了——其中的一些是平臺(tái)的一部分,另一些可能來(lái)自于一些第三方庫(kù),而且可能你自己還收藏了很多。React Native已經(jīng)封裝了大部分最常見的組件,譬如ScrollView和TextInput,但不可能封裝全部組件。而且,說(shuō)不定你曾經(jīng)為自己以前的App還封裝過(guò)一些組件,React Native肯定沒(méi)法包含它們。幸運(yùn)的是,在React Naitve應(yīng)用程序中封裝和植入已有的組件非常簡(jiǎn)單。

和原生模塊向?qū)б粯?#xff0c;本向?qū)б彩且粋€(gè)相對(duì)高級(jí)的向?qū)?#xff0c;我們假設(shè)你已經(jīng)對(duì)iOS編程頗有經(jīng)驗(yàn)。本向?qū)?huì)引導(dǎo)你如何構(gòu)建一個(gè)原生UI組件,帶領(lǐng)你了解React Native核心庫(kù)中MapView組件的具體實(shí)現(xiàn)。

iOS MapView樣例

假設(shè)我們要把地圖組件植入到我們的App中——我們用到的是MKMapView,而現(xiàn)在只需要讓它可以被Javascript重用。

原生視圖都需要被一個(gè)RCTViewManager的子類來(lái)創(chuàng)建和管理。這些管理器在功能上有些類似“視圖控制器”,但它們本質(zhì)上都是單例 - React Native只會(huì)為每個(gè)管理器創(chuàng)建一個(gè)實(shí)例。它們創(chuàng)建原生的視圖并提供給RCTUIManager,RCTUIManager則會(huì)反過(guò)來(lái)委托它們?cè)谛枰臅r(shí)候去設(shè)置和更新視圖的屬性。RCTViewManager還會(huì)代理視圖的所有委托,并給JavaScript發(fā)回對(duì)應(yīng)的事件。

提供原生視圖很簡(jiǎn)單:

  • 首先創(chuàng)建一個(gè)子類
  • 添加RCT_EXPORT_MODULE()標(biāo)記宏
  • 實(shí)現(xiàn)-(UIView *)view方法
// RCTMapManager.m #import <MapKit/MapKit.h>#import "RCTViewManager.h"@interface RCTMapManager : RCTViewManager @end@implementation RCTMapManagerRCT_EXPORT_MODULE()- (UIView *)view {return [[MKMapView alloc] init]; }@end

接下來(lái)你需要一些Javascript代碼來(lái)讓這個(gè)視圖變成一個(gè)可用的React組件:

// MapView.jsvar { requireNativeComponent } = require('react-native');// requireNativeComponent 自動(dòng)把這個(gè)組件提供給 "RCTMapManager" module.exports = requireNativeComponent('RCTMap', null);

現(xiàn)在我們就已經(jīng)實(shí)現(xiàn)了一個(gè)完整功能的地圖組件了,諸如捏放和其它的手勢(shì)都已經(jīng)完整支持。但是現(xiàn)在我們還不能真正的從Javascript端控制它。(╯﹏╰)

屬性

我們能讓這個(gè)組件變得更強(qiáng)大的第一件事情就是要能夠封裝一些原生屬性供Javascript使用。舉例來(lái)說(shuō),我們希望能夠禁用手指捏放操作,然后指定一個(gè)初始的地圖可見區(qū)域。禁用捏放操作只需要一個(gè)布爾值類型的屬性就行了,所以我們添加這么一行:

// RCTMapManager.m RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)

注意我們現(xiàn)在把類型聲明為BOOL類型——React Native用RCTConvert來(lái)在JavaScript和原生代碼之間完成類型轉(zhuǎn)換。如果轉(zhuǎn)換無(wú)法完成,會(huì)產(chǎn)生一個(gè)“紅屏”的報(bào)錯(cuò)提示,這樣你就能立即知道代碼中出現(xiàn)了問(wèn)題。如果一切進(jìn)展順利,上面這個(gè)宏就已經(jīng)包含了導(dǎo)出屬性的全部實(shí)現(xiàn)。

現(xiàn)在要想禁用捏放操作,我們只需要在JS里設(shè)置對(duì)應(yīng)的屬性:

// MyApp.js <MapView pitchEnabled={false} />

但這樣并不能很好的說(shuō)明這個(gè)組件的用法——用戶要想知道我們的組件有哪些屬性可以用,以及可以取什么樣的值,他不得不一路翻到Objective-C的代碼。要解決這個(gè)問(wèn)題,我們可以創(chuàng)建一個(gè)封裝組件,并且通過(guò)PropTypes來(lái)說(shuō)明這個(gè)組件的接口。

// MapView.js var React = require('react-native'); var { requireNativeComponent } = React;class MapView extends React.Component {render() {return <RCTMap {...this.props} />;} }MapView.propTypes = {/*** 當(dāng)這個(gè)屬性被設(shè)置為true,并且地圖上綁定了一個(gè)有效的可視區(qū)域的情況下,* 可以通過(guò)捏放操作來(lái)改變攝像頭的偏轉(zhuǎn)角度。* 當(dāng)這個(gè)屬性被設(shè)置成false時(shí),攝像頭的角度會(huì)被忽略,地圖會(huì)一直顯示為俯視狀態(tài)。*/pitchEnabled: React.PropTypes.bool, };var RCTMap = requireNativeComponent('RCTMap', MapView);module.exports = MapView;

譯注:使用了封裝組件之后,你還需要注意到module.exports導(dǎo)出的不再是requireNativeComponent的返回值,而是所創(chuàng)建的包裝組件。

現(xiàn)在我們有了一個(gè)封裝好的組件,還有了一些注釋文檔,用戶使用起來(lái)也更方便了。注意我們現(xiàn)在把requireNativeComponent的第二個(gè)參數(shù)從null變成了用于封裝的組件MapView。這使得React Native的底層框架可以檢查原生屬性和包裝類的屬性是否一致,來(lái)減少出現(xiàn)問(wèn)題的可能。

現(xiàn)在,讓我們添加一個(gè)更復(fù)雜些的region屬性。我們首先添加原生代碼:

// RCTMapManager.m RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) {[view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES]; }

這段代碼比剛才的一個(gè)簡(jiǎn)單的BOOL要復(fù)雜的多了。現(xiàn)在我們多了一個(gè)需要做類型轉(zhuǎn)換的MKCoordinateRegion類型,還添加了一部分自定義的代碼,這樣當(dāng)我們?cè)贘S里改變地圖的可視區(qū)域的時(shí)候,視角會(huì)平滑地移動(dòng)過(guò)去。在我們提供的函數(shù)體內(nèi),json代表了JS中傳遞的尚未解析的原始值。函數(shù)里還有一個(gè)view變量,使得我們可以訪問(wèn)到對(duì)應(yīng)的視圖實(shí)例。最后,還有一個(gè)defaultView對(duì)象,這樣當(dāng)JS給我們發(fā)送null的時(shí)候,可以把視圖的這個(gè)屬性重置回默認(rèn)值。

你可以為視圖編寫任何你所需要的轉(zhuǎn)換函數(shù)——下面就是MKCoordinateRegion的轉(zhuǎn)換實(shí)現(xiàn),它通過(guò)兩個(gè)RCTConvert的擴(kuò)展來(lái)完成:

@implementation RCTConvert(CoreLocation)RCT_CONVERTER(CLLocationDegrees, CLLocationDegrees, doubleValue); RCT_CONVERTER(CLLocationDistance, CLLocationDistance, doubleValue);+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json {json = [self NSDictionary:json];return (CLLocationCoordinate2D){[self CLLocationDegrees:json[@"latitude"]],[self CLLocationDegrees:json[@"longitude"]]}; }@end@implementation RCTConvert(MapKit)+ (MKCoordinateSpan)MKCoordinateSpan:(id)json {json = [self NSDictionary:json];return (MKCoordinateSpan){[self CLLocationDegrees:json[@"latitudeDelta"]],[self CLLocationDegrees:json[@"longitudeDelta"]]}; }+ (MKCoordinateRegion)MKCoordinateRegion:(id)json {return (MKCoordinateRegion){[self CLLocationCoordinate2D:json],[self MKCoordinateSpan:json]}; }

這些轉(zhuǎn)換函數(shù)被設(shè)計(jì)為可以安全的處理任何JS扔過(guò)來(lái)的JSON:當(dāng)有任何缺少的鍵或者其它問(wèn)題發(fā)生的時(shí)候,顯示一個(gè)“紅屏”的錯(cuò)誤提示。

為了完成region屬性的支持,我們還需要在propTypes里添加相應(yīng)的說(shuō)明(否則我們會(huì)立刻收到一個(gè)錯(cuò)誤提示),然后就可以像使用其他屬性一樣使用了:

// MapView.jsMapView.propTypes = {/*** 當(dāng)這個(gè)屬性被設(shè)置為true,并且地圖上綁定了一個(gè)有效的可視區(qū)域的情況下,* 可以通過(guò)捏放操作來(lái)改變攝像頭的偏轉(zhuǎn)角度。* 當(dāng)這個(gè)屬性被設(shè)置成false時(shí),攝像頭的角度會(huì)被忽略,地圖會(huì)一直顯示為俯視狀態(tài)。*/pitchEnabled: React.PropTypes.bool,/*** 地圖要顯示的區(qū)域。** 區(qū)域由中心點(diǎn)坐標(biāo)和區(qū)域范圍坐標(biāo)來(lái)定義。* */region: React.PropTypes.shape({/*** 地圖中心點(diǎn)的坐標(biāo)。*/latitude: React.PropTypes.number.isRequired,longitude: React.PropTypes.number.isRequired,/*** 最小/最大經(jīng)、緯度間的距離。*/latitudeDelta: React.PropTypes.number.isRequired,longitudeDelta: React.PropTypes.number.isRequired,}), };// MyApp.jsrender() {var region = {latitude: 37.48,longitude: -122.16,latitudeDelta: 0.1,longitudeDelta: 0.1,};return <MapView region={region} />;}

現(xiàn)在你可以看到region屬性的整個(gè)結(jié)構(gòu)已經(jīng)加上了文檔說(shuō)明——將來(lái)可能我們會(huì)自動(dòng)生成一些類似的代碼,但目前還沒(méi)有這樣的手段。

有時(shí)候你的原生組件有一些特殊的屬性希望導(dǎo)出,但并不希望它成為公開的接口。舉個(gè)例子,Switch組件可能會(huì)有一個(gè)onChange屬性用來(lái)傳遞原始的原生事件,然后導(dǎo)出一個(gè)onValueChange屬性,這個(gè)屬性在調(diào)用的時(shí)候會(huì)帶上Switch的狀態(tài)作為參數(shù)之一。這樣的話你可能不希望原生專用的屬性出現(xiàn)在API之中,也就不希望把它放到propTypes里。可是如果你不放的話,又會(huì)出現(xiàn)一個(gè)報(bào)錯(cuò)。解決方案就是帶上額外的nativeOnly參數(shù),像這樣:

var RCTSwitch = requireNativeComponent('RCTSwitch', Switch, {nativeOnly: { onChange: true } });

事件

現(xiàn)在我們已經(jīng)有了一個(gè)原生地圖組件,并且從JS可以很容易的控制它了。不過(guò)我們?cè)趺床拍芴幚韥?lái)自用戶的事件,譬如縮放操作或者拖動(dòng)來(lái)改變可視區(qū)域?關(guān)鍵的步驟就在于讓RCTMapManager來(lái)委托我們提供的所有視圖,然后把事件通過(guò)分發(fā)器傳遞給JavaScript。最終的代碼看起來(lái)類似這樣(比起完整的實(shí)現(xiàn)有所簡(jiǎn)化):

// RCTMapManager.m#import "RCTMapManager.h"#import <MapKit/MapKit.h>#import "RCTBridge.h" #import "RCTEventDispatcher.h" #import "UIView+React.h"@interface RCTMapManager() <MKMapViewDelegate> @end@implementation RCTMapManagerRCT_EXPORT_MODULE()- (UIView *)view {MKMapView *map = [[MKMapView alloc] init];map.delegate = self;return map; }#pragma mark MKMapViewDelegate- (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(BOOL)animated {MKCoordinateRegion region = mapView.region;NSDictionary *event = @{@"target": mapView.reactTag,@"region": @{@"latitude": @(region.center.latitude),@"longitude": @(region.center.longitude),@"latitudeDelta": @(region.span.latitudeDelta),@"longitudeDelta": @(region.span.longitudeDelta),}};[self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event]; }

如你所見,我們剛才配置了管理器,委托它代理創(chuàng)建的所有視圖,并且在委托方法-mapView:regionDidChangeAnimated:中,把地圖目前的區(qū)域以及reactTag目標(biāo)封裝成了一個(gè)事件,這樣我們的事件就可以通過(guò)sendInputEventWithName:body:發(fā)送到正確的React組件實(shí)例上。事件名@"topChange"對(duì)應(yīng)的是JavaScript端的onChange回調(diào)屬性。這個(gè)回調(diào)會(huì)被原生事件執(zhí)行,然后我們通常都會(huì)在封裝組件里做一些處理,來(lái)使得API更簡(jiǎn)明:

// MapView.jsclass MapView extends React.Component {constructor() {this._onChange = this._onChange.bind(this);}_onChange(event: Event) {if (!this.props.onRegionChange) {return;}this.props.onRegionChange(event.nativeEvent.region);}render() {return <RCTMap {...this.props} onChange={this._onChange} />;} } MapView.propTypes = {/*** Callback that is called continuously when the user is dragging the map.*/onRegionChange: React.PropTypes.func,... };

樣式

因?yàn)槲覀兯械囊晥D都是UIView的子類,大部分的樣式屬性應(yīng)該直接就可以生效。但有一部分組件會(huì)希望使用自己定義的默認(rèn)樣式,例如UIDatePicker希望自己的大小是固定的。這個(gè)默認(rèn)屬性對(duì)于布局算法的正常工作來(lái)說(shuō)很重要,但我們也希望在使用這個(gè)組件的時(shí)候可以覆蓋這些默認(rèn)的樣式。DatePickerIOS實(shí)現(xiàn)這個(gè)功能的辦法是通過(guò)封裝一個(gè)擁有彈性樣式的額外視圖,然后在內(nèi)層的視圖上應(yīng)用一個(gè)固定樣式(通過(guò)原生傳遞來(lái)的常數(shù)生成):

// DatePickerIOS.ios.jsvar RCTDatePickerIOSConsts = require('react-native').UIManager.RCTDatePicker.Constants; ...render: function() {return (<View style={this.props.style}><RCTDatePickerIOSref={DATEPICKER}style={styles.rkDatePickerIOS}.../></View>);} });var styles = StyleSheet.create({rkDatePickerIOS: {height: RCTDatePickerIOSConsts.ComponentHeight,width: RCTDatePickerIOSConsts.ComponentWidth,}, });

常量RCTDatePickerIOSConsts在原生代碼中導(dǎo)出,從一個(gè)組件的實(shí)際布局上獲取到:

// RCTDatePickerManager.m- (NSDictionary *)constantsToExport {UIDatePicker *dp = [[UIDatePicker alloc] init];[dp layoutIfNeeded];return @{@"ComponentHeight": @(CGRectGetHeight(dp.frame)),@"ComponentWidth": @(CGRectGetWidth(dp.frame)),@"DatePickerModes": @{@"time": @(UIDatePickerModeTime),@"date": @(UIDatePickerModeDate),@"datetime": @(UIDatePickerModeDateAndTime),}}; }

本向?qū)Ц采w了包裝原生組件所需了解的許多方面,不過(guò)你可能還有很多知識(shí)需要了解,譬如特殊的方式來(lái)插入和布局子視圖。如果你想更深入了解,可以閱讀RCTMapManager和其它的組件的源代碼。


本文轉(zhuǎn)自React Native中文網(wǎng):http://reactnative.cn/docs/0.20/native-component-ios.html#content

總結(jié)

以上是生活随笔為你收集整理的React Native使用指南-原生UI组件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。