DCloud 内置引擎库libnative-imagetranscoder.so、libweexcore.so、libweexjss.so 中的检测报警
libjpeg,mbed_tls,zlib这三个组件分别依赖于:
xxxx.apk.extracted/lib/armeabi-v7a/libnative-imagetranscoder.so
xxxx.apk.extracted/lib/armeabi-v7a/libweexcore.so
xxxx.apk.extracted/lib/armeabi-v7a/libweexjss.so
xxxx.apk.extracted/lib/arm64-v8a/libnative-imagetranscoder.so
xxxx.apk.extracted/lib/arm64-v8a/libweexcore.so
xxxx.apk.extracted/lib/arm64-v8a/libweexjss.so
我们程序做第三方漏洞审计,这些组件版本均被检测出有漏洞修复更新
具体审计问题见附件详情,官方重视一下尽快给出解决方案并修复问题漏洞
libjpeg,mbed_tls,zlib这三个组件分别依赖于:
xxxx.apk.extracted/lib/armeabi-v7a/libnative-imagetranscoder.so
xxxx.apk.extracted/lib/armeabi-v7a/libweexcore.so
xxxx.apk.extracted/lib/armeabi-v7a/libweexjss.so
xxxx.apk.extracted/lib/arm64-v8a/libnative-imagetranscoder.so
xxxx.apk.extracted/lib/arm64-v8a/libweexcore.so
xxxx.apk.extracted/lib/arm64-v8a/libweexjss.so
我们程序做第三方漏洞审计,这些组件版本均被检测出有漏洞修复更新
具体审计问题见附件详情,官方重视一下尽快给出解决方案并修复问题漏洞
收起阅读 »【Alpha 体验邀请】luch-request v4:面向 uni-app 重新设计的 TypeScript 请求库
大家好,我是 luch-request 的维护者。
luch-request v4 Alpha 现已发布到 npm,想邀请正在使用 uni-app 的开发者参与体验,帮助验证不同平台下的请求、上传下载、取消和原生 Task 行为。
npm install luch-request@alpha
v4 不是对 v3 的内部重构,而是一个 breaking version。它围绕 uni API、原生 Task、TypeScript 类型边界和多平台行为重新设计,希望在保留 uni 原生能力的同时,让请求配置、错误处理和取消行为更加明确。
当前版本为
4.0.0-alpha.1,公共 API 在稳定版前仍可能调整。建议先在测试项目或非核心业务中体验,并在自己的目标平台完成验证。
为什么重新设计 v4
uni-app 可以运行在 H5、App 和不同小程序平台,但各平台支持的请求参数、原生 Task、错误对象和取消能力并不完全一致。
如果直接套用浏览器 HTTP client 的设计,会遇到一些实际问题:
- 浏览器的 API 和类型不能代表所有 uni-app 平台;
request、uploadFile、downloadFile拥有不同的原生参数和 Task;- 平台新增参数的速度可能快于请求库和类型声明的更新;
- 网络失败、HTTP 状态码失败、取消和 JSON 解析失败需要不同的判断依据;
- TypeScript 类型过于宽松会失去约束,过于严格又可能阻碍平台新增能力。
因此,v4 没有把 v3 的内部结构继续扩展,而是重新确定了公共 API、配置合并、Interceptor、错误、取消和跨平台边界。
v4 带来了什么
1. TypeScript-first
请求响应、请求体和查询参数可以分别建模,常见调用保持直接:
import { createLuchRequest } from 'luch-request'
const http = createLuchRequest({
baseURL: 'https://api.example.com',
timeout: 10_000
})
interface User {
id: number
name: string
}
interface UserListParams {
page: number
keyword?: string
}
const response = await http.get<User[], UserListParams>('/users', {
params: {
page: 1
}
})
console.log(response.data)
JavaScript 项目仍然可以使用 v4;TypeScript 项目则能获得更完整的配置、响应、错误和公共导出类型。
2. 保留 uni 原生能力
v4 分别支持:
requestuploaddownload
三类操作共用实例配置、Interceptor 和错误契约,但在派发边界保留各自的原生参数,不使用 UPLOAD、DOWNLOAD 这样的伪 HTTP method。
3. 可以访问原生 Task
请求 Promise 提供 abort()、task 和 onTask(),单次请求配置也可以使用 onTask:
const request = http.get('/users', {
onTask(task) {
console.log('原生 Task:', task)
}
})
// 需要取消时
request.abort()
如果取消能力需要跨 service 层传递,可以使用 createCancelSource(),不必把请求 Promise 暴露给每一层调用方。
4. 明确的配置边界与 nativeOptions
v4 将配置分成不同职责:
- 常用 uni 请求参数可以直接配置;
- luch-request 自身行为放在
luchOptions; - 平台新增参数或插件尚未声明的参数通过
nativeOptions透传。
nativeOptions 是面向平台新能力的逃生窗口。当比较新的 uni API、某个平台参数或第三方插件参数还没有进入请求库类型时,使用方仍然可以显式透传,而不需要等待库发布新版本。
5. Interceptor 区分操作类型
v4 提供 request/response interceptor,支持同步和异步处理。Interceptor 上下文可以使用 LuchOperation 判断当前执行的是 request、upload 还是 download,避免依赖字符串或伪 method。
6. 统一错误与 JSON 解析策略
请求失败时统一抛出 LuchRequestError,并保留可用于判断的上下文,例如:
codeconfigresponsetaskcauserawcancelMode
网络失败、HTTP 状态码失败、取消和 JSON 解析失败可以分别判断。对于 JSON 解析失败,还可以通过 JSONParsingMode 选择抛出错误、保留文本等处理方式。
Alpha 阶段暂不包含什么
为了先稳定核心请求契约,首个版本不会内置以下能力:
- 自动重试
- 请求缓存
- 请求去重
- 并发控制
- Token 自动刷新
- WebSocket
uni_modules- uni-app x
这些能力并不是简单地全部塞进核心库。后续会根据真实使用场景,判断应该由 Interceptor、独立扩展还是核心能力提供。
v3 用户需要立即升级吗
不需要。
v3 稳定项目可以继续使用现有版本。v4 是 breaking version,适合以下开发者优先体验:
- 正在开发新项目;
- 希望获得更完整的 TypeScript 类型;
- 需要统一错误和取消行为;
- 需要访问原生 Task 或进度事件;
- 希望分别处理 request、upload 和 download;
- 愿意在自己的目标平台验证 Alpha 行为。
正式迁移前,请先阅读 v3 到 v4 的迁移文档,并在实际运行平台完成 smoke test。
希望大家重点帮助验证
自动化测试能够验证请求库逻辑,但不能代替所有真实设备、运行平台和 uni-app 版本。特别希望大家帮助验证:
- H5、App、微信小程序及其他小程序中的基础请求;
upload、download及进度事件;abort()是否触发平台原生中断;onTask()是否能取得预期的原生 Task;- 不同平台返回的错误结构是否存在差异;
nativeOptions是否能正确传递平台新增参数;- TypeScript 类型是否存在过严、过松或无法表达的场景;
- JavaScript 项目中的配置和错误行为是否足够清晰。
如何反馈
提交问题时,如果方便,请尽量提供以下信息:
运行平台:
HBuilderX / uni-app 版本:
luch-request 版本:
使用 TypeScript 还是 JavaScript:
最小复现代码:
预期结果:
实际结果:
错误对象或控制台日志:
如果问题与取消、上传下载或原生 Task 有关,也请说明实际运行平台,以及是否能取得对应 Task。
相关链接
- v4 文档:https://luch-request.quanzhan.co/
- GitHub:https://github.com/lei-mu/luch-request
- 问题反馈:https://github.com/lei-mu/luch-request/issues
- npm:https://www.npmjs.com/package/luch-request
- v3 稳定文档:https://v3.luch-request.quanzhan.co/
写在最后
v4 的核心设计和自动化测试已经完成,但 uni-app 请求库真正困难的部分始终是多平台运行差异。
希望正在使用 uni-app 的开发者帮助验证 request、upload、download、取消和原生 Task 等能力。无论是兼容性问题、类型设计建议,还是某个平台特有的行为差异,都欢迎在 GitHub Issue 或本帖中反馈。
感谢每一位愿意安装、运行和提供反馈的开发者。
大家好,我是 luch-request 的维护者。
luch-request v4 Alpha 现已发布到 npm,想邀请正在使用 uni-app 的开发者参与体验,帮助验证不同平台下的请求、上传下载、取消和原生 Task 行为。
npm install luch-request@alpha
v4 不是对 v3 的内部重构,而是一个 breaking version。它围绕 uni API、原生 Task、TypeScript 类型边界和多平台行为重新设计,希望在保留 uni 原生能力的同时,让请求配置、错误处理和取消行为更加明确。
当前版本为
4.0.0-alpha.1,公共 API 在稳定版前仍可能调整。建议先在测试项目或非核心业务中体验,并在自己的目标平台完成验证。
为什么重新设计 v4
uni-app 可以运行在 H5、App 和不同小程序平台,但各平台支持的请求参数、原生 Task、错误对象和取消能力并不完全一致。
如果直接套用浏览器 HTTP client 的设计,会遇到一些实际问题:
- 浏览器的 API 和类型不能代表所有 uni-app 平台;
request、uploadFile、downloadFile拥有不同的原生参数和 Task;- 平台新增参数的速度可能快于请求库和类型声明的更新;
- 网络失败、HTTP 状态码失败、取消和 JSON 解析失败需要不同的判断依据;
- TypeScript 类型过于宽松会失去约束,过于严格又可能阻碍平台新增能力。
因此,v4 没有把 v3 的内部结构继续扩展,而是重新确定了公共 API、配置合并、Interceptor、错误、取消和跨平台边界。
v4 带来了什么
1. TypeScript-first
请求响应、请求体和查询参数可以分别建模,常见调用保持直接:
import { createLuchRequest } from 'luch-request'
const http = createLuchRequest({
baseURL: 'https://api.example.com',
timeout: 10_000
})
interface User {
id: number
name: string
}
interface UserListParams {
page: number
keyword?: string
}
const response = await http.get<User[], UserListParams>('/users', {
params: {
page: 1
}
})
console.log(response.data)
JavaScript 项目仍然可以使用 v4;TypeScript 项目则能获得更完整的配置、响应、错误和公共导出类型。
2. 保留 uni 原生能力
v4 分别支持:
requestuploaddownload
三类操作共用实例配置、Interceptor 和错误契约,但在派发边界保留各自的原生参数,不使用 UPLOAD、DOWNLOAD 这样的伪 HTTP method。
3. 可以访问原生 Task
请求 Promise 提供 abort()、task 和 onTask(),单次请求配置也可以使用 onTask:
const request = http.get('/users', {
onTask(task) {
console.log('原生 Task:', task)
}
})
// 需要取消时
request.abort()
如果取消能力需要跨 service 层传递,可以使用 createCancelSource(),不必把请求 Promise 暴露给每一层调用方。
4. 明确的配置边界与 nativeOptions
v4 将配置分成不同职责:
- 常用 uni 请求参数可以直接配置;
- luch-request 自身行为放在
luchOptions; - 平台新增参数或插件尚未声明的参数通过
nativeOptions透传。
nativeOptions 是面向平台新能力的逃生窗口。当比较新的 uni API、某个平台参数或第三方插件参数还没有进入请求库类型时,使用方仍然可以显式透传,而不需要等待库发布新版本。
5. Interceptor 区分操作类型
v4 提供 request/response interceptor,支持同步和异步处理。Interceptor 上下文可以使用 LuchOperation 判断当前执行的是 request、upload 还是 download,避免依赖字符串或伪 method。
6. 统一错误与 JSON 解析策略
请求失败时统一抛出 LuchRequestError,并保留可用于判断的上下文,例如:
codeconfigresponsetaskcauserawcancelMode
网络失败、HTTP 状态码失败、取消和 JSON 解析失败可以分别判断。对于 JSON 解析失败,还可以通过 JSONParsingMode 选择抛出错误、保留文本等处理方式。
Alpha 阶段暂不包含什么
为了先稳定核心请求契约,首个版本不会内置以下能力:
- 自动重试
- 请求缓存
- 请求去重
- 并发控制
- Token 自动刷新
- WebSocket
uni_modules- uni-app x
这些能力并不是简单地全部塞进核心库。后续会根据真实使用场景,判断应该由 Interceptor、独立扩展还是核心能力提供。
v3 用户需要立即升级吗
不需要。
v3 稳定项目可以继续使用现有版本。v4 是 breaking version,适合以下开发者优先体验:
- 正在开发新项目;
- 希望获得更完整的 TypeScript 类型;
- 需要统一错误和取消行为;
- 需要访问原生 Task 或进度事件;
- 希望分别处理 request、upload 和 download;
- 愿意在自己的目标平台验证 Alpha 行为。
正式迁移前,请先阅读 v3 到 v4 的迁移文档,并在实际运行平台完成 smoke test。
希望大家重点帮助验证
自动化测试能够验证请求库逻辑,但不能代替所有真实设备、运行平台和 uni-app 版本。特别希望大家帮助验证:
- H5、App、微信小程序及其他小程序中的基础请求;
upload、download及进度事件;abort()是否触发平台原生中断;onTask()是否能取得预期的原生 Task;- 不同平台返回的错误结构是否存在差异;
nativeOptions是否能正确传递平台新增参数;- TypeScript 类型是否存在过严、过松或无法表达的场景;
- JavaScript 项目中的配置和错误行为是否足够清晰。
如何反馈
提交问题时,如果方便,请尽量提供以下信息:
运行平台:
HBuilderX / uni-app 版本:
luch-request 版本:
使用 TypeScript 还是 JavaScript:
最小复现代码:
预期结果:
实际结果:
错误对象或控制台日志:
如果问题与取消、上传下载或原生 Task 有关,也请说明实际运行平台,以及是否能取得对应 Task。
相关链接
- v4 文档:https://luch-request.quanzhan.co/
- GitHub:https://github.com/lei-mu/luch-request
- 问题反馈:https://github.com/lei-mu/luch-request/issues
- npm:https://www.npmjs.com/package/luch-request
- v3 稳定文档:https://v3.luch-request.quanzhan.co/
写在最后
v4 的核心设计和自动化测试已经完成,但 uni-app 请求库真正困难的部分始终是多平台运行差异。
希望正在使用 uni-app 的开发者帮助验证 request、upload、download、取消和原生 Task 等能力。无论是兼容性问题、类型设计建议,还是某个平台特有的行为差异,都欢迎在 GitHub Issue 或本帖中反馈。
感谢每一位愿意安装、运行和提供反馈的开发者。
收起阅读 »uni-app / uni-app x / 微信小程序 / 抖音小程序 / App 外包开发|全栈接单|二开修Bug / 后台 / 后端 / 前端
uni-app / uni-app x / 微信小程序 / 抖音小程序 / App 外包开发|全栈接单|二开修Bug / 后台 / 后端 / 前端
20年开发经验,需要的咨询
uni-app / uni-app x / 微信小程序 / 抖音小程序 / App 外包开发|全栈接单|二开修Bug / 后台 / 后端 / 前端
20年开发经验,需要的咨询
打造高效移动应用,Android 与 iOS 性能优化策略与工具选择
'''之前一直做 iOS 开发,性能优化就是 Instruments 一套流程走下来,从 CPU 到内存到能耗都有对应的模板。后来接手一个 Flutter 跨平台项目,才发现 Android 和 iOS 的性能优化策略差别不小,用的工具也不一样,一套思路套两个平台行不通。这里把两个平台的优化策略和工具选择放在一起对比,给跨平台团队做个参考,重点是找出哪些策略是共通的、哪些要分开做。
启动速度优化
两个平台都很看重启动速度,但优化点不同。iOS 的冷启动受动态库加载和首屏渲染影响,优化方向是精简启动逻辑、延迟初始化、减少主线程首帧前的工作量,把不着急的初始化移到首帧之后。Android 启动受 Application 初始化影响更大,大量 SDK 在 Application 里同步初始化会明显拖慢启动,优化方向是 SDK 异步初始化、按需加载、能懒加载就懒加载。两个平台都可以通过拆分启动任务、错峰执行来改善,这个思路是共通的。
内存优化
内存问题的表现形式不同。iOS 常见的是循环引用导致的内存泄漏和图片缓存堆积,排查用 Instruments Allocations 做深度分析,或者用 KeyMob 的内存面板实时看曲线——退出页面时扫一眼曲线有没有回落,就能判断释放是否正常。Android 常见的是 Bitmap 占用过高和内存泄漏(Activity 泄漏居多),排查用 Android Studio 的 Memory Profiler,看 GC 后的内存水位。优化方向类似:及时释放资源、合理设置缓存上限、避免持有不必要的大对象,这些策略两个平台通用。
流畅度与帧率
流畅度优化的核心都是保证帧率稳定,但排查路径不同。iOS 端重点排查主线程耗时操作和离屏渲染(圆角、阴影、mask),用 Instruments Time Profiler 深挖,或者 KeyMob 实时监控 FPS 曲线,操作页面时看哪里掉帧。Android 端重点排查主线程阻塞(比如在主线程做网络请求或大文件 IO)和过度绘制,用 Android Studio 的 CPU Profiler 和 GPU Profiler,开启开发者选项里的"显示布局边界"可以直观看到过度绘制区域。Flutter 项目两个平台共用渲染层,优化思路一致,主要关注 build 和 layout 阶段的耗时,这部分优化一次两个平台同时受益。
能耗优化
能耗优化两个平台方向接近:减少后台定位频率、合批网络请求、控制唤醒。iOS 用 Instruments Energy Log 或 KeyMob 的能耗记录查看硬件调用情况,Android 用 Battery Historian 分析耗电明细。跨平台项目在共享代码层做合批和降频处理,两个平台同时受益。
工具怎么选
跨平台团队建议每个平台备一套监控工具:iOS 用 Instruments 做深度分析,日常开发用 KeyMob 实时看 CPU、内存、FPS 曲线,改完代码随手扫一眼;Android 用 Android Studio Profiler 做深度分析,日常用 PerfDog 或厂商工具快速验证。工具不一定要最全,能形成"日常快检 + 深度定位"的组合就行,重点是养成每次发版前跑一轮性能对比的习惯。'''
'''之前一直做 iOS 开发,性能优化就是 Instruments 一套流程走下来,从 CPU 到内存到能耗都有对应的模板。后来接手一个 Flutter 跨平台项目,才发现 Android 和 iOS 的性能优化策略差别不小,用的工具也不一样,一套思路套两个平台行不通。这里把两个平台的优化策略和工具选择放在一起对比,给跨平台团队做个参考,重点是找出哪些策略是共通的、哪些要分开做。
启动速度优化
两个平台都很看重启动速度,但优化点不同。iOS 的冷启动受动态库加载和首屏渲染影响,优化方向是精简启动逻辑、延迟初始化、减少主线程首帧前的工作量,把不着急的初始化移到首帧之后。Android 启动受 Application 初始化影响更大,大量 SDK 在 Application 里同步初始化会明显拖慢启动,优化方向是 SDK 异步初始化、按需加载、能懒加载就懒加载。两个平台都可以通过拆分启动任务、错峰执行来改善,这个思路是共通的。
内存优化
内存问题的表现形式不同。iOS 常见的是循环引用导致的内存泄漏和图片缓存堆积,排查用 Instruments Allocations 做深度分析,或者用 KeyMob 的内存面板实时看曲线——退出页面时扫一眼曲线有没有回落,就能判断释放是否正常。Android 常见的是 Bitmap 占用过高和内存泄漏(Activity 泄漏居多),排查用 Android Studio 的 Memory Profiler,看 GC 后的内存水位。优化方向类似:及时释放资源、合理设置缓存上限、避免持有不必要的大对象,这些策略两个平台通用。
流畅度与帧率
流畅度优化的核心都是保证帧率稳定,但排查路径不同。iOS 端重点排查主线程耗时操作和离屏渲染(圆角、阴影、mask),用 Instruments Time Profiler 深挖,或者 KeyMob 实时监控 FPS 曲线,操作页面时看哪里掉帧。Android 端重点排查主线程阻塞(比如在主线程做网络请求或大文件 IO)和过度绘制,用 Android Studio 的 CPU Profiler 和 GPU Profiler,开启开发者选项里的"显示布局边界"可以直观看到过度绘制区域。Flutter 项目两个平台共用渲染层,优化思路一致,主要关注 build 和 layout 阶段的耗时,这部分优化一次两个平台同时受益。
能耗优化
能耗优化两个平台方向接近:减少后台定位频率、合批网络请求、控制唤醒。iOS 用 Instruments Energy Log 或 KeyMob 的能耗记录查看硬件调用情况,Android 用 Battery Historian 分析耗电明细。跨平台项目在共享代码层做合批和降频处理,两个平台同时受益。
工具怎么选
跨平台团队建议每个平台备一套监控工具:iOS 用 Instruments 做深度分析,日常开发用 KeyMob 实时看 CPU、内存、FPS 曲线,改完代码随手扫一眼;Android 用 Android Studio Profiler 做深度分析,日常用 PerfDog 或厂商工具快速验证。工具不一定要最全,能形成"日常快检 + 深度定位"的组合就行,重点是养成每次发版前跑一轮性能对比的习惯。'''
收起阅读 »Navigation在分栏模式下,如何给右侧空白部分设置默认页面
问题现象
Navigation在Split模式下,未点击左侧导航栏时,右侧子页显示区显示为空白。当未进行页面路由推送时,如何给右侧的空白部分设置默认展示页面?
背景知识
Navigation组件的分栏模式由mode属性控制,包括单栏(Stack)、分栏(Split)和自适应(Auto)三个属性。该属性默认为Auto模式,在该模式下会自动监听屏幕属性,当为折叠屏或平板时,默认分栏显示,在折叠状态或普通手机时可为单栏显示。
splitPlaceholder:在API20下,Navigation双栏模式支持设置右侧页面显示默认占位页,占位页仅作为UI展示页,不可获焦和响应事件。
解决方案
方案一:push方法推送默认页面。
编写默认页面,并重写onBackPressed返回,当在该页面返回时直接退出软件。
Navigation主页执行aboutToAppear,将默认页面加入页面栈进行默认显示。
import { window } from '@kit.ArkUI';
@Entry
@Component
struct NavigationExample {
@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack();
private arr: number[] = [1, 2, 3];
aboutToAppear(): void {
this.pageInfos.pushPath({ name: 'DefaultPage' });
}
@Builder
PageMap(name: string) {
if (name === 'NavDestinationTitle1') {
pageOneTmp();
} else if (name === 'NavDestinationTitle2') {
pageTwoTmp();
} else if (name === 'NavDestinationTitle3') {
pageThreeTmp();
} else if (name === 'DefaultPage') {
DefaultPage();
}
}
build() {
Column() {
Navigation(this.pageInfos) {
TextInput({ placeholder: 'search...' })
.width('90%')
.height(40)
.backgroundColor('#FFFFFF');
List({ space: 12 }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('Page' + item)
.width('100%')
.height(72)
.backgroundColor('#FFFFFF')
.borderRadius(24)
.fontSize(16)
.fontWeight(500)
.textAlign(TextAlign.Center)
.onClick(() => {
this.pageInfos.pushPath({ name: 'NavDestinationTitle' + item });
});
};
}, (item: number) => item.toString());
}
.width('90%')
.margin({ top: 12 });
}
.title('主标题')
.mode(NavigationMode.Split)
.navDestination(this.PageMap);
}
.height('100%')
.width('100%')
.background('#F1F3F5');
}
}
// PageOne页面
@Component
export struct pageOneTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent1');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle1');
}
}
// PageTwo页面
@Component
export struct pageTwoTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent2');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle2');
}
}
// PageThree页面
@Component
export struct pageThreeTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent3');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle3');
}
}
@Component
export struct DefaultPage {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('DefaultPage');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('DefaultPage')
.onBackPressed(() => {
// 返回true表示自定义返回,能避免返回空白页面。返回false则表示系统默认返回,会返回空白页面。
try {
// 需要导入window
window.getLastWindow(this.getUIContext().getHostContext(), (err, win) => {
const errCode: number = err.code;
if (errCode) {
console.error(Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message});
return;
}
win.minimize((err) => {
const errCode: number = err.code;
if (errCode) {
console.error(Failed to minimize the window. Cause code: ${err.code}, message: ${err.message});
return;
}
console.info('Succeeded in minimizing the window.');
});
});
} catch (exception) {
console.error(Failed to obtain the top window. Cause code: ${exception.code}, message: ${exception.message});
}
return true;
});
}
}
实现效果如下:
点击放大
方案二:采用API20中的splitPlaceholder属性。
详情参考:示例14。
常见FAQ
Q:API20以下,当采用Auto模式时,如何判断是否需要推送默认显示页面?
A:可以通过以下方式在aboutToAppear中判断是否需要推送默认页面:
通过设备信息@ohos.deviceInfo接口获取设备的deviceType类型。
通过屏幕属性@ohos.display的isFoldable方法返回当前设备是否可折叠的结果。
总结
实现默认展示页面的方式为,创建一个默认页面,在创建主页时自动推送该默认页面即可,其他关联实现方式参照常见FAQ实现即可。
https://coub.com/view/4b7lh7
https://coub.com/view/4b7lh5
https://coub.com/view/4b7lh4
https://coub.com/view/4b7lh1
https://coub.com/view/4b7lh0
https://coub.com/view/4b7lgx
https://coub.com/view/4b7lgu
https://coub.com/view/4b7lgr
https://coub.com/view/4b7lgo
https://coub.com/view/4b7lgj
https://coub.com/view/4b7lgh
https://coub.com/view/4b7lgd
https://coub.com/view/4b7lgb
https://coub.com/view/4b7lg6
https://coub.com/view/4b7lg2
https://coub.com/view/4b7lfw
https://coub.com/view/4b7lft
https://coub.com/view/4b7lfn
https://coub.com/view/4b7lfh
https://coub.com/view/4b7lfa
https://coub.com/view/4b7lek
https://coub.com/view/4b7leh
https://coub.com/view/4b7lef
https://coub.com/view/4b7lec
https://coub.com/view/4b7leb
https://coub.com/view/4b7le7
https://coub.com/view/4b7le1
https://coub.com/view/4b7ldz
https://coub.com/view/4b7ldx
https://coub.com/view/4b7ldw
https://coub.com/view/4b7ldt
https://coub.com/view/4b7ldq
https://coub.com/view/4b7ldn
https://coub.com/view/4b7lda
https://coub.com/view/4b7ld8
https://coub.com/view/4b7ld4
https://coub.com/view/4b7lcz
https://coub.com/view/4b7lcv
https://coub.com/view/4b7lcq
https://coub.com/view/4b7lco
https://coub.com/view/4b7lcm
https://coub.com/view/4b7lcj
https://coub.com/view/4b7lcd
https://coub.com/view/4b7lc9
https://coub.com/view/4b7lc0
https://coub.com/view/4b7lbx
https://coub.com/view/4b7lbw
https://coub.com/view/4b7lbv
https://coub.com/view/4b7lbu
https://coub.com/view/4b7lbt
https://coub.com/view/4b7lbc
https://coub.com/view/4b7lb5
https://coub.com/view/4b7lax
https://coub.com/view/4b7laj
https://coub.com/view/4b7laf
https://coub.com/view/4b7lae
https://coub.com/view/4b7lab
https://coub.com/view/4b7la7
https://coub.com/view/4b7l9u
https://coub.com/view/4b7l9s
https://coub.com/view/4b7l9q
https://coub.com/view/4b7l9n
https://coub.com/view/4b7l9k
https://coub.com/view/4b7l9j
https://coub.com/view/4b7l9i
https://coub.com/view/4b7l9h
https://coub.com/view/4b7l9g
https://coub.com/view/4b7l9f
https://coub.com/view/4b7l99
https://coub.com/view/4b7l95
https://coub.com/view/4b7l93
https://coub.com/view/4b7l90
https://coub.com/view/4b7l8u
https://coub.com/view/4b7l8r
https://coub.com/view/4b7l8q
https://coub.com/view/4b7l8p
https://coub.com/view/4b7l8o
https://coub.com/view/4b7l8m
https://coub.com/view/4b7l8k
https://coub.com/view/4b7l8g
https://coub.com/view/4b7l8f
https://coub.com/view/4b7l8e
https://coub.com/view/4b7l8b
https://coub.com/view/4b7l88
https://coub.com/view/4b7l86
https://coub.com/view/4b7l84
https://coub.com/view/4b7l81
https://coub.com/view/4b7l7z
https://coub.com/view/4b7l7x
https://coub.com/view/4b7l7w
https://coub.com/view/4b7l7v
https://coub.com/view/4b7l7u
https://coub.com/view/4b7l7s
问题现象
Navigation在Split模式下,未点击左侧导航栏时,右侧子页显示区显示为空白。当未进行页面路由推送时,如何给右侧的空白部分设置默认展示页面?
背景知识
Navigation组件的分栏模式由mode属性控制,包括单栏(Stack)、分栏(Split)和自适应(Auto)三个属性。该属性默认为Auto模式,在该模式下会自动监听屏幕属性,当为折叠屏或平板时,默认分栏显示,在折叠状态或普通手机时可为单栏显示。
splitPlaceholder:在API20下,Navigation双栏模式支持设置右侧页面显示默认占位页,占位页仅作为UI展示页,不可获焦和响应事件。
解决方案
方案一:push方法推送默认页面。
编写默认页面,并重写onBackPressed返回,当在该页面返回时直接退出软件。
Navigation主页执行aboutToAppear,将默认页面加入页面栈进行默认显示。
import { window } from '@kit.ArkUI';
@Entry
@Component
struct NavigationExample {
@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack();
private arr: number[] = [1, 2, 3];
aboutToAppear(): void {
this.pageInfos.pushPath({ name: 'DefaultPage' });
}
@Builder
PageMap(name: string) {
if (name === 'NavDestinationTitle1') {
pageOneTmp();
} else if (name === 'NavDestinationTitle2') {
pageTwoTmp();
} else if (name === 'NavDestinationTitle3') {
pageThreeTmp();
} else if (name === 'DefaultPage') {
DefaultPage();
}
}
build() {
Column() {
Navigation(this.pageInfos) {
TextInput({ placeholder: 'search...' })
.width('90%')
.height(40)
.backgroundColor('#FFFFFF');
List({ space: 12 }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('Page' + item)
.width('100%')
.height(72)
.backgroundColor('#FFFFFF')
.borderRadius(24)
.fontSize(16)
.fontWeight(500)
.textAlign(TextAlign.Center)
.onClick(() => {
this.pageInfos.pushPath({ name: 'NavDestinationTitle' + item });
});
};
}, (item: number) => item.toString());
}
.width('90%')
.margin({ top: 12 });
}
.title('主标题')
.mode(NavigationMode.Split)
.navDestination(this.PageMap);
}
.height('100%')
.width('100%')
.background('#F1F3F5');
}
}
// PageOne页面
@Component
export struct pageOneTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent1');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle1');
}
}
// PageTwo页面
@Component
export struct pageTwoTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent2');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle2');
}
}
// PageThree页面
@Component
export struct pageThreeTmp {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('NavDestinationContent3');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('NavDestinationTitle3');
}
}
@Component
export struct DefaultPage {
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Column() {
Text('DefaultPage');
}.width('100%').height('100%').justifyContent(FlexAlign.Center);
}.title('DefaultPage')
.onBackPressed(() => {
// 返回true表示自定义返回,能避免返回空白页面。返回false则表示系统默认返回,会返回空白页面。
try {
// 需要导入window
window.getLastWindow(this.getUIContext().getHostContext(), (err, win) => {
const errCode: number = err.code;
if (errCode) {
console.error(Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message});
return;
}
win.minimize((err) => {
const errCode: number = err.code;
if (errCode) {
console.error(Failed to minimize the window. Cause code: ${err.code}, message: ${err.message});
return;
}
console.info('Succeeded in minimizing the window.');
});
});
} catch (exception) {
console.error(Failed to obtain the top window. Cause code: ${exception.code}, message: ${exception.message});
}
return true;
});
}
}
实现效果如下:
点击放大
方案二:采用API20中的splitPlaceholder属性。
详情参考:示例14。
常见FAQ
Q:API20以下,当采用Auto模式时,如何判断是否需要推送默认显示页面?
A:可以通过以下方式在aboutToAppear中判断是否需要推送默认页面:
通过设备信息@ohos.deviceInfo接口获取设备的deviceType类型。
通过屏幕属性@ohos.display的isFoldable方法返回当前设备是否可折叠的结果。
总结
实现默认展示页面的方式为,创建一个默认页面,在创建主页时自动推送该默认页面即可,其他关联实现方式参照常见FAQ实现即可。
https://coub.com/view/4b7lh7
https://coub.com/view/4b7lh5
https://coub.com/view/4b7lh4
https://coub.com/view/4b7lh1
https://coub.com/view/4b7lh0
https://coub.com/view/4b7lgx
https://coub.com/view/4b7lgu
https://coub.com/view/4b7lgr
https://coub.com/view/4b7lgo
https://coub.com/view/4b7lgj
https://coub.com/view/4b7lgh
https://coub.com/view/4b7lgd
https://coub.com/view/4b7lgb
https://coub.com/view/4b7lg6
https://coub.com/view/4b7lg2
https://coub.com/view/4b7lfw
https://coub.com/view/4b7lft
https://coub.com/view/4b7lfn
https://coub.com/view/4b7lfh
https://coub.com/view/4b7lfa
https://coub.com/view/4b7lek
https://coub.com/view/4b7leh
https://coub.com/view/4b7lef
https://coub.com/view/4b7lec
https://coub.com/view/4b7leb
https://coub.com/view/4b7le7
https://coub.com/view/4b7le1
https://coub.com/view/4b7ldz
https://coub.com/view/4b7ldx
https://coub.com/view/4b7ldw
https://coub.com/view/4b7ldt
https://coub.com/view/4b7ldq
https://coub.com/view/4b7ldn
https://coub.com/view/4b7lda
https://coub.com/view/4b7ld8
https://coub.com/view/4b7ld4
https://coub.com/view/4b7lcz
https://coub.com/view/4b7lcv
https://coub.com/view/4b7lcq
https://coub.com/view/4b7lco
https://coub.com/view/4b7lcm
https://coub.com/view/4b7lcj
https://coub.com/view/4b7lcd
https://coub.com/view/4b7lc9
https://coub.com/view/4b7lc0
https://coub.com/view/4b7lbx
https://coub.com/view/4b7lbw
https://coub.com/view/4b7lbv
https://coub.com/view/4b7lbu
https://coub.com/view/4b7lbt
https://coub.com/view/4b7lbc
https://coub.com/view/4b7lb5
https://coub.com/view/4b7lax
https://coub.com/view/4b7laj
https://coub.com/view/4b7laf
https://coub.com/view/4b7lae
https://coub.com/view/4b7lab
https://coub.com/view/4b7la7
https://coub.com/view/4b7l9u
https://coub.com/view/4b7l9s
https://coub.com/view/4b7l9q
https://coub.com/view/4b7l9n
https://coub.com/view/4b7l9k
https://coub.com/view/4b7l9j
https://coub.com/view/4b7l9i
https://coub.com/view/4b7l9h
https://coub.com/view/4b7l9g
https://coub.com/view/4b7l9f
https://coub.com/view/4b7l99
https://coub.com/view/4b7l95
https://coub.com/view/4b7l93
https://coub.com/view/4b7l90
https://coub.com/view/4b7l8u
https://coub.com/view/4b7l8r
https://coub.com/view/4b7l8q
https://coub.com/view/4b7l8p
https://coub.com/view/4b7l8o
https://coub.com/view/4b7l8m
https://coub.com/view/4b7l8k
https://coub.com/view/4b7l8g
https://coub.com/view/4b7l8f
https://coub.com/view/4b7l8e
https://coub.com/view/4b7l8b
https://coub.com/view/4b7l88
https://coub.com/view/4b7l86
https://coub.com/view/4b7l84
https://coub.com/view/4b7l81
https://coub.com/view/4b7l7z
https://coub.com/view/4b7l7x
https://coub.com/view/4b7l7w
https://coub.com/view/4b7l7v
https://coub.com/view/4b7l7u
https://coub.com/view/4b7l7s
建议把uts规范 知识库开放出来 而不是藏在ai-agent里让用户订阅付费
建议把uts规范 知识库开放出来 而不是藏在ai-agent里让用户订阅付费
建议把uts规范 知识库开放出来 而不是藏在ai-agent里让用户订阅付费
深入解析iOS应用性能分析:CPU、内存与网络监控技术
'''
iOS app性能分析
iOS app的性能是用户体验的重要指标之一。一个高性能的app能够快速响应用户的操作,流畅地展示内容,并且减少耗电量和资源占用。因此,开发者需要对自己的app进行性能分析,找出潜在的性能问题并进行优化。本文将介绍一些常见的iOS app性能分析工具和技术,并提供相关的代码示例。
1. CPU使用率分析
CPU使用率是衡量app性能的重要指标之一。通过分析CPU使用率,我们可以了解到app在运行过程中是否存在高CPU占用的情况,从而找出可能导致性能问题的代码段。
示例代码:
import UIKit
func calculatePrimes() {
var primes: [Int] = []
for number in 2...10000 {
var isPrime = true
for i in 2..<number {
if number % i == 0 {
isPrime = false
break
}
}
if isPrime {
primes.append(number)
}
}
print(primes)
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
calculatePrimes()
}
}
上述示例代码中的 calculatePrimes 函数用于计算2到10000之间的所有质数。为了分析CPU使用率,我们可以通过Xcode中的Instruments工具来进行。
- 在Xcode中,选择"Product" -> “Profile” -> “Instruments”。
- 在Instruments面板中,选择"CPU Usage"。
- 点击红色的录制按钮,然后运行app。
- 在app运行的过程中,Instruments会记录CPU的使用情况。
- 结束录制后,我们可以分析记录的数据,找出CPU占用较高的时刻和对应的代码。
除了Xcode自带的工具,开发者还可以使用第三方工具如KeyMob进行更全面的iOS性能监控。KeyMob提供实时的CPU、GPU、内存、FPS等性能指标监控,并支持图表展示,帮助开发者更方便地分析应用性能。
2. 内存使用分析
内存使用是另一个重要的性能指标。一个高效的app应该在使用内存方面做到合理分配和及时释放,避免内存泄漏和过度消耗。
示例代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var array: [Int] = []
for i in 0..<100000 {
array.append(i)
}
print(array)
}
}
上述示例代码中,我们创建了一个包含十万个整数的数组。为了分析内存使用情况,我们可以使用Xcode中的"Debug Memory Graph"工具。
- 在Xcode中,选择"Product" -> “Profile” -> “Debug Memory Graph”。
- 运行app,并在界面上进行一些操作。
- 可以看到当前内存使用的情况,包括内存泄漏的对象等。
对于内存管理,KeyMob不仅支持文件管理,还能监控内存使用情况,帮助开发者识别内存泄漏和优化内存分配。KeyMob的IOS性能监控功能包括实时内存监控,方便开发者追踪应用资源消耗。
3. 网络请求分析
网络请求是很多app中常见的操作,因此网络性能对于app的整体性能也有着重要的影响。我们可以使用一些工具来分析网络请求的性能,比如Charles等。
示例代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error {
print("Error: \(error)")
} else if let data = data {
print("Data: \(data)")
}
}
task.resume()
}
}
上述示例代码中,我们使用 URLSession 来进行网络请求。为了分析网络请求的性能,我们可以使用Charles等工具来进行抓包分析。
- 在Charles中启动抓包功能。
- 运行app,并进行网络请求。
- 在Charles中可以看到抓包的结果,包括请求和响应的数据。
通过分析抓包的结果,我们可以了解到网络请求的耗时、数据大小等信息,从而优化网络请求的性能。KeyMob也提供网络性能监控功能,可以实时查看网络请求的耗时和能耗,辅助开发者优化网络交互。此外,KeyMob还支持查看手机使用记录和能耗分析,帮助全面评估应用性能。
总结
iOS app性能分析是对于开发者而言非常重要的一项工作。通过分析CPU使用率、内存使用情况和网络请求性能,我们可以找出潜在的性能问题并进行优化。结合工具如Xcode Instruments、Debug Memory Graph、Charles以及KeyMob等,开发者可以更高效地进行性能监控和优化,提升应用质量。'''
'''
iOS app性能分析
iOS app的性能是用户体验的重要指标之一。一个高性能的app能够快速响应用户的操作,流畅地展示内容,并且减少耗电量和资源占用。因此,开发者需要对自己的app进行性能分析,找出潜在的性能问题并进行优化。本文将介绍一些常见的iOS app性能分析工具和技术,并提供相关的代码示例。
1. CPU使用率分析
CPU使用率是衡量app性能的重要指标之一。通过分析CPU使用率,我们可以了解到app在运行过程中是否存在高CPU占用的情况,从而找出可能导致性能问题的代码段。
示例代码:
import UIKit
func calculatePrimes() {
var primes: [Int] = []
for number in 2...10000 {
var isPrime = true
for i in 2..<number {
if number % i == 0 {
isPrime = false
break
}
}
if isPrime {
primes.append(number)
}
}
print(primes)
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
calculatePrimes()
}
}
上述示例代码中的 calculatePrimes 函数用于计算2到10000之间的所有质数。为了分析CPU使用率,我们可以通过Xcode中的Instruments工具来进行。
- 在Xcode中,选择"Product" -> “Profile” -> “Instruments”。
- 在Instruments面板中,选择"CPU Usage"。
- 点击红色的录制按钮,然后运行app。
- 在app运行的过程中,Instruments会记录CPU的使用情况。
- 结束录制后,我们可以分析记录的数据,找出CPU占用较高的时刻和对应的代码。
除了Xcode自带的工具,开发者还可以使用第三方工具如KeyMob进行更全面的iOS性能监控。KeyMob提供实时的CPU、GPU、内存、FPS等性能指标监控,并支持图表展示,帮助开发者更方便地分析应用性能。
2. 内存使用分析
内存使用是另一个重要的性能指标。一个高效的app应该在使用内存方面做到合理分配和及时释放,避免内存泄漏和过度消耗。
示例代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var array: [Int] = []
for i in 0..<100000 {
array.append(i)
}
print(array)
}
}
上述示例代码中,我们创建了一个包含十万个整数的数组。为了分析内存使用情况,我们可以使用Xcode中的"Debug Memory Graph"工具。
- 在Xcode中,选择"Product" -> “Profile” -> “Debug Memory Graph”。
- 运行app,并在界面上进行一些操作。
- 可以看到当前内存使用的情况,包括内存泄漏的对象等。
对于内存管理,KeyMob不仅支持文件管理,还能监控内存使用情况,帮助开发者识别内存泄漏和优化内存分配。KeyMob的IOS性能监控功能包括实时内存监控,方便开发者追踪应用资源消耗。
3. 网络请求分析
网络请求是很多app中常见的操作,因此网络性能对于app的整体性能也有着重要的影响。我们可以使用一些工具来分析网络请求的性能,比如Charles等。
示例代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error {
print("Error: \(error)")
} else if let data = data {
print("Data: \(data)")
}
}
task.resume()
}
}
上述示例代码中,我们使用 URLSession 来进行网络请求。为了分析网络请求的性能,我们可以使用Charles等工具来进行抓包分析。
- 在Charles中启动抓包功能。
- 运行app,并进行网络请求。
- 在Charles中可以看到抓包的结果,包括请求和响应的数据。
通过分析抓包的结果,我们可以了解到网络请求的耗时、数据大小等信息,从而优化网络请求的性能。KeyMob也提供网络性能监控功能,可以实时查看网络请求的耗时和能耗,辅助开发者优化网络交互。此外,KeyMob还支持查看手机使用记录和能耗分析,帮助全面评估应用性能。
总结
iOS app性能分析是对于开发者而言非常重要的一项工作。通过分析CPU使用率、内存使用情况和网络请求性能,我们可以找出潜在的性能问题并进行优化。结合工具如Xcode Instruments、Debug Memory Graph、Charles以及KeyMob等,开发者可以更高效地进行性能监控和优化,提升应用质量。'''
收起阅读 »详细指南:如何解决iOS社交应用被4.3(b)审核拒绝并成功上架App Store
'''
我公司是做社交应用的,收益较为可观,但是不幸的是,不知道什么原因突然被下架了
经过几天的分析, 可能是由于关联所致, 那么接下来要面临的问题就是使用这套代码重新上架
有过iOS开发经验的都会知道,第一步需要做的就是代码混淆,而且我对混淆有较多的经验,感觉这难不倒我,经过两天的混淆后,提交了一个1.0版本.
对于iOS应用上架,使用AppUploader可以简化IPA上传和证书管理流程,支持在Windows、Mac或Linux系统中操作,无需Mac电脑,比传统方法更高效。
进入审核速度较快, 大概是凌晨左右就近入了 In Reiview 状态, 心惊胆战的等待了很久, 很遗憾 ,看到了苹果回复邮件的标题我就知道 , 审核被拒, 颤抖的打开邮件, 心中期望千万不要4.3
不期所望,还真给了4.3, 不过本次4.3与以往的4.3有所不同, 后面跟了一个(b),经查询得知,现在的4.3进行了区分,分为a和b两种
a:是由于代码或元数据相似度过高产生. 可通过代码混淆解决
b:是于市场接受了过多的社交应用,导致的本问题产生.被判定为 4.3(b). 此问题解决较为困难, 没有创意和新意的社交应用目前大都会遇到4.3(b), 不过也要看运气.
那么说明我的代码混淆过关了, 但是类型问题导致了4.3(b), 接下来的几天我与苹果公司进行了交涉. 来来回回 回复的多封邮件说明了我们软件的特点,与众不同,但是并没有撼动苹果对本次判定的决心
我前思后想,查找了大量相关文章,没有得到解决, 就在不知道怎么办的时候, 灵光一现, 我想到了一个方法
我第二天很早跑到公司 立马尝试了一下,提交了一个版本,同样的时间大概是凌晨左右进入审核,我心惊胆战,经过了30分钟的煎熬的等待后,苹果回复了那个熟悉的文案,我的心跳达到了120转/分钟
Congratulations!
Your submission was accepted for release on the App Store
AppUploader等工具还能帮助批量上传应用截图和本地化信息,优化上架流程,提高审核通过率。
后续迭代了几个版本, 最后都上架成功, 后来我对此方法进行了整理,优化, 目前已经在公司大面积使用且全部上架,并且制作多个马甲包备用
如果你也遇到了这个问题不知道怎么解决, 可以分享给你我的方法, 尤其对社交应用较为奏效'''
'''
我公司是做社交应用的,收益较为可观,但是不幸的是,不知道什么原因突然被下架了
经过几天的分析, 可能是由于关联所致, 那么接下来要面临的问题就是使用这套代码重新上架
有过iOS开发经验的都会知道,第一步需要做的就是代码混淆,而且我对混淆有较多的经验,感觉这难不倒我,经过两天的混淆后,提交了一个1.0版本.
对于iOS应用上架,使用AppUploader可以简化IPA上传和证书管理流程,支持在Windows、Mac或Linux系统中操作,无需Mac电脑,比传统方法更高效。
进入审核速度较快, 大概是凌晨左右就近入了 In Reiview 状态, 心惊胆战的等待了很久, 很遗憾 ,看到了苹果回复邮件的标题我就知道 , 审核被拒, 颤抖的打开邮件, 心中期望千万不要4.3
不期所望,还真给了4.3, 不过本次4.3与以往的4.3有所不同, 后面跟了一个(b),经查询得知,现在的4.3进行了区分,分为a和b两种
a:是由于代码或元数据相似度过高产生. 可通过代码混淆解决
b:是于市场接受了过多的社交应用,导致的本问题产生.被判定为 4.3(b). 此问题解决较为困难, 没有创意和新意的社交应用目前大都会遇到4.3(b), 不过也要看运气.
那么说明我的代码混淆过关了, 但是类型问题导致了4.3(b), 接下来的几天我与苹果公司进行了交涉. 来来回回 回复的多封邮件说明了我们软件的特点,与众不同,但是并没有撼动苹果对本次判定的决心
我前思后想,查找了大量相关文章,没有得到解决, 就在不知道怎么办的时候, 灵光一现, 我想到了一个方法
我第二天很早跑到公司 立马尝试了一下,提交了一个版本,同样的时间大概是凌晨左右进入审核,我心惊胆战,经过了30分钟的煎熬的等待后,苹果回复了那个熟悉的文案,我的心跳达到了120转/分钟
Congratulations!
Your submission was accepted for release on the App Store
AppUploader等工具还能帮助批量上传应用截图和本地化信息,优化上架流程,提高审核通过率。
后续迭代了几个版本, 最后都上架成功, 后来我对此方法进行了整理,优化, 目前已经在公司大面积使用且全部上架,并且制作多个马甲包备用
如果你也遇到了这个问题不知道怎么解决, 可以分享给你我的方法, 尤其对社交应用较为奏效'''
收起阅读 »输入组件获焦时实现提示文本的动态效果
问题现象
在使用TextInput和TextArea组件进行输入时,如何实现提示文字的动态效果?
背景知识
组件的某些通用属性变化时,可以通过animation属性动画实现渐变过渡效果,提升用户体验。
TextInput是HarmonyOS提供的一种单行文本输入框组件。
TextArea是多行文本输入框组件,当输入的文本内容超过组件宽度时会自动换行显示。
attributeModifier接口支持动态属性设置,支持开发者在属性设置时使用if/else语法,且根据需要使用多态样式设置属性。
解决方案
在Stack组件中创建Text提示文本和TextInput输入框。
创建TextModifier类,实现attributeModifier接口,该类中的applyNormalAttribute方法可根据对应的boolean类型的值动态修改对应组件的样式属性。
当点击TextInput组件时,执行onFocus方法,若文本值为空,将对应的TextModifier类的属性装载至该组件上,从而实现animation动画效果。
完整示例参考如下:
@Entry
@Component
struct TextInputTestOne {
// 定义状态变量
message: string = 'Hello World';
textAreaValue: string = '';
textInputValue: string = '';
selectValue: string = '';
@State modifierOne: TextModifier = new TextModifier(50, 150, 20);
@State modifierTwo: TextModifier = new TextModifier(50, 0, 20);
opacityOne: number = 0.3;
opacityTwo: number = 0.3;
build() {
Column() {
Column() {
Stack() {
Text('请在这输入文字:')
.fontSize(this.modifierOne.fontSize)
.textAlign(TextAlign.Start)
.fontColor(Color.Black)
.margin({ left: this.modifierOne.marginLeft, bottom: this.modifierOne.marginBottom })
.attributeModifier(this.modifierOne)
.width('100%')
.opacity(this.opacityOne)
// 实现动画效果
.animation({
duration: 300,
curve: Curve.Friction,
iterations: 1,
playMode: PlayMode.Normal
});
TextArea({ text: $$this.textAreaValue })
// 聚焦回调时间
.onFocus(() => {
if (this.textAreaValue === '') {
this.modifierOne.isFocus = true;
this.opacityOne = 1;
}
})
// 失焦回调事件
.onBlur(() => {
if (this.textAreaValue === '') {
this.modifierOne.isFocus = false;
this.opacityOne = 0.3;
}
})
.height('80%');
};
}
.justifyContent(FlexAlign.Center)
.height('30%')
.margin({ left: 20, right: 20 });
Column() {
Stack() {
Text('请在这输入文字:')
.fontSize(this.modifierTwo.fontSize)
.fontColor(Color.Black)
.textAlign(TextAlign.Start)
.width('100%')
.margin({ left: this.modifierTwo.marginLeft, bottom: this.modifierTwo.marginBottom })
.attributeModifier(this.modifierTwo)
.opacity(this.opacityTwo)
// 实现动画效果
.animation({
duration: 300,
curve: Curve.Friction,
iterations: 1,
playMode: PlayMode.Normal
});
TextInput({ text: $$this.textInputValue })
// 聚焦回调时间
.onFocus(() => {
if (this.textInputValue === '') {
this.modifierTwo.isFocus = true;
this.opacityTwo = 1;
}
})
// 失焦回调事件
.onBlur(() => {
if (this.textInputValue === '') {
this.modifierTwo.isFocus = false;
this.opacityTwo = 0.3;
}
});
};
}
.justifyContent(FlexAlign.Center)
.height('30%')
.margin({ left: 20, right: 20 });
}
.height('100%')
.width('100%');
}
}
class TextModifier implements AttributeModifier<TextAttribute> {
isFocus: boolean | undefined = undefined;
marginLeft: number = 0;
marginBottom: number = 0;
fontSize: number = 0;
// 绑定属性
applyNormalAttribute(instance: TextAttribute): void {
if (this.isFocus) {
this.marginLeft -= 10;
this.marginBottom += 40;
this.fontSize -= 7;
instance
.fontSize(this.fontSize)
.margin({ left: this.marginLeft, bottom: this.marginBottom });
} else if (this.isFocus === false) {
this.marginLeft += 10;
this.marginBottom -= 40;
this.fontSize += 7;
instance
.fontSize(this.fontSize)
.margin({ left: this.marginLeft, bottom: this.marginBottom });
}
}
constructor(marginLeft: number, marginBottom: number, fontSize: number) {
this.marginLeft = marginLeft;
this.marginBottom = marginBottom;
this.fontSize = fontSize;
}
}
https://coub.com/view/4b7gqt
https://coub.com/view/4b7gqq
https://coub.com/view/4b7gql
https://coub.com/view/4b7gqi
https://coub.com/view/4b7gqg
https://coub.com/view/4b7gqe
https://coub.com/view/4b7gqb
https://coub.com/view/4b7gq9
https://coub.com/view/4b7gq6
https://coub.com/view/4b7gq3
https://coub.com/view/4b7gq1
https://coub.com/view/4b7gpz
https://coub.com/view/4b7gpw
https://coub.com/view/4b7gpe
https://coub.com/view/4b7gpa
https://coub.com/view/4b7gp8
https://coub.com/view/4b7gp7
https://coub.com/view/4b7gp6
https://coub.com/view/4b7gp5
https://coub.com/view/4b7gp1
https://coub.com/view/4b7gow
https://coub.com/view/4b7gou
https://coub.com/view/4b7goq
https://coub.com/view/4b7goj
https://coub.com/view/4b7goh
https://coub.com/view/4b7gog
https://coub.com/view/4b7go2
https://coub.com/view/4b7gnz
https://coub.com/view/4b7gnf
https://coub.com/view/4b7gne
https://coub.com/view/4b7gnd
https://coub.com/view/4b7gnb
https://coub.com/view/4b7gmd
https://coub.com/view/4b7gmb
https://coub.com/view/4b7gm6
https://coub.com/view/4b7gm1
https://coub.com/view/4b7glo
https://coub.com/view/4b7glk
https://coub.com/view/4b7glh
https://coub.com/view/4b7glg
https://coub.com/view/4b7glf
https://coub.com/view/4b7glc
https://coub.com/view/4b7glb
https://coub.com/view/4b7gl9
https://coub.com/view/4b7gl4
https://coub.com/view/4b7gl1
https://coub.com/view/4b7gkp
https://coub.com/view/4b7gkh
https://coub.com/view/4b7gk8
https://coub.com/view/4b7gk2
https://coub.com/view/4b7gju
https://coub.com/view/4b7gjn
https://coub.com/view/4b7gjl
https://coub.com/view/4b7gjj
https://coub.com/view/4b7gji
https://coub.com/view/4b7gjg
https://coub.com/view/4b7gjf
https://coub.com/view/4b7gje
https://coub.com/view/4b7gjb
https://coub.com/view/4b7gj7
https://coub.com/view/4b7gj5
https://coub.com/view/4b7gj2
https://coub.com/view/4b7giy
https://coub.com/view/4b7giw
https://coub.com/view/4b7giv
https://coub.com/view/4b7giu
https://coub.com/view/4b7gis
https://coub.com/view/4b7giq
https://coub.com/view/4b7gio
https://coub.com/view/4b7gil
https://coub.com/view/4b7gij
https://coub.com/view/4b7gie
https://coub.com/view/4b7gic
https://coub.com/view/4b7gia
https://coub.com/view/4b7gi7
https://coub.com/view/4b7gi2
https://coub.com/view/4b7ghx
https://coub.com/view/4b7ghi
https://coub.com/view/4b7ghh
https://coub.com/view/4b7ghd
https://coub.com/view/4b7ggo
https://coub.com/view/4b7ggn
https://coub.com/view/4b7ggl
https://coub.com/view/4b7ggg
https://coub.com/view/4b7gge
https://coub.com/view/4b7ggd
https://coub.com/view/4b7ggc
https://coub.com/view/4b7ggb
https://coub.com/view/4b7gga
https://coub.com/view/4b7gg7
https://coub.com/view/4b7gg1
https://coub.com/view/4b7gfw
https://coub.com/view/4b7gft
https://coub.com/view/4b7gfs
https://coub.com/view/4b7gfq
https://coub.com/view/4b7gfm
https://coub.com/view/4b77kh
https://coub.com/view/4b77kd
https://coub.com/view/4b77ka
https://coub.com/view/4b77k8
https://coub.com/view/4b77k4
https://coub.com/view/4b77k2
https://coub.com/view/4b77k0
https://coub.com/view/4b77jz
https://coub.com/view/4b77ju
https://coub.com/view/4b77jk
https://coub.com/view/4b77jf
https://coub.com/view/4b77ja
https://coub.com/view/4b77j1
https://coub.com/view/4b77iz
问题现象
在使用TextInput和TextArea组件进行输入时,如何实现提示文字的动态效果?
背景知识
组件的某些通用属性变化时,可以通过animation属性动画实现渐变过渡效果,提升用户体验。
TextInput是HarmonyOS提供的一种单行文本输入框组件。
TextArea是多行文本输入框组件,当输入的文本内容超过组件宽度时会自动换行显示。
attributeModifier接口支持动态属性设置,支持开发者在属性设置时使用if/else语法,且根据需要使用多态样式设置属性。
解决方案
在Stack组件中创建Text提示文本和TextInput输入框。
创建TextModifier类,实现attributeModifier接口,该类中的applyNormalAttribute方法可根据对应的boolean类型的值动态修改对应组件的样式属性。
当点击TextInput组件时,执行onFocus方法,若文本值为空,将对应的TextModifier类的属性装载至该组件上,从而实现animation动画效果。
完整示例参考如下:
@Entry
@Component
struct TextInputTestOne {
// 定义状态变量
message: string = 'Hello World';
textAreaValue: string = '';
textInputValue: string = '';
selectValue: string = '';
@State modifierOne: TextModifier = new TextModifier(50, 150, 20);
@State modifierTwo: TextModifier = new TextModifier(50, 0, 20);
opacityOne: number = 0.3;
opacityTwo: number = 0.3;
build() {
Column() {
Column() {
Stack() {
Text('请在这输入文字:')
.fontSize(this.modifierOne.fontSize)
.textAlign(TextAlign.Start)
.fontColor(Color.Black)
.margin({ left: this.modifierOne.marginLeft, bottom: this.modifierOne.marginBottom })
.attributeModifier(this.modifierOne)
.width('100%')
.opacity(this.opacityOne)
// 实现动画效果
.animation({
duration: 300,
curve: Curve.Friction,
iterations: 1,
playMode: PlayMode.Normal
});
TextArea({ text: $$this.textAreaValue })
// 聚焦回调时间
.onFocus(() => {
if (this.textAreaValue === '') {
this.modifierOne.isFocus = true;
this.opacityOne = 1;
}
})
// 失焦回调事件
.onBlur(() => {
if (this.textAreaValue === '') {
this.modifierOne.isFocus = false;
this.opacityOne = 0.3;
}
})
.height('80%');
};
}
.justifyContent(FlexAlign.Center)
.height('30%')
.margin({ left: 20, right: 20 });
Column() {
Stack() {
Text('请在这输入文字:')
.fontSize(this.modifierTwo.fontSize)
.fontColor(Color.Black)
.textAlign(TextAlign.Start)
.width('100%')
.margin({ left: this.modifierTwo.marginLeft, bottom: this.modifierTwo.marginBottom })
.attributeModifier(this.modifierTwo)
.opacity(this.opacityTwo)
// 实现动画效果
.animation({
duration: 300,
curve: Curve.Friction,
iterations: 1,
playMode: PlayMode.Normal
});
TextInput({ text: $$this.textInputValue })
// 聚焦回调时间
.onFocus(() => {
if (this.textInputValue === '') {
this.modifierTwo.isFocus = true;
this.opacityTwo = 1;
}
})
// 失焦回调事件
.onBlur(() => {
if (this.textInputValue === '') {
this.modifierTwo.isFocus = false;
this.opacityTwo = 0.3;
}
});
};
}
.justifyContent(FlexAlign.Center)
.height('30%')
.margin({ left: 20, right: 20 });
}
.height('100%')
.width('100%');
}
}
class TextModifier implements AttributeModifier<TextAttribute> {
isFocus: boolean | undefined = undefined;
marginLeft: number = 0;
marginBottom: number = 0;
fontSize: number = 0;
// 绑定属性
applyNormalAttribute(instance: TextAttribute): void {
if (this.isFocus) {
this.marginLeft -= 10;
this.marginBottom += 40;
this.fontSize -= 7;
instance
.fontSize(this.fontSize)
.margin({ left: this.marginLeft, bottom: this.marginBottom });
} else if (this.isFocus === false) {
this.marginLeft += 10;
this.marginBottom -= 40;
this.fontSize += 7;
instance
.fontSize(this.fontSize)
.margin({ left: this.marginLeft, bottom: this.marginBottom });
}
}
constructor(marginLeft: number, marginBottom: number, fontSize: number) {
this.marginLeft = marginLeft;
this.marginBottom = marginBottom;
this.fontSize = fontSize;
}
}
https://coub.com/view/4b7gqt
https://coub.com/view/4b7gqq
https://coub.com/view/4b7gql
https://coub.com/view/4b7gqi
https://coub.com/view/4b7gqg
https://coub.com/view/4b7gqe
https://coub.com/view/4b7gqb
https://coub.com/view/4b7gq9
https://coub.com/view/4b7gq6
https://coub.com/view/4b7gq3
https://coub.com/view/4b7gq1
https://coub.com/view/4b7gpz
https://coub.com/view/4b7gpw
https://coub.com/view/4b7gpe
https://coub.com/view/4b7gpa
https://coub.com/view/4b7gp8
https://coub.com/view/4b7gp7
https://coub.com/view/4b7gp6
https://coub.com/view/4b7gp5
https://coub.com/view/4b7gp1
https://coub.com/view/4b7gow
https://coub.com/view/4b7gou
https://coub.com/view/4b7goq
https://coub.com/view/4b7goj
https://coub.com/view/4b7goh
https://coub.com/view/4b7gog
https://coub.com/view/4b7go2
https://coub.com/view/4b7gnz
https://coub.com/view/4b7gnf
https://coub.com/view/4b7gne
https://coub.com/view/4b7gnd
https://coub.com/view/4b7gnb
https://coub.com/view/4b7gmd
https://coub.com/view/4b7gmb
https://coub.com/view/4b7gm6
https://coub.com/view/4b7gm1
https://coub.com/view/4b7glo
https://coub.com/view/4b7glk
https://coub.com/view/4b7glh
https://coub.com/view/4b7glg
https://coub.com/view/4b7glf
https://coub.com/view/4b7glc
https://coub.com/view/4b7glb
https://coub.com/view/4b7gl9
https://coub.com/view/4b7gl4
https://coub.com/view/4b7gl1
https://coub.com/view/4b7gkp
https://coub.com/view/4b7gkh
https://coub.com/view/4b7gk8
https://coub.com/view/4b7gk2
https://coub.com/view/4b7gju
https://coub.com/view/4b7gjn
https://coub.com/view/4b7gjl
https://coub.com/view/4b7gjj
https://coub.com/view/4b7gji
https://coub.com/view/4b7gjg
https://coub.com/view/4b7gjf
https://coub.com/view/4b7gje
https://coub.com/view/4b7gjb
https://coub.com/view/4b7gj7
https://coub.com/view/4b7gj5
https://coub.com/view/4b7gj2
https://coub.com/view/4b7giy
https://coub.com/view/4b7giw
https://coub.com/view/4b7giv
https://coub.com/view/4b7giu
https://coub.com/view/4b7gis
https://coub.com/view/4b7giq
https://coub.com/view/4b7gio
https://coub.com/view/4b7gil
https://coub.com/view/4b7gij
https://coub.com/view/4b7gie
https://coub.com/view/4b7gic
https://coub.com/view/4b7gia
https://coub.com/view/4b7gi7
https://coub.com/view/4b7gi2
https://coub.com/view/4b7ghx
https://coub.com/view/4b7ghi
https://coub.com/view/4b7ghh
https://coub.com/view/4b7ghd
https://coub.com/view/4b7ggo
https://coub.com/view/4b7ggn
https://coub.com/view/4b7ggl
https://coub.com/view/4b7ggg
https://coub.com/view/4b7gge
https://coub.com/view/4b7ggd
https://coub.com/view/4b7ggc
https://coub.com/view/4b7ggb
https://coub.com/view/4b7gga
https://coub.com/view/4b7gg7
https://coub.com/view/4b7gg1
https://coub.com/view/4b7gfw
https://coub.com/view/4b7gft
https://coub.com/view/4b7gfs
https://coub.com/view/4b7gfq
https://coub.com/view/4b7gfm
https://coub.com/view/4b77kh
https://coub.com/view/4b77kd
https://coub.com/view/4b77ka
https://coub.com/view/4b77k8
https://coub.com/view/4b77k4
https://coub.com/view/4b77k2
https://coub.com/view/4b77k0
https://coub.com/view/4b77jz
https://coub.com/view/4b77ju
https://coub.com/view/4b77jk
https://coub.com/view/4b77jf
https://coub.com/view/4b77ja
https://coub.com/view/4b77j1
https://coub.com/view/4b77iz
UI布局默认是多少vp为基准,以达到不同机器自适应
无论屏幕分辨率或密度如何,组件的视觉效果保持一致。
vp具体计算公式为:vp= px/(DPI/160)
px 是屏幕的真实物理像素值,densityDPI 通常指系统屏幕密度,densityPixels是屏幕密度与标准DPI的比率,常见取值有 0.75、1.0、1.5、2.0、3.0 等。在HarmonyOS中,标准DPI为160。以华为Mate 40 Pro为例,densityDPI为 560,densityPixels为3.5。要查看真机的DPI,可以调用屏幕属性中的display接口查询。
import { display } from '@kit.ArkUI';
let displayClass: display.Display | null = null;
try {
displayClass = display.getDefaultDisplaySync();
} catch (exception) {
console.error('Failed to obtain the default display object. Code: ' + JSON.stringify(exception));
}
AdaptiveForDifferentmMachines.ets
如果原型图没有提供vp单位的布局,开发者可以根据densityPixels把px转为vp,HarmonyOS也封装了现成的接口px2vp()和vp2px()供开发者直接调用。
参考链接
像素单位,@ohos.display (屏幕属性)
https://coub.com/view/4b77kh
https://coub.com/view/4b77kd
https://coub.com/view/4b77ka
https://coub.com/view/4b77k8
https://coub.com/view/4b77k4
https://coub.com/view/4b77k2
https://coub.com/view/4b77k0
https://coub.com/view/4b77jz
https://coub.com/view/4b77ju
https://coub.com/view/4b77jk
https://coub.com/view/4b77jf
https://coub.com/view/4b77ja
https://coub.com/view/4b77j1
https://coub.com/view/4b77iz
https://coub.com/stories/5089704-jcu
https://coub.com/stories/5089703-griffith
https://coub.com/stories/5089702-uq
https://coub.com/stories/5089700-swinburne
https://coub.com/view/4b73k6
https://coub.com/view/4b73k4
https://coub.com/view/4b73k3
https://coub.com/view/4b73k2
https://coub.com/view/4b73k1
https://coub.com/view/4b73jz
https://coub.com/view/4b73jw
https://coub.com/view/4b73jv
https://coub.com/view/4b73js
https://coub.com/view/4b73jp
https://coub.com/view/4b73jm
https://coub.com/view/4b73jj
https://coub.com/view/4b73ji
https://coub.com/view/4b73jh
https://coub.com/view/4b73jg
https://coub.com/sources/19155990
https://coub.com/sources/19155989
https://coub.com/sources/19155987
https://coub.com/view/4b73j9
https://coub.com/view/4b73j7
https://coub.com/stories/5089696-victoria
https://coub.com/stories/5089695-flinders
https://coub.com/stories/5089694-unisa
https://coub.com/stories/5089693-uc
https://coub.com/stories/5089691-cdu
https://coub.com/stories/5089689-curtin
https://coub.com/stories/5089687-bond
https://coub.com/stories/5089686-jcu
https://coub.com/stories/5089684-cqu
https://coub.com/view/4b707t
https://coub.com/view/4b707r
https://coub.com/view/4b707o
https://coub.com/view/4b707n
https://coub.com/view/4b707h
https://coub.com/view/4b707d
https://coub.com/view/4b7077
https://coub.com/view/4b7074
https://coub.com/view/4b706z
https://coub.com/view/4b706u
https://coub.com/view/4b706o
https://coub.com/view/4b706c
https://coub.com/view/4b7063
https://coub.com/view/4b7060
https://coub.com/view/4b705v
https://coub.com/view/4b705n
https://coub.com/view/4b705k
https://coub.com/view/4b701z
https://coub.com/view/4b6zjc
https://coub.com/view/4b6zja
https://coub.com/view/4b6zj9
https://coub.com/view/4b6zj6
https://coub.com/view/4b6zj4
https://coub.com/view/4b6zj3
https://coub.com/view/4b6zj2
https://coub.com/view/4b6zj1
https://coub.com/view/4b6ziz
https://coub.com/view/4b6zix
https://coub.com/view/4b6ziv
https://coub.com/view/4b6ziu
https://coub.com/view/4b6zit
https://coub.com/view/4b6zis
https://coub.com/view/4b6zio
https://coub.com/view/4b6zin
https://coub.com/view/4b6zim
https://coub.com/view/4b6zik
无论屏幕分辨率或密度如何,组件的视觉效果保持一致。
vp具体计算公式为:vp= px/(DPI/160)
px 是屏幕的真实物理像素值,densityDPI 通常指系统屏幕密度,densityPixels是屏幕密度与标准DPI的比率,常见取值有 0.75、1.0、1.5、2.0、3.0 等。在HarmonyOS中,标准DPI为160。以华为Mate 40 Pro为例,densityDPI为 560,densityPixels为3.5。要查看真机的DPI,可以调用屏幕属性中的display接口查询。
import { display } from '@kit.ArkUI';
let displayClass: display.Display | null = null;
try {
displayClass = display.getDefaultDisplaySync();
} catch (exception) {
console.error('Failed to obtain the default display object. Code: ' + JSON.stringify(exception));
}
AdaptiveForDifferentmMachines.ets
如果原型图没有提供vp单位的布局,开发者可以根据densityPixels把px转为vp,HarmonyOS也封装了现成的接口px2vp()和vp2px()供开发者直接调用。
参考链接
像素单位,@ohos.display (屏幕属性)
https://coub.com/view/4b77kh
https://coub.com/view/4b77kd
https://coub.com/view/4b77ka
https://coub.com/view/4b77k8
https://coub.com/view/4b77k4
https://coub.com/view/4b77k2
https://coub.com/view/4b77k0
https://coub.com/view/4b77jz
https://coub.com/view/4b77ju
https://coub.com/view/4b77jk
https://coub.com/view/4b77jf
https://coub.com/view/4b77ja
https://coub.com/view/4b77j1
https://coub.com/view/4b77iz
https://coub.com/stories/5089704-jcu
https://coub.com/stories/5089703-griffith
https://coub.com/stories/5089702-uq
https://coub.com/stories/5089700-swinburne
https://coub.com/view/4b73k6
https://coub.com/view/4b73k4
https://coub.com/view/4b73k3
https://coub.com/view/4b73k2
https://coub.com/view/4b73k1
https://coub.com/view/4b73jz
https://coub.com/view/4b73jw
https://coub.com/view/4b73jv
https://coub.com/view/4b73js
https://coub.com/view/4b73jp
https://coub.com/view/4b73jm
https://coub.com/view/4b73jj
https://coub.com/view/4b73ji
https://coub.com/view/4b73jh
https://coub.com/view/4b73jg
https://coub.com/sources/19155990
https://coub.com/sources/19155989
https://coub.com/sources/19155987
https://coub.com/view/4b73j9
https://coub.com/view/4b73j7
https://coub.com/stories/5089696-victoria
https://coub.com/stories/5089695-flinders
https://coub.com/stories/5089694-unisa
https://coub.com/stories/5089693-uc
https://coub.com/stories/5089691-cdu
https://coub.com/stories/5089689-curtin
https://coub.com/stories/5089687-bond
https://coub.com/stories/5089686-jcu
https://coub.com/stories/5089684-cqu
https://coub.com/view/4b707t
https://coub.com/view/4b707r
https://coub.com/view/4b707o
https://coub.com/view/4b707n
https://coub.com/view/4b707h
https://coub.com/view/4b707d
https://coub.com/view/4b7077
https://coub.com/view/4b7074
https://coub.com/view/4b706z
https://coub.com/view/4b706u
https://coub.com/view/4b706o
https://coub.com/view/4b706c
https://coub.com/view/4b7063
https://coub.com/view/4b7060
https://coub.com/view/4b705v
https://coub.com/view/4b705n
https://coub.com/view/4b705k
https://coub.com/view/4b701z
https://coub.com/view/4b6zjc
https://coub.com/view/4b6zja
https://coub.com/view/4b6zj9
https://coub.com/view/4b6zj6
https://coub.com/view/4b6zj4
https://coub.com/view/4b6zj3
https://coub.com/view/4b6zj2
https://coub.com/view/4b6zj1
https://coub.com/view/4b6ziz
https://coub.com/view/4b6zix
https://coub.com/view/4b6ziv
https://coub.com/view/4b6ziu
https://coub.com/view/4b6zit
https://coub.com/view/4b6zis
https://coub.com/view/4b6zio
https://coub.com/view/4b6zin
https://coub.com/view/4b6zim
https://coub.com/view/4b6zik
如何解决Navigation路由调用pop后onPop回调代码不执行的问题
问题现象
使用Navigation构建路由,从pageOne通过pushPath跳转到pageTwo,期望pageOne的onPop回调在pageTwo返回时被触发,但效果未达预期。
问题代码示例参考如下:
class ParamWithOp {
operation: number = 1
count: number = 10
}
@Entry
@Component
struct PageOne {
pageInfo: NavPathStack = new NavPathStack();
@State message: string = 'Hello World'
@Builder
pageMap(name: string, params: Object) {
if (name === 'pageTwo') {
PageTwo()
}
}
build() {
Navigation(this.pageInfo) {
Column() {
Text(this.message)
.width('80%')
.height(50)
.margin(10)
Button('pushPath', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(10)
.onClick(() => {
// 将name指定的NavDestination页面信息入栈,传递的数据为param,添加接收处理结果的onPop回调。
this.pageInfo.pushPath({
name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
this.message = `[pushPath]last page is: ${popInfo.info.name} result: ${JSON.stringify(popInfo.result)}`
}
});
})
}.width('100%').height('100%')
}.navDestination(this.pageMap)
.title('pageOne')
}
}
@Component
struct PageTwo {
pathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Column() {
Button('pop', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
// 回退到上一个页面,此处代码,在pop回pageOne页面时,未传参数
this.pathStack.pop();
})
}.width('100%').height('100%')
}.title('pageTwo')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
效果预览
点击放大
背景知识
Navigation组件是路由导航的根视图容器,结合导航控制器NavPathStack可实现组件导航。
pushPath:将info指定的NavDestination页面信息入栈。可设置onPop回调函数来接收参数。
pop:弹出路由栈栈顶元素,并触发onPop回调传入页面处理结果。
问题定位
点击放大
查阅官方文档关于pushPath方法的NavPathInfo入参说明,其中的onPop回调函数仅pop、popToName、popToIndex中设置result参数后触发。
分析结论
onPop回调函数需要使用pop、popToName、popToIndex方法返回时设置result参数才会触发,否则不会执行onPop回调。
修改建议
按上节所述,只需在pageTwo中调用pop方法时,传入result参数,即可在pageOne中成功收到onPop的回调。修改问题代码如下:
// 回退到上一个页面,随便传个result即可触发onPop回调
this.pathStack.pop(1);
修改后的运行效果参见效果预览,可以看到,当pageTwo调用pop返回时传入了result参数,在pageOne成功执行了onPop回调,并接收到相关参数。
https://coub.com/stories/5089704-jcu
https://coub.com/stories/5089703-griffith
https://coub.com/stories/5089702-uq
https://coub.com/stories/5089700-swinburne
https://coub.com/view/4b73k6
https://coub.com/view/4b73k4
https://coub.com/view/4b73k3
https://coub.com/view/4b73k2
https://coub.com/view/4b73k1
https://coub.com/view/4b73jz
https://coub.com/view/4b73jw
https://coub.com/view/4b73jv
https://coub.com/view/4b73js
https://coub.com/view/4b73jp
https://coub.com/view/4b73jm
https://coub.com/view/4b73jj
https://coub.com/view/4b73ji
https://coub.com/view/4b73jh
https://coub.com/view/4b73jg
https://coub.com/sources/19155990
https://coub.com/sources/19155989
https://coub.com/sources/19155987
https://coub.com/view/4b73j9
https://coub.com/view/4b73j7
https://coub.com/stories/5089696-victoria
https://coub.com/stories/5089695-flinders
https://coub.com/stories/5089694-unisa
https://coub.com/stories/5089693-uc
https://coub.com/stories/5089691-cdu
https://coub.com/stories/5089689-curtin
https://coub.com/stories/5089687-bond
https://coub.com/stories/5089686-jcu
https://coub.com/stories/5089684-cqu
https://coub.com/view/4b707t
https://coub.com/view/4b707r
https://coub.com/view/4b707o
https://coub.com/view/4b707n
https://coub.com/view/4b707h
https://coub.com/view/4b707d
https://coub.com/view/4b7077
https://coub.com/view/4b7074
https://coub.com/view/4b706z
https://coub.com/view/4b706u
https://coub.com/view/4b706o
https://coub.com/view/4b706c
https://coub.com/view/4b7063
https://coub.com/view/4b7060
https://coub.com/view/4b705v
https://coub.com/view/4b705n
https://coub.com/view/4b705k
https://coub.com/view/4b701z
https://coub.com/view/4b6zjc
https://coub.com/view/4b6zja
https://coub.com/view/4b6zj9
https://coub.com/view/4b6zj6
https://coub.com/view/4b6zj4
https://coub.com/view/4b6zj3
https://coub.com/view/4b6zj2
https://coub.com/view/4b6zj1
https://coub.com/view/4b6ziz
https://coub.com/view/4b6zix
https://coub.com/view/4b6ziv
https://coub.com/view/4b6ziu
https://coub.com/view/4b6zit
https://coub.com/view/4b6zis
https://coub.com/view/4b6zio
https://coub.com/view/4b6zin
https://coub.com/view/4b6zim
https://coub.com/view/4b6zik
问题现象
使用Navigation构建路由,从pageOne通过pushPath跳转到pageTwo,期望pageOne的onPop回调在pageTwo返回时被触发,但效果未达预期。
问题代码示例参考如下:
class ParamWithOp {
operation: number = 1
count: number = 10
}
@Entry
@Component
struct PageOne {
pageInfo: NavPathStack = new NavPathStack();
@State message: string = 'Hello World'
@Builder
pageMap(name: string, params: Object) {
if (name === 'pageTwo') {
PageTwo()
}
}
build() {
Navigation(this.pageInfo) {
Column() {
Text(this.message)
.width('80%')
.height(50)
.margin(10)
Button('pushPath', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(10)
.onClick(() => {
// 将name指定的NavDestination页面信息入栈,传递的数据为param,添加接收处理结果的onPop回调。
this.pageInfo.pushPath({
name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo) => {
this.message = `[pushPath]last page is: ${popInfo.info.name} result: ${JSON.stringify(popInfo.result)}`
}
});
})
}.width('100%').height('100%')
}.navDestination(this.pageMap)
.title('pageOne')
}
}
@Component
struct PageTwo {
pathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Column() {
Button('pop', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
// 回退到上一个页面,此处代码,在pop回pageOne页面时,未传参数
this.pathStack.pop();
})
}.width('100%').height('100%')
}.title('pageTwo')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
效果预览
点击放大
背景知识
Navigation组件是路由导航的根视图容器,结合导航控制器NavPathStack可实现组件导航。
pushPath:将info指定的NavDestination页面信息入栈。可设置onPop回调函数来接收参数。
pop:弹出路由栈栈顶元素,并触发onPop回调传入页面处理结果。
问题定位
点击放大
查阅官方文档关于pushPath方法的NavPathInfo入参说明,其中的onPop回调函数仅pop、popToName、popToIndex中设置result参数后触发。
分析结论
onPop回调函数需要使用pop、popToName、popToIndex方法返回时设置result参数才会触发,否则不会执行onPop回调。
修改建议
按上节所述,只需在pageTwo中调用pop方法时,传入result参数,即可在pageOne中成功收到onPop的回调。修改问题代码如下:
// 回退到上一个页面,随便传个result即可触发onPop回调
this.pathStack.pop(1);
修改后的运行效果参见效果预览,可以看到,当pageTwo调用pop返回时传入了result参数,在pageOne成功执行了onPop回调,并接收到相关参数。
https://coub.com/stories/5089704-jcu
https://coub.com/stories/5089703-griffith
https://coub.com/stories/5089702-uq
https://coub.com/stories/5089700-swinburne
https://coub.com/view/4b73k6
https://coub.com/view/4b73k4
https://coub.com/view/4b73k3
https://coub.com/view/4b73k2
https://coub.com/view/4b73k1
https://coub.com/view/4b73jz
https://coub.com/view/4b73jw
https://coub.com/view/4b73jv
https://coub.com/view/4b73js
https://coub.com/view/4b73jp
https://coub.com/view/4b73jm
https://coub.com/view/4b73jj
https://coub.com/view/4b73ji
https://coub.com/view/4b73jh
https://coub.com/view/4b73jg
https://coub.com/sources/19155990
https://coub.com/sources/19155989
https://coub.com/sources/19155987
https://coub.com/view/4b73j9
https://coub.com/view/4b73j7
https://coub.com/stories/5089696-victoria
https://coub.com/stories/5089695-flinders
https://coub.com/stories/5089694-unisa
https://coub.com/stories/5089693-uc
https://coub.com/stories/5089691-cdu
https://coub.com/stories/5089689-curtin
https://coub.com/stories/5089687-bond
https://coub.com/stories/5089686-jcu
https://coub.com/stories/5089684-cqu
https://coub.com/view/4b707t
https://coub.com/view/4b707r
https://coub.com/view/4b707o
https://coub.com/view/4b707n
https://coub.com/view/4b707h
https://coub.com/view/4b707d
https://coub.com/view/4b7077
https://coub.com/view/4b7074
https://coub.com/view/4b706z
https://coub.com/view/4b706u
https://coub.com/view/4b706o
https://coub.com/view/4b706c
https://coub.com/view/4b7063
https://coub.com/view/4b7060
https://coub.com/view/4b705v
https://coub.com/view/4b705n
https://coub.com/view/4b705k
https://coub.com/view/4b701z
https://coub.com/view/4b6zjc
https://coub.com/view/4b6zja
https://coub.com/view/4b6zj9
https://coub.com/view/4b6zj6
https://coub.com/view/4b6zj4
https://coub.com/view/4b6zj3
https://coub.com/view/4b6zj2
https://coub.com/view/4b6zj1
https://coub.com/view/4b6ziz
https://coub.com/view/4b6zix
https://coub.com/view/4b6ziv
https://coub.com/view/4b6ziu
https://coub.com/view/4b6zit
https://coub.com/view/4b6zis
https://coub.com/view/4b6zio
https://coub.com/view/4b6zin
https://coub.com/view/4b6zim
https://coub.com/view/4b6zik
uniappx本地打包微信登录分享提示无提供商
build.gradle.kts新增
buildConfigField("String", "UTSRegisterProviders", "\"[{\\"name\\":\\"weixin\\",\\"service\\":\\"oauth\\",\\"class\\":\\"uts.sdk.modules.DCloudUniOauthWeixin.UniOAuthWeixinProviderImpl\\"},{\\"name\\":\\"weixin\\",\\"service\\":\\"share\\",\\"class\\":\\"uts.sdk.modules.DCloudUniShareWeixin.UniShareWeixinProviderImpl\\"}]\"")
build.gradle.kts新增
buildConfigField("String", "UTSRegisterProviders", "\"[{\\"name\\":\\"weixin\\",\\"service\\":\\"oauth\\",\\"class\\":\\"uts.sdk.modules.DCloudUniOauthWeixin.UniOAuthWeixinProviderImpl\\"},{\\"name\\":\\"weixin\\",\\"service\\":\\"share\\",\\"class\\":\\"uts.sdk.modules.DCloudUniShareWeixin.UniShareWeixinProviderImpl\\"}]\"")




