UTS iOS插件

本文介绍了uniapp开发中遇到的常见问题,如插件高度宽度设置、iOSSDK集成、Swift类与协议的交互、闭包使用以及$emit的正确用法,提供了解决这些问题的方法和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

uni-app兼容模式组件(原“组件插件”)开发

内容是uni-app兼容模式组件(原“组件插件”)开发遇到的一些问题和解决方案

插件中emit的使用

注意:

  1. 不能把UTSJSONObject当成参数
  2. 参数不能是自定义类型
  3. emit不要驼峰命名(前期的hbuilder x版本不支持,后期不确定)
  4. 参数只能是map或map[](map数组),并且map中的value只能是map、string、number(未仔细验证 类型,Json、非map的array类型的value无法正确获取到内容✅

参考官方内容在这里插入图片描述
比如:

let lastResult = new Map<string, any>();
const listJsonArr = [{ "name": "0" }, {"name": "1" }];
lastResult.set("detailJsonArr",listJsonArr);//❌响应事件中,拿不到数据
lastResult.set("detailJson", { "name":"xxxx" });//❌响应事件中,拿不到数据
    
lastResult.set("detailStr",JSON.stringify(listJsonArr));//✅ string正常使用
let testMap = new Map<string, any>();
testMap.set("name", "testMapName")
lastResult.set("detailMap", testMap);//✅ Map正常使用
lastResult.set("detailMapArr",[testMap]);//✅ Map[](Map数组)正常使用

this.$emit('suggestions', lastResult)

结果如下图:
结果

报错:does not conform to protocol

插件中不支持函数中的参数带有默认值,如下图,会报错
在这里插入图片描述

1、使用时插件无法出现,内容显示不出来的原因

  • 再uniapp x中使用时,必须给这个插件高度和宽度,否则出不来!
///uniapp x 中使用
<uts-hello-view buttonText="点击按钮内容" style="width:375px;height: 375px;background-color: aqua;"></uts-hello-view>
  • 在IOS中编写这个插件的内容时候,不能全用self.bounds,有可能出不来啊!!!!需要直接写死frame,或者使用约束;
  • 同时,在didMoveToSuperview中获取view的frame不行,都是0,需要在layoutSubviews方法中获取view的frame才可以
  • 每次添加新的自定义UTS组件后最好重新走一遍自定义基座

2、无法运行

  • 检查是否都已经Podfile文件中版本修改为12
  • 问题:Sandbox: bash(72928) deny(1) file-write-create

方法:build settings->User Script Sandboxing 修改为NO在这里插入图片描述

3、使用第三方库时,不能在.h文件中导入这个内容第三方库 !!!

4、swift 如果类添加了public前缀,那么无法添加协议MAMapViewDelegate,会报错!!!

5、如何在uts插件中调用swift的方法

首先,swift必须是public 或者open修饰,比如 public class NHT_DemoView
但是高德地图又不能用这个修饰,所以将高德地图添加到一个view上,然后将这个view添加到另一个通过
public修饰的类上,通过这个类调用高德地图的api;

6、如何在UTS插件中使用swift的闭包

官网链接参考

  1. 使用

swift:
@objc open var SelectLocationChangeBlock: (([String:Any])->Void)?

UTS插件:

NVLoad() : NHT_DemoView {
			// -----NHT_AMAPView
			let button = new NHT_DemoView()
			button.SelectLocationChangeBlock = (res : any) => {
				this.$emit('fetchgis', res)
			};
	return button
		},
  • 原生中有些方法的闭包参数是逃逸闭包,此时就要在闭包前面添加 @escaping 标记:
    // 在闭包参数前添加@escaping
**.ust文件中定义使用** 
let blockMine : ((res : string) => void) | null = null;
// 逃逸闭包使用,在闭包参数前添加@escaping
export function testVariableBlock(@escaping completion : (res : string) => void) {
	// 在闭包参数前添加@escaping
	blockMine = completion
	setTimeout(function () {
		blockMine?.("xxxx111")
	}, 5000);
};

7、 this.$emit传值报错,error: cannot convert value of type ‘String’ to expected argument type ‘[String : Any]?’

因为传值类型不对,可以自己声明一个变量,然后当参数传值

const map1 = new Map<string,any>()
			map1.set('a', 'alpha');

或者改变响应回调的写法(必须按照你实际的返回内容),

button.SelectLocationChangeBlock = (res : Map<string, any>) => {
				this.$emit('fetchgis', res)
			};

自定义type不能传,在UTS插件中通过emit当成参数不能在使用的地方正确拿到数据
比如:

export type SuggestionItem = {
	name ?: string,
	address ?: string,
	adcode ?: string,
	latitude ?: string,
	longitude ?: string,
};

	let subSug : SuggestionItem = {}
	subSug.name = "name"
	subSug.address = "adress"
	this.$emit('test', [subSug])//不行,在使用的地方拿不到正确的数据
	this.$emit('test', { 'info': subSug })//不行,插件中不能直接传UTSJSONObject

	// 可行的
	let subJson = {} as UTSJSONObject
	subJson["name"] = "name"
	subJson["address"] = "adress"
	let lastResult = new Map<string, any>();
	lastResult.set("detail", subJson);
	this.$emit('test', lastResult)//可行,能正常收到内容

8、error: uni_modules/nhyt-texs/utssdk/app-ios/src/index.swift:61:9: error: contextual closure type ‘(Any, Any) -> Void’ expects 2 arguments, but 1 was used in closure body

可能是watch中出现的问题
比如监听下面的值
“enableLocation”: {//允许定位
type: Boolean,
default: false
},

watch: {
//这样写对,必须有newValue : boolean, oldVel : boolean,只有newValue会报这个错误
“enableLocation”: {
handler(newValue : boolean, oldVel : boolean) {
// if (!this.isInitLocation && newValue && !this.innerEnableLocation) {
// this.isInitLocation = true
// this.innerEnableLocation = newValue
// this.initLocation()
// }
},
immediate: false
},
}

10、插件中使用通知

目前的问题是无法获取通知发过来的信息内容
插件.uts文件中使用通知

import { NotificationCenter, NSNotification, Notification } from 'Foundation';
import { UIApplication } from "UIKit"

// 响应通知方法
class NotificationListener {
	// 按钮点击回调方法
	@objc notificationAction() {
		console.log("通知")
	}
}

let listsner = new NotificationListener()
// 启动监听推送消息事件
export function onPushMessage() {
	const method = Selector("notificationAction")
	NotificationCenter.default.addObserver(listsner, selector = method, name = NSNotification.Name('testNNNN'), object = null)
	NotificationCenter.default.addObserver(listsner, selector = method, name = UIApplication.userDidTakeScreenshotNotification, object = null)
	setTimeout(function () {
		NotificationCenter.default.post(name = NSNotification.Name(rawValue = 'testNNNN'), object = { "2": "xxx32" } as any)
		offPushMessage()
	}, 3000);
};

11、插件中iOS推送功使用

前提是,你的app已经在苹果开发者中心配置了推送证书

  • 配置权限描述:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>UIBackgroundModes</key>
		<array>
			<string>remote-notification</string>
		</array>
		<key>NSUserNotificationsUsageDescription</key>
		<string>打开XXX通知权限</string>
	</dict>
</plist>
  • UTS.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>aps-environment</key>
	<string>development</string>
</dict>
</plist>

  • 接收远程推送消息的方法
    applicationDidReceiveRemoteNotificationCompletionHandler(application : UIApplication, userInfo : Map<AnyHashable, any>, completionHandler : ((res : Int) => void)) {}
export class MyPluginClass implements UTSiOSHookProxy {
	// uts 插件创建时的回调。
	onCreate() {
	}
	// 应用正常启动时 (不包括已在后台转到前台的情况)的回调函数。
	applicationDidFinishLaunchingWithOptions(application : UIApplication | null, launchOptions : Map<UIApplication.LaunchOptionsKey, any> | null = null) : boolean {
		nowApplication = application
		// console.log("===============applicationDidFinishLaunchingWithOptions")
		return true
	}
	// 当应用程序接收到与用户活动相关的数据时调用此方法,例如,当用户使用 Universal Link 唤起应用时。
	applicationContinueUserActivityRestorationHandler(application : UIApplication | null, userActivity : NSUserActivity | null, restorationHandler : ((res : [any] | null) => void) | null = null) : boolean {
		console.log("===============applicationContinueUserActivityRestorationHandler")
		return true
	}



	// 远程通知注册成功时的回调函数。(打自定义基座时需要勾选 push 模块)
	didRegisterForRemoteNotifications(deviceToken : Data | null) {
		console.log("===============didRegisterForRemoteNotifications:", deviceToken)
		const token = PushObject.share().getHexString(for = deviceToken!)
		console.log("token----", token);
		NHTPushService.shareNHTPush().application(nowApplication!, didRegisterForRemoteNotificationsWithDeviceToken = deviceToken!);
	}
	// 远程通知注册失败时的回调函数。(打自定义基座时需要勾选 push 模块)
	didFailToRegisterForRemoteNotifications(error : NSError | null) {
		console.log("===============didFailToRegisterForRemoteNotifications:", error)
		NHTPushService.shareNHTPush().application(nowApplication!, didFailToRegisterForRemoteNotificationsWithError = error!);
	}

	/* 接收到远程推送消息
	如果想在后台的时候收到远程推送的时候能调用这个方法
	 那么推送时后台必须加上参数
	 "content-available":1 */
		applicationDidReceiveRemoteNotificationCompletionHandler(application : UIApplication, userInfo : Map<AnyHashable, any>, completionHandler : ((res : Int) => void)) {
		NHTPushService.shareNHTPush().application(application, didReceiveRemoteNotification = userInfo, fetchCompletionHandler = (res : UIBackgroundFetchResult) => {
			completionHandler(0);
		});
	}

	// 通过 url scheme 方式唤起 app 时的回调函数。(iOS9 之前的系统回调此方法,iOS9 之后的系统请使用 applicationOpenURLOptions)
	applicationHandleOpenURL(application : UIApplication | null, url : URL | null) : boolean {
		console.log("===============applicationDidFinishLaunchingWithOptions")
		return true
	}
	// 通过 url scheme 方式唤起 app 时的回调函数。
	applicationOpenURLOptions(app : UIApplication | null, url : URL, options : Map<UIApplication.OpenURLOptionsKey, any> | null = null) : boolean {
		console.log("===============applicationDidFinishLaunchingWithOptions")
		return true
	}
	// 当应用从活动状态主动变为非活动状态的时的回调函数。
	applicationWillResignActive(application : UIApplication | null) {
		// console.log("===============applicationWillResignActive")
	}
	// 应用完全激活时的回调函数。
	applicationDidBecomeActive(application : UIApplication | null) {
		// console.log("===============applicationDidBecomeActive")
	}
	// 应用程序进入后台时的回调函数。
	applicationDidEnterBackground(application : UIApplication | null) {
		console.log("===============did enter background")

	}
	// 当应用在后台状态,将要进入到前台运行时的回调函数。
	applicationWillEnterForeground(application : UIApplication | null) {
		console.log("===============applicationWillEnterForeground")

	}
	// 应用程序的 main 函数。
	applicationMain(argc : Int32, argv : UnsafeMutablePointer<UnsafeMutablePointer<CChar> | null>) {
		console.log("applicationMain")
	}


}

12、常见几种iOS原生函数在uniapp x中的使用示例

UTS中使用时的注意点

  • 参数之间用=连接
  • OC写的方法,在uniapp x中使用样式和在swift中类似,可以参考
//oc中的方法
-(void)checkIsHavePower_Microphone:(void (^)(NSInteger status))allowBlock;
//UTS文件中使用
export function checkIsHavePower_Microphone(callBack : (res : number) => void) {
	NHTFunction.share().checkIsHavePower_Microphone((state : Int) => {
		callBack(Number(state))
	});
};
//oc中的方法
-(void)startChangeTextToSpeech:(NSString *)text repeatPlay:(BOOL)repeatPlay;
//UTS文件中使用
export function startChangeText(speechText : string, repeatPlay : boolean) {
	NHTFunction.share().startChangeText(toSpeech = speechText, repeatPlay = repeatPlay);
};

13、类型转换

  • swift 中的 Dictionary 类型,在 uts 中使用 Map 类型代替
let resultInfo = res as Map<string, any | null>
  • Int转number
Number(res)

14.部分原生数据类型问题

部分类型(比如:CBCharacteristic),不能单独从数据中取出并使用,会直接崩溃,console也不行

export function notifyBLECharacteristicValueChange(options : NotifyBLECharacteristicValue) {
	NHT_Bluetooth.share().notifyBLECharacteristicValueChange((res : any) => {
		var resultInfo = res as Map<string, any | null>
		console.log("----订阅特征值--notifyBLECharacteristicValueChange", res);//这个可以
		const characteristic = resultInfo.get('characteristic') ?? "xxx"; //CBCharacteristic
		if (characteristic != null) {
			//不能打印characteristic(类型是CBCharacteristic),也不能使用,会崩溃
			console.log("----订阅特征值--notifyBLECharacteristicValueChange",characteristic);
		}
		options.success(characteristic)//这样也会崩溃
	});
}

15、调用原生方法时,不同参数类型的处理方法

  • [String:Any]

原生参数:[String:Any]
UTS插件中传的参数:Map<string,any>
比如:@objc open func addLine(options:[String:Any]){ }
调用:let params = new Map<string,any>()
this.$el.addLine(options = params)

  • [Float]
    原生参数:[Float]
    比如:@objc open func addCircle(strokeColor:[Float]){ }
    调用:
    const fillColorLast = [
    fillColor.red.toFloat(),
    fillColor.green.toFloat(),
    fillColor.blue.toFloat(),
    fillColor.alpha.toFloat()
    ];
    this.$el.addCircle( strokeColor = strokeColorLast)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值