React Native achieved with a screening function room search list (3)

Original link React Native achieved with a screening function room search list (3)

In the previous two articles already describes how to implement a pull-down refresh and support the LAC upload more Redux list and how to use one-way data flow, and that this will be the last module introduces the development of next screening function, namely React Native native communication with iOS.

Before you begin, or look at the final results achieved

Code Portal --NNHybrid

React Native on how to communicate with the native iOS, there are very clear tutorial official website, I will not elaborate here. I share mainly how to use those interfaces to achieve such an effect.

In the project, the screening strip and the corresponding submenus are using native code implementation, and use the list js code implementation. We have to do is to add these to the original page by page js bridging manner.

Bridging implemented in FHTFilterMenuManagerthis class, which is implemented as follows:

  • Native to achieve
// .h
#import <React/RCTViewManager.h>
#import "FHTFilterMenu.h"

@interface FHTFilterMenu (RNBridge)

@property (nonatomic, copy) RCTBubblingEventBlock onUpdateParameters;
@property (nonatomic, copy) RCTBubblingEventBlock onChangeParameters;

@end

@interface RCTConvert (FHTFilterMenu)

@end

@interface FHTFilterMenuManager : RCTViewManager <RCTBridgeModule>

@end


// .m
#import "FHTFilterMenuManager.h"
#import <React/RCTUIManager.h>
#import <objc/runtime.h>

#import "FilterMenuRentTypeController.h"
#import "FilterMenuGeographicController.h"
#import "FilterMenuOrderByController.h"
#import "FilterMenuMoreController.h"
#import "FilterMenuRentalController.h"

static ConstString kFilterParams = @"filterParams";

typedef NS_ENUM(NSInteger, FilterMenuType) {
    FilterMenuTypeNone,
    FilterMenuTypeEntireRent,    //整租
    FilterMenuTypeSharedRent,    //合租
    FilterMenuTypeApartment,     //独栋公寓
    FilterMenuTypeBelowThousand, //千元房源
    FilterMenuTypePayMonthly,    //月付
    FilterMenuTypeVR,            //VR
};

@implementation RCTConvert (FHTFilterMenu)

RCT_ENUM_CONVERTER(FilterMenuType,
                   (@{@"None": @(FilterMenuTypeNone),
                      @"EntireRent": @(FilterMenuTypeEntireRent),
                      @"SharedRent": @(FilterMenuTypeSharedRent),
                      @"Apartment": @(FilterMenuTypeApartment),
                      @"BelowThousand": @(FilterMenuTypeBelowThousand),
                      @"PayMonthly": @(FilterMenuTypePayMonthly),
                      @"VR":@(FilterMenuTypeVR)}),
                   FilterMenuTypeNone,
                   integerValue);
@end

@implementation FHTFilterMenu (RNBridge)

#pragma mark - Setter & Getter

- (void)setOnUpdateParameters:(RCTBubblingEventBlock)onUpdateParameters {
    objc_setAssociatedObject(self,
                             @selector(onUpdateParameters),
                             onUpdateParameters,
                             OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (RCTBubblingEventBlock)onUpdateParameters {
    return objc_getAssociatedObject(self, @selector(onUpdateParameters));
}

- (void)setOnChangeParameters:(RCTBubblingEventBlock)onChangeParameters {
    objc_setAssociatedObject(self,
                             @selector(onChangeParameters),
                             onChangeParameters,
                             OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (RCTBubblingEventBlock)onChangeParameters {
    return objc_getAssociatedObject(self, @selector(onChangeParameters));
}

- (void)setFilterMenuType:(FilterMenuType)filterMenuType {
    switch (filterMenuType) {
        case FilterMenuTypeEntireRent: {
            UIViewController *vc = (UIViewController *)self.filterControllers[0];
            [vc presetWithOptionTitles:@[@"整租"]];
        }
            break;
        case FilterMenuTypeSharedRent: {
            UIViewController *vc = (UIViewController *)self.filterControllers[0];
            [vc presetWithOptionTitles:@[@"合租"]];
        }
            break;
        case FilterMenuTypeApartment: {
            UIViewController *vc = (UIViewController *)self.filterControllers[3];
            [vc presetWithOptionTitles:@[@"房源类型/独栋公寓"]];
        }
            break;
        case FilterMenuTypeBelowThousand: {
            UIViewController *vc = (UIViewController *)self.filterControllers[2];
            [vc presetWithOptionTitles:@[@"1500以下"]];
        }
            break;
        case FilterMenuTypePayMonthly: {
            UIViewController *vc = (UIViewController *)self.filterControllers[3];
            [vc presetWithOptionTitles:@[@"房源亮点/月付"]];
        }
            break;
        case FilterMenuTypeVR: {
            UIViewController *vc = (UIViewController *)self.filterControllers[3];
            [vc presetWithOptionTitles:@[@"房源亮点/VR"]];
        }
            break;
        default:
            break;
    }
};

@end

@implementation FHTFilterMenuManager

RCT_EXPORT_MODULE();

RCT_EXPORT_VIEW_PROPERTY(onUpdateParameters, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onChangeParameters, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(filterMenuType, FilterMenuType);

RCT_CUSTOM_VIEW_PROPERTY(cityId, NSString, FHTFilterMenu) {
    FilterMenuGeographicController *vc = view.filterControllers[1];
    vc.cityId = (NSString *)json;
};

RCT_CUSTOM_VIEW_PROPERTY(subwayData, NSArray, FHTFilterMenu) {
    FilterMenuGeographicController *vc = view.filterControllers[1];
    vc.originalSubwayData = (NSArray *)json;
};

RCT_EXPORT_METHOD(showFilterMenuOnView:(nonnull NSNumber *)containerTag filterMenuTag:(nonnull NSNumber *)filterMenuTag) {
    RCTUIManager *uiManager = self.bridge.uiManager;
    dispatch_async(uiManager.methodQueue, ^{
        [uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
            UIView *view = viewRegistry[containerTag];
            FHTFilterMenu *filterMenu = (FHTFilterMenu *)viewRegistry[filterMenuTag];
            [filterMenu showFilterMenuOnView:view];
        }];
    });
}

- (dispatch_queue_t)methodQueue {
    return dispatch_get_main_queue();
}

- (UIView *)view {
    FilterMenuRentTypeController *rentTypeVC =
    [[FilterMenuRentTypeController alloc] initWithStyle:UITableViewStylePlain];
    FilterMenuGeographicController *geographicVC = [FilterMenuGeographicController new];
    FilterMenuRentalController *rentalVC =
    [[FilterMenuRentalController alloc] initWithStyle:UITableViewStylePlain];
    FilterMenuMoreController *moreVC = [FilterMenuMoreController new];
    FilterMenuOrderByController *orderByVC =
    [[FilterMenuOrderByController alloc] initWithStyle:UITableViewStylePlain];
    
    CGRect frame = CGRectMake(0, FULL_NAVIGATION_BAR_HEIGHT, SCREEN_WIDTH, 44);
    FHTFilterMenu *filterMenu = [[FHTFilterMenu alloc] initWithFrame:frame];
    filterMenu.filterControllers = @[rentTypeVC, geographicVC, rentalVC, moreVC, orderByVC];
    [filterMenu dismissSubmenu:NO];
    [filterMenu resetFilter];
    
    [rentTypeVC presetWithOptionTitles:@[]];
    __weak FHTFilterMenu *weakFilterMenu = filterMenu;

    rentTypeVC.didSetFilterHandler = ^(NSDictionary * _Nonnull params) {
        BLOCK_EXEC(weakFilterMenu.onUpdateParameters, @{kFilterParams: params});
    };
    
    geographicVC.didSetFilterHandler = ^(NSDictionary * _Nonnull params) {
        NSMutableDictionary *tmpParams = [@{@"regionId": nn_makeSureString(params[@"regionId"]),
                                            @"zoneIds": nn_makeSureArray(params[@"zoneIds"]),
                                            @"subwayRouteId": nn_makeSureString(params[@"subwayRouteId"]),
                                            @"subwayStationCodes": nn_makeSureArray(params[@"subwayStationCodes"])} mutableCopy];
        
        BLOCK_EXEC(weakFilterMenu.onUpdateParameters, @{kFilterParams: [tmpParams copy]});
    };
    

    rentalVC.didSetFilterHandler = ^(NSDictionary * _Nonnull params) {
        NSDictionary *tmpParams = @{@"minPrice": nn_makeSureString(params[@"minPrice"]),
                                    @"maxPrice": nn_makeSureString(params[@"maxPrice"])};
        BLOCK_EXEC(weakFilterMenu.onUpdateParameters, @{kFilterParams: tmpParams});
    };
    
    moreVC.didSetFilterHandler = ^(NSDictionary * _Nonnull params) {
        NSArray *typeArray = params[@"typeArray"];
        NSString *type = typeArray.count == 1 ? (typeArray.lastObject)[@"type"] : @"";
        
        NSDictionary *tmpParams = @{@"roomAttributeTags": params[@"highlightArray"],
                                    @"chamberCounts": params[@"chamberArray"],
                                    @"type": type};
        
        BLOCK_EXEC(weakFilterMenu.onUpdateParameters, @{kFilterParams: tmpParams});
    };
    
    orderByVC.didSetFilterHandler = ^(NSDictionary * _Nonnull params) {
        BLOCK_EXEC(weakFilterMenu.onUpdateParameters, @{kFilterParams: params});
    };
    
    filterMenu.filterDidChangedHandler = ^(FHTFilterMenu * _Nonnull filterMenu, id<FHTFilterController>  _Nonnull filterController) {
        BLOCK_EXEC(weakFilterMenu.onChangeParameters, nil);
    };
    
    return filterMenu;
}

@end
复制代码
  • JS achieve
import React, { Component } from 'react';
import { requireNativeComponent, NativeModules, findNodeHandle } from 'react-native';

const FilterMenu = requireNativeComponent('FHTFilterMenu', SearchFilterMenu);
const filterMenuManager = NativeModules.FHTFilterMenuManager;

export const FilterMenuType = {
    NONE: 'None',
    ENTIRERENT: 'EntireRent',
    SHAREDRENT: 'SharedRent',
    APARTMENT: 'Apartment',
    BELOWTHOUSAND: 'BelowThousand',
    PAYMONTHLY: 'PayMonthly',
    VR: 'VR'
}

export default class SearchFilterMenu extends Component {

    componentDidUpdate() {
        const filterMenuTag = findNodeHandle(this.refs.filterMenu);
        const containerTag = findNodeHandle(this.props.containerRef);
        if (filterMenuTag && containerTag) filterMenuManager.showFilterMenuOnView(containerTag, filterMenuTag);
    }
    
    render() {
        return <FilterMenu ref='filterMenu' {...this.props} />;
    }
}
复制代码
  • JS call
<SearchFilterMenu
    style={styles.filterMenu}
    cityId={`${home.cityId}`}
    subwayData={home.subwayData}
    containerRef={this.refs.container}
    filterMenuType={this.params.filterMenuType}
    onChangeParameters={() => this._loadData(true)}
    onUpdateParameters={({ nativeEvent: { filterParams } }) => {
        this.filterParams = {
            ...this.filterParams,
            ...filterParams,
        };
    }}
/>
复制代码

Creating ViewManager

FHTFilterMenuManagerIs inherited from RCTViewManagereach of the native UI needs to be a RCTViewManagersub-class to create and manage. The program is running, RCTViewManagerwill create a native UI and to provide to the view RCTUIManager, RCTUIManagerin turn, entrusted RCTViewManagerproperty when needed to set up and update the view. There is a point of note: ViewManager naming format is a native component Manager name + .

The most important is the ViewManager must be implemented - (UIView *)view, used to return the native UI that you want to bridge.

React Native transfer properties from the native component to

We know that property is the most simple cross-component communication, if the RN component receives a property, they can pass RCT_EXPORT_VIEW_PROPERTYand RCT_CUSTOM_VIEW_PROPERTYpassed to the native component of the way.

RCT_EXPORT_VIEW_PROPERTYJS may be exposed to the original green component carrying properties. So FHTFilterMenuManager, I own three properties exposed to JS, respectively,

// 点击确定按钮执行网络请求的block
RCT_EXPORT_VIEW_PROPERTY(onUpdateParameters, RCTBubblingEventBlock);
// 点击子菜单item,参数变更的block
RCT_EXPORT_VIEW_PROPERTY(onChangeParameters, RCTBubblingEventBlock);
// 筛选菜单的类型,
RCT_EXPORT_VIEW_PROPERTY(filterMenuType, FilterMenuType);
复制代码

Here I used the way the classification of the properties of native components to expand, because in the project, I do not want to have too many native components RN bridge-related code, so the use of classified expand, so the code can also be decoupled.

There is a point Note: If you own property is a block type, then the property name must start with ON .

RCT_CUSTOM_VIEW_PROPERTYIt allows us to add some more complex properties. Because cityIdand subwayDatais FilterMenuGeographicControllerthis submenu only required if they are set to FHTFilterMenuthe property is not appropriate, but FilterMenuGeographicControllerwe do not exposed to JS, so we can use RCT_CUSTOM_VIEW_PROPERTYto not be exposed to the object JS property transfer.

RCT_CUSTOM_VIEW_PROPERTY(cityId, NSString, FHTFilterMenu) {
    FilterMenuGeographicController *vc = view.filterControllers[1];
    vc.cityId = (NSString *)json;
};

RCT_CUSTOM_VIEW_PROPERTY(subwayData, NSArray, FHTFilterMenu) {
    FilterMenuGeographicController *vc = view.filterControllers[1];
    vc.originalSubwayData = (NSArray *)json;
};
复制代码

React Native native component method call

RCT_EXPORT_METHODTo provide native method calls to the JS. RCT_EXPORT_METHOD(showFilterMenuOnView:(nonnull NSNumber *)containerTag filterMenuTag:(nonnull NSNumber *)filterMenuTag)Used to implement added to a submenu SearchHousePage, which achieve the following:

RCT_EXPORT_METHOD(showFilterMenuOnView:(nonnull NSNumber *)containerTag filterMenuTag:(nonnull NSNumber *)filterMenuTag) {
    RCTUIManager *uiManager = self.bridge.uiManager;
    dispatch_async(uiManager.methodQueue, ^{
        [uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
            UIView *view = viewRegistry[containerTag];
            FHTFilterMenu *filterMenu = (FHTFilterMenu *)viewRegistry[filterMenuTag];
            [filterMenu showFilterMenuOnView:view];
        }];
    });
}
复制代码

Wherein containerTagthe code used to represent SearchHousePage, filterMenuTagsaid filter criteria. Note that there is a point: You can not use self.view way because it will create a new FHTFilterMenu objects, but can not - (UIView *)viewuse for reference refers to a creation out of the View, then if your components are used in multiple pages using , it will go wrong .

Further, since the iOS UI operation to put the main thread is done, it is preferable methodQueueto specify the main thread.

end

Here the whole SearchHousePagedevelopment of the page has been completed, if you need to see the complete code words in the code portal --NNHybrid in.

参考: Native Modules Native UI Components

Guess you like

Origin blog.csdn.net/weixin_34365417/article/details/91371275