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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

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

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

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

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

iOS MapView樣例

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

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

提供原生視圖很簡單:

  • 首先創(chuàng)建一個子類
  • 添加RCT_EXPORT_MODULE()標(biāo)記宏
  • 實現(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

接下來你需要一些Javascript代碼來讓這個視圖變成一個可用的React組件:

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

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

屬性

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

// RCTMapManager.m RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)

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

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

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

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

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

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

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

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

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

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

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

@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è)計為可以安全的處理任何JS扔過來的JSON:當(dāng)有任何缺少的鍵或者其它問題發(fā)生的時候,顯示一個“紅屏”的錯誤提示。

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

// MapView.jsMapView.propTypes = {/*** 當(dāng)這個屬性被設(shè)置為true,并且地圖上綁定了一個有效的可視區(qū)域的情況下,* 可以通過捏放操作來改變攝像頭的偏轉(zhuǎn)角度。* 當(dāng)這個屬性被設(shè)置成false時,攝像頭的角度會被忽略,地圖會一直顯示為俯視狀態(tài)。*/pitchEnabled: React.PropTypes.bool,/*** 地圖要顯示的區(qū)域。** 區(qū)域由中心點坐標(biāo)和區(qū)域范圍坐標(biāo)來定義。* */region: React.PropTypes.shape({/*** 地圖中心點的坐標(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屬性的整個結(jié)構(gòu)已經(jīng)加上了文檔說明——將來可能我們會自動生成一些類似的代碼,但目前還沒有這樣的手段。

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

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

事件

現(xiàn)在我們已經(jīng)有了一個原生地圖組件,并且從JS可以很容易的控制它了。不過我們怎么才能處理來自用戶的事件,譬如縮放操作或者拖動來改變可視區(qū)域?關(guān)鍵的步驟就在于讓RCTMapManager來委托我們提供的所有視圖,然后把事件通過分發(fā)器傳遞給JavaScript。最終的代碼看起來類似這樣(比起完整的實現(xià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)封裝成了一個事件,這樣我們的事件就可以通過sendInputEventWithName:body:發(fā)送到正確的React組件實例上。事件名@"topChange"對應(yīng)的是JavaScript端的onChange回調(diào)屬性。這個回調(diào)會被原生事件執(zhí)行,然后我們通常都會在封裝組件里做一些處理,來使得API更簡明:

// 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,... };

樣式

因為我們所有的視圖都是UIView的子類,大部分的樣式屬性應(yīng)該直接就可以生效。但有一部分組件會希望使用自己定義的默認樣式,例如UIDatePicker希望自己的大小是固定的。這個默認屬性對于布局算法的正常工作來說很重要,但我們也希望在使用這個組件的時候可以覆蓋這些默認的樣式。DatePickerIOS實現(xiàn)這個功能的辦法是通過封裝一個擁有彈性樣式的額外視圖,然后在內(nèi)層的視圖上應(yīng)用一個固定樣式(通過原生傳遞來的常數(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)出,從一個組件的實際布局上獲取到:

// 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了包裝原生組件所需了解的許多方面,不過你可能還有很多知識需要了解,譬如特殊的方式來插入和布局子視圖。如果你想更深入了解,可以閱讀RCTMapManager和其它的組件的源代碼。


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

總結(jié)

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

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