美文网首页iOS控件
uni-app与iOS原生App的数据交互

uni-app与iOS原生App的数据交互

作者: Joymerry | 来源:发表于2022-12-01 09:35 被阅读0次

    uni-app开发过程中,难免需要原生代码去处理业务逻辑,如何与原生进行数据交互就变得尤为重要。怎么传递数据,怎么接收数据,我们才能更好处理这些逻辑?下面我们就介绍一些uni-app与iOS原生的一些数据处理方式。

    一、使用uni原生插件

    这种方式是uniapp比较推荐的一种方式。但是实现起来比较依赖原生知识。不过这一部分交给原生端开发就可以了。

    插件类型:

    Module模式:不需要参与页面布局,只需要通过 API 调用原生功能,比如:获取当前定位信息、数据请求等功能,通过扩展module的方式来实现;
    Component模式:需要参与页面布局,比如:map、image等需要显示UI的功能,通过扩展component即组件的方法来实现;

    vue页面中仅支持使用Module类型的原生插件,不支持调用同步方法返回数据
    nvue页面中支持使用Module和Component两种类型的原生插件。也就是如需实现嵌入页面的ui组件,前提是该页面需要使用nvue编写。

    1.Module模式:

    通过宏 UNI_EXPORT_METHOD 将异步方法暴露给 js 端,只有通过UNI_EXPORT_METHOD暴露的原生方法才能被 js 端识别到。
    通过宏 UNI_EXPORT_METHOD_SYNC 将同步方法暴露给 js 端。

    module 支持在 vue 和 nvue 中调用,添加如下代码

    <template>
        <div>
            <button type="primary" @click="testAsyncFunc">testAsyncFunc</button>
            <button type="primary" @click="testSyncFunc">testSyncFunc</button>
        </div>
    </template>
    
    <script>
        // 首先需要通过 uni.requireNativePlugin("ModuleName") 获取 module 
        var testModule = uni.requireNativePlugin("DCTestUniPlugin-TestModule")
        export default {
            methods: {
                testAsyncFunc() {
                    // 调用异步方法
                    testModule.testAsyncFunc({
                            'name': 'uni-app',
                            'age': 1
                        },
                        (ret) => {
                            uni.showToast({
                                title:'调用异步方法 ' + ret,
                                icon: "none"
                            })
                        })
                },
                testSyncFunc() {
                    // 调用同步方法
                    var ret = testModule.testSyncFunc({
                        'name': 'uni-app',
                        'age': 1
                    })
    
                    uni.showToast({
                        title:'调用同步方法 ' + ret,
                        icon: "none"
                    })
                }
            }
        }
    </script>
    

    2.component模式:

    复写 DCUniComponent 中的生命周期方法

    (1)liadView方法

    一个组件默认对应一个原生 view,如果未复写loadView方法提供自定义view,会默认调用基类方法返回一个继承于 UIView 的实例。

    (2)viewDidLoad

    如果需要对组件view做一些配置,比如设置delegate,在 viewDidLoad 生命周期方法中是一个比较好的时机

    在 uni-app 中使用组件

    注意:扩展的 component 只能在 nvue 文件中使用,不需要引入即可直接使用

    在uni-app项目中新建nvue文件

    <template>
        <view>
            <dc-testmap style="width:750rpx;height:300px"></dc-testmap>
        </view>
    </template>
    

    自定义事件

    <template>
        <div>
            <dc-testmap style="width:750rpx;height:300px" @mapLoaded="onMapLoaded"></dc-testmap>
        </div>
    </template>
    
    <script>
    export default {
        methods: {
            onMapLoaded:function(e) {
                // 原生端传递的数据保存在 e.detail 中
                console.log("map loaded: "+JSON.stringify(e.detail))
            }
        }
    }
    </script>
    

    在前端注册的mapLoaded方法,会在原生端调用addEvent:方法

    如果需要原生端向前端发送事件,原生端在合适的时机调用fireEvent:params: domChanges:方法

    自定义属性

    给组建添加一个新的属性showTraffic

    <template>
        <div>
            <dc-testmap style="width:750rpx;height:300px" showTraffic="true"></dc-testmap>
        </div>
    </template>
    

    对应原生端的实现,覆盖组件方法 onCreateComponentWithRef... 给组件添加一个成员变量记录 showTraffic 属性的值,并在 viewDidLoad 方法中初始化

    -(void)onCreateComponentWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events uniInstance:(DCUniSDKInstance *)uniInstance
    {
        if (attributes[@"showsTraffic"]) {
            _showsTraffic = [DCUniConvert BOOL: attributes[@"showsTraffic"]];
        }
    }
    

    onCreateComponentWithRef的方法是比viewDidLoad先进入的,所以可以先处理数据,然后在初始化方法viewDidLoad中进行一些操作

    当前端更新属性时,会触发updateAttributes:方法,同步给地图控件

    /// 前端更新属性回调方法
    /// @param attributes 更新的属性
    - (void)updateAttributes:(NSDictionary *)attributes {
        // 解析属性
        if (attributes[@"showsTraffic"]) {
            _showsTraffic = [DCUniConvert BOOL: attributes[@"showsTraffic"]];
            ((MKMapView*)self.view).showsTraffic = _showsTraffic;
        }
    }
    

    给组件添加方法

    原生端实现

    在组件代码中使用宏 UNI_EXPORT_METHOD 暴露原生方法供前端调用

    @implementation TestMapComponent
    
    // 通过 UNI_EXPORT_METHOD 将方法暴露给前端
    UNI_EXPORT_METHOD(@selector(focus:))
    
    // options 为前端传递的参数,支持 NSDictionary 或 NSString 类型
    - (void)focus:(NSDictionary *)options {
        NSLog(@"%@",options);
    }
    @end
    

    在 uni-app 中调用 focus: 方法

    <template>
      <dc-testmap ref='mycomponent'></dc-testmap>
    </template>
    <script>
      module.exports = {
        created: function() {
          // 通过 this.$refs.mycomponent 获取地图组件
          // 调用组件 focus 方法
          this.$refs.mycomponent.focus({'value':'Hello'});
        }
      }
    </script>
    

    globalEvent 事件

    在module 和 component中 用于页面监听持久性事件,例如定位信息,陀螺仪等的变化。

    globalEvent事件只能通过页面的DCUniSDKInstance实例给当前页面发送globalEvent事件。其他页面无法接受。

    示例:

    页面监听event事件

    var globalEvent = uni.requireNativePlugin('globalEvent');
    globalEvent.addEventListener('myEvent', function(e) {
      console.log('myEvent'+JSON.stringify(e));
    });
    

    在原生代码 发出myEvent事件

    NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:@"value",@"key",nil];
    NSString * eventName = @"myEvent";
    DCUniSDKInstance * instance = self.uniInstance;
    [instance fireGlobalEvent:eventName params:params];
    

    二、HTML5 plus api中的Native.js方法

    这种方式的话,使用起来非常简单,我们只需要在app原生端创建一个类,使用这个类接手uni-app的调用方法即可。
    在uni-app端根据native 的api去创建对应的原生类,然后使用原生类去调用原生方法,进行传参就能进行简单的数据交互。

    uni-app端的代码实现:

    let newVCobj = plus.ios.newObject('MessageHand');
    plus.ios.invoke(newVCobj, 'jump:', userInfo);
    

    原生端的代码实现

    -(void)jump:(NSDictionary *)userInfo {
        dispatch_async(dispatch_get_main_queue(), ^{
            HomeViewController * homeVC = [[HomeViewController alloc]init];
            homeVC.userInfo = userInfo;
            AppDelegate * delegate=(AppDelegate*)[[UIApplication sharedApplication] delegate];
            delegate.rootViewController.navigationBarHidden = NO;
            [delegate.rootViewController pushViewController:homeVC animated:YES];
        });
    }
    

    native.js如何获取原生的数据呢?这个问题就稍微复杂,从H5+ api中没有找到文档讲解,在网上查也没有查到好的办法,但是可以通过两种方式。

    第一,uni-app端主动获取。我们可以在原生定一个getInfo的方法,让uni-app去主动获取。

    let newVCobj = plus.ios.newObject('MessageHand');
    plus.ios.invoke(newVCobj, 'getInfo');
    

    原生端的代码实现

    -(NSString*)getInfo {
        return @“”;
    }
    

    第二种方式的话,通过回调方法去拿到数据。

    let messageHand = plus.ios.newObject('MessageHand');
    let delegate = plus.ios.invoke(messageHand,'getInfoCallBack:', (e)=>{
    // 可以拿到事件,但是拿不到e数据值
    });
    

    原生端的代码实现

    -(NSString*) getInfoCallBack:(void(^)(NSString * _Nonnull timer))handleBlock{
        handleBlock(@"nihao");
        return @"wohenhao";
    }
    

    但是第二种方式有一个问题就是,如果在回调方法中我是拿不到预期的结果值,那又如何传递给uni-app端数据。

    相关文章

      网友评论

        本文标题:uni-app与iOS原生App的数据交互

        本文链接:https://www.haomeiwen.com/subject/mbncfdtx.html