简介

本文档主要介绍如何在 iOS 平台开发 uni-app 原生插件,在您阅读此文档时,您需要具备 iOS 应用开发经验,对 HTML、JavaScript、CSS 等前端开发有一定的了解,并且熟悉在JavaScript 和 Objective-C 环境下的 JSON 格式数据操作等。

什么是uni原生插件

uni原生插件指的是将您原生开发的功能按照规范封装成插件包,然后即可在 uni-app 前端项目中通过js调用您开发的原生能力。

开发环境

  • iOS开发环境,Xcode 11.0+
  • 下载开发插件需要的 SDK包 并解压
  • 安装 uni-app 开发工具 HBuilderX (请与 SDK 包的版本保持一致)

SDK 包结构说明

  1. |--iOSSDK
  2. |-- 5+app-uniplugin-demo // 在 uni-app 中按照 5+ App 规范开发插件工程
  3. |-- HBuilder-Hello // uni-app 或 5+ App 离线打包工程
  4. |-- HBuilder-Integrate // 5+ App 插件开发工程
  5. |-- HBuilder-uniPluginDemo // uni-app 插件开发主工程 (本文档需要使用的工程)
  6. |-- SDK // 依赖库及依赖资源文件
  7. |-- Feature-iOS.xls // 功能模块与依赖库对应关系说明表格
  8. |-- readme.txt // 目录说明

SDK 目录中的 HBuilder-uniPluginDemouni原生插件开发主工程,该工程已经将各项配置都配置齐全,开发uni原生插件需要依赖此工程,本文档的插件示例工程DCTestUniPlugin也在目录中,另外插件市场的 原生增强提示框插件 对应的原生插件工程libWeexDCRichAlert也放到了此目录中提供给开发者作为参考,其他工程及文件,开发uni原生插件不需要关心,如果想了解更多可以参考 App离线打包 相关文档

创建插件工程

打开 Xcode,创建一个新的工程,template 选择 FrameworkStatic Library(示例工程选择的是 Framework),然后点击 Next

iOS 插件开发教程 - 图1

在 Product Name 中输入插件工程名称(建议使用一个性化的前缀,避免与其他人的插件包名冲突),其他项不需要修改保持工程默认填充的即可,然后点击Next

iOS 插件开发教程 - 图2

然后选择工程存放路径,建议直接存放在 iOSSDK目录中的 HBuilder-uniPluginDemo 插件开发主工程目录下,如下图所示,然后点击 Create

强烈建议将插件工程存放在 HBuilder-uniPluginDemo 插件开发主工程目录中,因为插件工程需要根据插件开发主工程相对路径引用一些文件,之后您升级SDK的时候只需要将您的插件工程 Copy 到新的 SDK 的相同位置下即可

iOS 插件开发教程 - 图3

可以删除工程自动创建的 .h 文件,这个文件用不到

iOS 插件开发教程 - 图4

然后选中工程名,在TARGETS->Build Settings中,将 Mach-O Type 设置为 Static Library 如下图所示

iOS 插件开发教程 - 图5

然后将插件工程关闭,接下来需要将插件工程导入到插件开发主工程中

导入插件工程

打开 iOSSDK/HBuilder-uniPluginDemo工程目录,双击目录中的HBuilder-uniPlugin.xcodeproj 文件运行插件开发主工程

iOS 插件开发教程 - 图6

在 Xcode 项目左侧目录选中主工程名,然后点击右键选择Add Files to “HBuilder-uniPlugin” ...

iOS 插件开发教程 - 图7

然后选择您刚刚创建的插件工程路径中,选中插件工程文件,勾选 Create folder referencesAdd to targets 两项,然后点击Add

iOS 插件开发教程 - 图8

这时在 Xcode 左侧目录中可以看到插件工程已经添加到了主工程中,如下图所示

iOS 插件开发教程 - 图9

工程配置

然后在 Xcode 项目左侧目录选中主工程名,在TARGETS->Build Phases->Dependencies中点击+

iOS 插件开发教程 - 图10

在弹窗中选中插件工程,如图所示,然后点击Add,将插件工程添加到Dependencies

iOS 插件开发教程 - 图11

然后在Link Binary With Libraries中点击+,同样在弹窗中选中插件工程,点击Add

iOS 插件开发教程 - 图12

此时可以看到 DependenciesLink Binary With Libraries 都添加了插件工程,如下图所示

iOS 插件开发教程 - 图13

接下来需要在插件工程的Header Search Paths中添加开发插件所需的头文件引用,头文件存放在主工程的HBuilder-Hello/inc中,添加方法如下图所示,在 Xcode 项目左侧目录选中插件工程名,找到TARGETS->Build Settings->Header Search Paths双击右侧区域打开添加窗口,然后将inc目录拖入会自动填充相对路径,然后将模式改成recursive

iOS 插件开发教程 - 图14

代码实现

插件扩展方式

原生插件是基于 WeexSDK 规范来实现,扩展原生功能有两种方式:

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

您需要根据实际的情况选择扩展方式,当然插件中可以同时存在 module 和 component,也可以是多个 module 和 多个 component;

扩展 module

TestModule为例,源码请查看 iOSSDK/HBuilder-uniPluginDemo/DCTestUniPlugin 插件工程;

新建TestModule类,继承 NSObject,引入 WeexSDK.h 头文件,让该类遵循 WXModuleProtocol 的协议。

TestModule.h 文件

  1. #import <Foundation/Foundation.h>
  2. // 引入 WeexSDK.h 头文件
  3. #import "WeexSDK.h"
  4. @interface TestModule : NSObject <WXModuleProtocol>
  5. @end

TestModule.h 文件截图:

iOS 插件开发教程 - 图15

然后在 TestModule.m 文件中添加实现方法

异步方法实现

  1. /// 异步方法(注:异步方法会在主线程(UI线程)执行)
  2. /// @param options js 端调用方法时传递的参数
  3. /// @param callback 回调方法,回传参数给 js 端
  4. - (void)testAsyncFunc:(NSDictionary *)options callback:(WXModuleKeepAliveCallback)callback {
  5. // options 为 js 端调用此方法时传递的参数
  6. NSLog(@"%@",options);
  7. // 可以在该方法中实现原生能力,然后通过 callback 回调到 js
  8. // 回调方法,传递参数给 js 端 注:只支持返回 String 或 NSDictionary (map) 类型
  9. if (callback) {
  10. // 第一个参数为回传给js端的数据,第二个参数为标识,表示该回调方法是否支持多次调用,如果原生端需要多次回调js端则第二个参数传 YES;
  11. callback(@"success",NO);
  12. }
  13. }

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

  1. // 通过宏 WX_EXPORT_METHOD 将异步方法暴露给 js 端
  2. WX_EXPORT_METHOD(@selector(testAsyncFunc:callback:))

同步方法实现

  1. /// 同步方法(注:同步方法会在 js 线程执行)
  2. /// @param options js 端调用方法时传递的参数
  3. - (NSString *)testSyncFunc:(NSDictionary *)options {
  4. // options 为 js 端调用此方法时传递的参数
  5. NSLog(@"%@",options);
  6. /*
  7. 可以在该方法中实现原生功能,然后直接通过 return 返回参数给 js
  8. */
  9. // 同步返回参数给 js 端 注:只支持返回 String 或 NSDictionary (map) 类型
  10. return @"success";
  11. }

通过宏 WX_EXPORT_METHOD_SYNC 将同步方法暴露给 js 端

  1. // 通过宏 WX_EXPORT_METHOD_SYNC 将同步方法暴露给 js 端
  2. WX_EXPORT_METHOD_SYNC(@selector(testSyncFunc:))

TestModule.m 文件截图:

iOS 插件开发教程 - 图16

配置插件信息

选中工程中的HBuilder-uniPlugin-Info.plist文件右键->Open As->Source Code找到dcloud_uniplugins节点,copy下面的内容添加到dcloud_uniplugins节点下,按您插件的实际信息填写对应的项

  1. <dict>
  2. <key>hooksClass</key>
  3. <string>填写 hooksClass 类名 </string>
  4. <key>plugins</key>
  5. <array>
  6. <dict>
  7. <key>class</key>
  8. <string>填写 module 或 component 的类名</string>
  9. <key>name</key>
  10. <string>填写暴露给js端对应的 module 或 component 名称</string>
  11. <key>type</key>
  12. <string>填写 module 或 component</string>
  13. </dict>
  14. </array>
  15. </dict>

配置说明

  • hooksClass:App系统方法钩子类,值是类名,是给有些插件需要在 app 启动时做初始化或者获取系统事件用的,如果没有可以不填为空(示例中不需要所以置空,后面章节会详细说明 hooksClass 的使用)
  • class:module 或 component 对应的原生类名(示例中为 TestModule
  • name:暴露给js端使用的 module 或 component 对应的名称(示例为DC-TestModule一定要使用一个个性化的前缀,避免与其他插件冲突
  • type:module 或 component (示例为module

配置完如下图所示(必须严格按照格式配置)

iOS 插件开发教程 - 图17

到此,我们已经完成了一个简单的 module 扩展,接下来讲解如何在 uni-app 项目中调用刚刚扩展的 module 方法

在 uni-app 项目中调用 module 方法

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

  1. <template>
  2. <div>
  3. <button type="primary" @click="testAsyncFunc">testAsyncFunc</button>
  4. <button type="primary" @click="testSyncFunc">testSyncFunc</button>
  5. </div>
  6. </template>
  7. <script>
  8. // 首先需要通过 uni.requireNativePlugin("ModuleName") 获取 module
  9. var testModule = uni.requireNativePlugin("DC-TestModule")
  10. export default {
  11. methods: {
  12. testAsyncFunc() {
  13. // 调用异步方法
  14. testModule.testAsyncFunc({
  15. 'name': 'uni-app',
  16. 'age': 1
  17. },
  18. (ret) => {
  19. uni.showToast({
  20. title:'调用异步方法 ' + ret,
  21. icon: "none"
  22. })
  23. })
  24. },
  25. testSyncFunc() {
  26. // 调用同步方法
  27. var ret = testModule.testSyncFunc({
  28. 'name': 'uni-app',
  29. 'age': 1
  30. })
  31. uni.showToast({
  32. title:'调用同步方法 ' + ret,
  33. icon: "none"
  34. })
  35. }
  36. }
  37. }
  38. </script>

然后我们要生成 uni-app 项目的本地打包资源,导入到插件开发工程中,测试一下功能是否正常

导入 uni-app 资源

生成 uni-app 本地打包资源

首先需要生成本地打包资源,在 HBuilderX 中选您的 uni-app 工程,右键->发现->原生App-本地打包->生成本地打包App资源

iOS 插件开发教程 - 图18

项目编译完成后会在 HBuilderX 控制台输出资源存路径,点击路径会自动打开资源所在文件夹

iOS 插件开发教程 - 图19

如下图所示,__UNI_7F5F813文件夹即为应用资源包(__UNI_7F5F813 为 uni-app 的 id)

iOS 插件开发教程 - 图20

导入 uni-app 资源

接下来,将应用资源导入到插件开发主工程的HBuilder-Hello/Pandora/apps/中,如下图所示,直接拖进去即可

iOS 插件开发教程 - 图21

然后打开工程的 control.xml 文件,将 appid 改成 uni-app 的 id,如下图所示

iOS 插件开发教程 - 图22

然后运行项目测试,如下图所示(能调到 module 的方法,并且可以获取 module 返回的数据,则说明功能正常)

iOS 插件开发教程 - 图23

再次导入资源时注意事项

:前端代码修改后重新导入资源时,需要在插件开发工程中删除之前导入的资源,同时将模拟器或真机上的 App 删除,在按照上面的教程操作,避免因为缓存问题导致加载的还是旧的资源;

接下来,我们学习一下如何扩展 Component

扩展组件 component

TestComponent为例,源码请查看 iOSSDK/HBuilder-uniPluginDemo/DCTestUniPlugin 插件工程;

新建TestComponent类,继承WXComponent类(如果这个类里什么代码也不写,它和默认的的 <view> 组件能力是一致的)

  1. #import "WXComponent.h"
  2. @interface TestComponent : WXComponent
  3. @end

复写 WXComponent 中的生命周期方法

- loadView 方法

一个组件默认对应一个原生 view,如果未复写loadView方法提供自定义view,会默认调用基类方法返回一个继承于 UIView 的实例。比如我们要实现一个组件支持地图功能,我们可以返回系统的 MKMapView

注:不需要为view设置frameview的大小及位置由前端css决定

  1. - (UIView *)loadView {
  2. return [MKMapView new];
  3. }

- viewDidLoad

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

注:可以直接通过 self.view 获取 view 实例

  1. - (void)viewDidLoad {
  2. ((MKMapView*)self.view).delegate = self;
  3. }

至此,已经完成了一个简单 component 的实现

然后我们需要在info.plist 中添加 component 的配置信息,和 module 配置格式一样具体请参考 配置插件信息

  • class:填写TestComponent
  • name:填写dc-testmap (在页面中使用时的组件名称,添加一个个性化的前缀,避免与其他组件冲突)
  • type:填写component

配置完后如下图所示

iOS 插件开发教程 - 图24

接下来可以在 uni-app 中使用组件

在 uni-app 中使用组件

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

在 uni-app 项目中新建 nvue 文件,加入下面的代码

  1. <template>
  2. <view>
  3. <dc-testmap style="width:750rpx;height:300px"></dc-testmap>
  4. </view>
  5. </template>

然后生成本地打包资源,导入到插件开发工程中进行测试,具体操作参考文档上方的教程 导入 uni-app 资源

然后运行测试,效果如下图:

iOS 插件开发教程 - 图25

刚刚做的组件只实现了 UI 显示,下面讲解组件的交互方式等一些高阶用法

自定义事件

对于每个组件默认提供了一些事件能力,如点击等。假如想给我们的地图组件提供 mapLoaded 事件。

uni-app 中是通过 @事件名="方法名" 添加事件,如下方代码所示在nvue中,给地图组件添加mapLoaded 事件

  1. <template>
  2. <div>
  3. <dc-testmap style="width:750rpx;height:300px" @mapLoaded="onMapLoaded"></dc-testmap>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. methods: {
  9. onMapLoaded:function(e) {
  10. console.log("map loaded"+JSON.stringify(e))
  11. }
  12. }
  13. }
  14. </script>
对应的原生端实现

我们需要添加一个 BOOL 类型成员变量 mapLoadedEvent 用来记录该事件是否生效,如下方代码所示:

  1. /// 前端注册的事件会调用此方法
  2. /// @param eventName 事件名称
  3. - (void)addEvent:(NSString *)eventName {
  4. if ([eventName isEqualToString:@"mapLoaded"]) {
  5. _mapLoadedEvent = YES;
  6. }
  7. }
  8. /// 对应的移除事件回调方法
  9. /// @param eventName 事件名称
  10. - (void)removeEvent:(NSString *)eventName {
  11. if ([eventName isEqualToString:@"mapLoaded"]) {
  12. _mapLoadedEvent = NO;
  13. }
  14. }
原生端向前端发送事件

在地图加载完毕的方法中触发 mapLoaded 事件

  1. - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
  2. if (_mapLoadedEvent) {
  3. // 向前端发送事件 (params:为传给前端的数据 支持 NSDictionary 或 NSString 类型)
  4. [self fireEvent:@"mapLoaded" params:@{@"customKey":@"customValue"} domChanges:nil];
  5. }
  6. }

自定义属性

给我们的地图组件添加一个新的属性showTraffic,在前端代码里可以控制组件是否显示路况信息

  1. <template>
  2. <div>
  3. <dc-testmap style="width:750rpx;height:300px" showTraffic="true"></dc-testmap>
  4. </div>
  5. </template>
对应的原生端实现

覆盖组件初始化方法 initWithRef... 给组件添加一个成员变量记录 showTraffic 属性的值,并在 init 方法中初始化

  1. - (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance {
  2. if(self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]) {
  3. if (attributes[@"showsTraffic"]) {
  4. _showsTraffic = [WXConvert BOOL: attributes[@"showsTraffic"]];
  5. }
  6. }
  7. return self;
  8. }

在生命周期方法中记得将属性值同步给地图控件

  1. - (void)viewDidLoad {
  2. ((MKMapView*)self.view).showsTraffic = _showsTraffic;
  3. }

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

  1. /// 前端更新属性回调方法
  2. /// @param attributes 更新的属性
  3. - (void)updateAttributes:(NSDictionary *)attributes {
  4. // 解析属性
  5. if (attributes[@"showsTraffic"]) {
  6. _showsTraffic = [WXConvert BOOL: attributes[@"showsTraffic"]];
  7. ((MKMapView*)self.view).showsTraffic = _showsTraffic;
  8. }
  9. }

更多的组件生命期方法

组件是由框架管理的,比如创建、布局、渲染、销毁。组件的生命周期方法都是可以重写的,你可以在这些生命周期中去做自己的事情。

方法描述
initWithRef:type:…用给定的属性初始化一个component.
layoutDidFinish在component完成布局时候会调用.
loadView创建component管理的view.
viewWillLoad在component的view加载之前会调用.
viewDidLoad在component的view加载完之后调用.
viewWillUnload在component的view被释放之前调用.
viewDidUnload在component的view被释放之后调用.
updateStyles:在component的style更新时候调用.
updateAttributes:在component的attribute更新时候调用.
addEvent:给component添加event的时候调用.
removeEvent:在event移除的时候调用.

给组件添加方法

原生端实现

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

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

在 uni-app 中调用 focus: 方法

  1. <template>
  2. <dc-testmap ref='mycomponent'></dc-testmap>
  3. </template>
  4. <script>
  5. module.exports = {
  6. created: function() {
  7. // 通过 this.$refs.mycomponent 获取地图组件
  8. // 调用组件 focus 方法
  9. this.$refs.mycomponent.focus({'value':'Hello'});
  10. }
  11. }
  12. </script>

uni-app 完整项目存放在 iOSSDK/HBuilder-uniPluginDemo/UniPluginDemo

插件开发完毕并通过测试后,接下来就可以生成插件包了

生成插件包

此步骤应该在您插件所有功能都开发完毕,并在开发工程中测试完成进行

插件包结构说明

  1. |-- 插件id // 插件包是一个以插件id命名的文件夹
  2. |-- android // 存放 android 插件所需要的依赖库及资源文件
  3. |-- ios // 存放 ios 插件所需要的依赖库及资源文件
  4. |-- package.json // 插件配置文件

插件包的目录结构是固定的,是一个以插件id命名的文件夹,其中 android 路径中放的是 android 端插件所需要的依赖库及资源文件,ios 路径中放的是 ios 端插件所需要的依赖库及资源文件,package.json为插件的配置文件,接下来,我们需要生成 iOS 插件所需的依赖库,和编写 package.json 文件

编译生成插件库文件(.framework 或 .a)

如下图所示,将编译工程选择为插件项目(DCTestUniPlugin),运行设备选择Generic iOS Device

iOS 插件开发教程 - 图26

然后点击Edit Scheme...

iOS 插件开发教程 - 图27

在弹窗中,将Run->Info->Build Configuration切换到Release,然后点击Close关闭弹窗

iOS 插件开发教程 - 图28

然后在 Xcode 左侧目录中选中插件工程名,查看TARGETS->Build Settings->Architectures,确保

  • Build Active Architecture Only->ReleaseNo
  • Valid Architectures 中至少包含 arm64armv7(一般保持工程默认配置即可)

iOS 插件开发教程 - 图29

iOS Deployment Target中选中最低支持的 iOS 版本,建议选择 iOS9.0

iOS 插件开发教程 - 图30

然后点击运行按钮Command + B 编译运行工程

编译完成后,在插件工程 Products 下生成的库(DCTestUniPlugin.framework)即为插件所需要的依赖库文件,右键->Show in Finder,可打开库所在文件夹

iOS 插件开发教程 - 图31

编写 package.json 配置文件

package.json 为插件的配置文件,配置了插件id、格式、插件资源以及插件所需权限等等信息

新建一个 package.json 文件,然后请参考 uni原生插件包格式 说明,根据您插件实际情况填写插件配置信息,示例插件配置完后如下图所示

iOS 插件开发教程 - 图32

然后以插件id为名新建一个文件夹,将编辑好的 package.json 放进去,然后在文件夹中在新建一个 ios 文件夹,将刚刚生成的依赖库(DCTestUniPlugin.framework)copy 到 ios 根目录,这样我们的插件包就构建完成了,如下图所示

iOS 插件开发教程 - 图33

注:iOS 插件包至少需要包含:package.json文件 和ios文件夹(小写的ios)里面包含 .a 或 .framework 依赖库;

使用插件

放到 HBuilderX 的 uni-app 项目中直接使用

插件包开发完后,可以直接放到 HX 的 uni-app 项目中使用,具体请参考文档 HBuilderX 中使用本地插件

提交插件市场

按照上面的方式放到 HBuilderX 的 uni-app 项目中提交打包测试完插件后,可以将插件包压缩成 zip 格式,然后提交到插件市场共享给其他开发者使用,登录注册DCloud插件市场,然后按照提示步骤提交插件,(需要编写对应插件的使用说明文档,.md(markdown)格式);

常见问题

广告插件说明

由于官方 UniAD 广告组件集成了“广点通”和“穿山甲”SDK,目前不支持自行开发包含这两个SDK的原生插件,云打包会导致冲突;

插件开发需要依赖第三方SDK,或需要依赖资源文件

  • 如果您的插件需要依赖第三方的SDK,开发阶段将第三方SDK引入到开发工程中进行正常开发,然后在构建插件包的时候,需要将依赖的三方SDK库文件放到ios路径下,然后编辑 package.json
  • 如果需要依赖资源文件比如图片等,建议把资源文件放到 .bundle 包中使用,然后把 bundle 包放到 ios 路径下,然后配置 package.json

具体请参考开源项目 百度OCR识别插件源码,对应插件市场的 插件

获取 UIViewController

因为 uni 框架机制,module 的 weexInstance 没有绑定 viewController,故 weexInstance.viewController 值为 nil,如果想通过 UIViewController 来跳转页面可使用下面的方法获取 UIViewController**

  1. // 获取当前显示的 UIViewController
  2. + (UIViewController *)dc_findCurrentShowingViewController {
  3. //获得当前活动窗口的根视图
  4. UIViewController *vc = [UIApplication sharedApplication].keyWindow.rootViewController;
  5. UIViewController *currentShowingVC = [self findCurrentShowingViewControllerFrom:vc];
  6. return currentShowingVC;
  7. }
  8. + (UIViewController *)findCurrentShowingViewControllerFrom:(UIViewController *)vc
  9. {
  10. // 递归方法 Recursive method
  11. UIViewController *currentShowingVC;
  12. if ([vc presentedViewController]) {
  13. // 当前视图是被presented出来的
  14. UIViewController *nextRootVC = [vc presentedViewController];
  15. currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];
  16. } else if ([vc isKindOfClass:[UITabBarController class]]) {
  17. // 根视图为UITabBarController
  18. UIViewController *nextRootVC = [(UITabBarController *)vc selectedViewController];
  19. currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];
  20. } else if ([vc isKindOfClass:[UINavigationController class]]){
  21. // 根视图为UINavigationController
  22. UIViewController *nextRootVC = [(UINavigationController *)vc visibleViewController];
  23. currentShowingVC = [self findCurrentShowingViewControllerFrom:nextRootVC];
  24. } else {
  25. // 根视图为非导航类
  26. currentShowingVC = vc;
  27. }
  28. return currentShowingVC;
  29. }

获取页面可访问图片资源存储路径 (vue页面需要注意,nvue页面不存这个问题)

有些插件可能需要返回本地的图片路径给页面来显示,如果页面是vue的话是使用 WKWebview 渲染,访问本地图片资源可能会存在跨域问题访问不到图片资源,所以需要将图片存放到指定路径下,按照下面的方法获取图片存储路径即可

原生端实现

  1. // 引用头文件
  2. #import "PDRCoreApp.h"
  3. #import "PDRCoreAppManager.h"
  4. #import "PDRCoreAppInfo.h"
  5. // 获取路径信息
  6. PDRCoreAppInfo *appinfo = [PDRCore Instance].appManager.getMainAppInfo;
  7. // 将图片存储到 appinfo.documentPath 路径下即可,可以创建子目录;
  8. // 示例,原生图片存储路径为
  9. NSString *imgPath = [appinfo.documentPath stringByAppendingPathComponent:@"test.png"];

js 端获取图片路径有两种方式

  1. // 1.使用相对路径
  2. var relativePath = "_doc/test.png"
  3. // 2.使用平台的绝对路径
  4. var docPath = plus.io.convertLocalFileSystemURL("_doc/");
  5. var absolutePath = docPath + 'test.png'

发现错误?想参与编辑?在 GitHub 上编辑此页面!