-
harmony-cordova摘要cordova是美国Apache基金会下的移动端跨平台开源项目,目前并不支持HarmonyOS next版本,但是在鸿蒙三方库中心的harmony-cordova主要用于鸿蒙版跨平台研发,特别是原Android和Ios的cordova项目,无需投入任何研发即可轻松生成鸿蒙版APP。背景知识Android是谷歌旗下的操作系统,由于制裁原因,华为已不能使用Android操作系统,从HarmonyOS Next版本之后,华为手机也慢慢将不再兼容安卓APP,原安卓APP,如果继续要在华为手机上用,必须适配鸿蒙操作系统,说适配是好听的,真实情况是在HarmonyOS next操作系统上全部重新研发,不是简单的适配工作,原Android的代码已一无是处了,这样势必增加研发成本。对于大厂这些都是小case了,但是对于中小企业在新的操作系统投入研发,投入的时间成本和资金势必难以接受,另外由于HarmonyOS是一个新的生命,社区支持不够成熟,人才短缺很多中小企业就会望而生畏了。为此我推荐cordova混合研发,只有cordova才是真正的混合研发免费且开源,调用原生态API,一次研发满足安卓、Ios和鸿蒙操作系统。对于市场上流行的开源项目,我可以好不夸张的说只有cordova,没有之一了。国内也有也有一个开发框架,托管了APP的打包工作,虽然也是打着免费的口号,确强制集成了不该集成的SDK,以至于在上架的时候无法满足应用市场的要求和国家法律的要求,因为要说明这些SDK的作用是什么,但是很多开发者就不知道集成有这个SDK,因此隐私政策写不好而不能上架,因为盈利性公司不是基金会,不会提供免费的午餐。我们能够理解,这里我就不明说了。因此对于用心运营的APP的企业,我推荐混合式研发,也不要装X全部使用原生研发,后面维护和升级拿石头砸自己脚。鸿蒙原生开发原生鸿蒙研发支持ArkTS和C/C++语言。并不支持Java,Java的同学可能比较难过,其实也不用担心,看我接下来的分析,ArkTS是华为在TS语言上的进一步优化的后开发语言,不同于TS语言,学起来并不难,只需要几分钟看看就可以上手了,当然这是对于之前熟练的掌握了Java、Js或C/C++的基础上,说实在的如果有了前面的基础TS无需单独学习,直接就可以上手,如果使用native c/c++就不同了,C/C++语言会使很多开发者心里发怵,以致于不敢轻易创建native C++项目,所以有一个部分开发者只能使用ArkTS开发原生鸿蒙APP了,但这样就失去了优势,只能开发更上层应用性的内容,涉及框架或者底层的开发就无法胜任了.harmony-cordova就是使用C/C++研发的,cordova的插件也是C/C++研发的,只有涉及到鸿蒙UI层的必须使用ArkTS了。所以是两者结合开发的,harmony-cordova所涉及的技术方案,我这里不过多的说明,大家只要会用就可以了。接下来说一下harmonyOS吧,HarmonyOS 内核并不是Linux内核,虽然支持C/C++研发,但是并不是现存所有的Linux的开源项目都可以集成到HarmonyOS上的,在加上DevEco采用的是CMake编译,如果原Linux的开源项目是Makefile编译的,要移植到HarmonyOS上,也并非易事,需要Linux C/C++开发的熟手才可以做到,并非入门级别的就可以胜任。如果原Linux下的开源项目,源文件数量少,可以直接拷贝源码集成,如果原Linux下的开源项目文件众多,编译复杂,就需要使用交叉编译移植到HarmonyOS上,为什么要移植Linux开源项目呢?因为大多项目都会依赖Linux的动态库so,所以要进行移植,如果不移植很多C/C++程序就很难开发。但是并不是所有的Linux的开源项目都可以移植的,因为HarmonyOS并不是Linux内核,使用的编译器也不是gcc,所以Linxu下面的so之间互相依赖,因此在Harmony OS上有些so无法移植。另外Linux下面的C/C++的程序和HarmonyOS也有差异,HarmonyOS也预制集成了类似Linux下的so,虽然有预制so,但是并不是所有的函数都可以使用,因为接口并不全,特别是一些涉及到内核的调用和Linux并不一样。综上所述,在HarmonOS平台上,使用native c/c++研发框架类的har,则需要更多的C/C++开发经验,这方面的人才会更少。因此您在网上看到最多的都是ArkTS开发的。harmony-cordova为什么要研发鸿蒙版cordova,公司研发harmony cordova是因为公司内的APP原使用cordova框架研发,很遗憾的是Apache基金会不支持,所以我们公司就自己研发了,研发好以后首先使用在我们公司自己的产品上,1.0版本首先满足了我们公司自己需要插件,后面会慢慢升级以兼容大部分cordova插件。自定义插件,很多Android项目或者Ios项目,集成了自定义插件,例如每个手机厂商的推送功能,都是自定义插件研发的,这里插句话,不要集成第三方的统一推送的SDK,会让你的APP由于隐私政策审核起来出现很多的问题,所以建议集成所有手机厂商自己的推送SDK,因为手机厂商并不多,集成也没有太多的工作。还有OSS对象存储等都需要自定义插件,harmony-cordova也集成了一些常用的自定义插件。如果您的项目中有自定义插件,需要使用harmony-cordova的,就需要开发者支持了。Android移植鸿蒙步骤1,打开DevEco创建项目,选择Empty Ability进入下一步,填写必要信息,这里要注意,bundle name 先填写com.example.myapplication,也就是保持默认不变,因为在没有cordova.crt证书的情况下,cordova鸿蒙版要求bundle name必须为com.example.myapplication,主要用于研发测试,如果开发测试完成要修改bundle name上架鸿蒙应用市场,请联系开发者申请cordova.ert证书,或者事先联系开发者提供技术服务。2,项目创建成功后,复制原有Android studio的工程assests/www目录下面的所有文件到鸿蒙工程entry/src/main/resources/目录下,注意直接复制原andriod工程www目录下的文件,不包含www。3,复制原android工程res/xml目录下的config.xml文件到鸿蒙工程entry/src/main/resources/目录下。4,打开DevEco studio的Terminal终端,进入工程目录,执行 ohpm install harmony-cordova 安装本插件。5,打开鸿蒙工程文件entry/src/main/etx/pages/Index.ets文件,修改代码如下:import { MainPage, pageBackPress, pageHideEvent, pageShowEvent } from 'harmony-cordova/Index'; @Entry @Component struct Index { onPageShow(){ pageShowEvent(); //页面显示通知cordova } onBackPress() { pageBackPress(); //拦截返回键由cordova处理 return true; } onPageHide() { pageHideEvent(); //页面隐藏通知cordova } build() { RelativeContainer() { MainPage(); //webview首页index.html } .height('100%') .width('100%') } }6,打开鸿蒙工程文件/entry/src/main/ets/entryAbility/EntryAbility.ets文件,修改onCreate函数如下import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { window } from '@kit.ArkUI'; import { webview } from '@kit.ArkWeb'; import { setSchemeHandler } from 'harmony-cordova/Index';...onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {webview.WebviewController.initializeWebEngine();//webview引擎初始化setSchemeHandler();//设置webview schemehilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');}7,鸿蒙混合研发,也许您会增加其他page页面,不一定应用的首页为cordova webview(index.html)的首页,例如应用增加了鸿蒙的原生的启动页面,包含首页弹窗,同意隐私政策后,然后再从启动页面进入cordova的页面,这样避免在用户没有同意隐私政策的情况下,初始化cordova sdk,因为初始化cordova sdk,系统读取了设备的网络状态,因为国内相关法律规定,在用户没有同意隐私政策的情况下,不允许读取设备的网络标识。8,做以上代码修改后,鸿蒙的移植已经完毕,可以使用模拟器或者真机进行编译和测试了。Ios移植鸿蒙步骤如果您的项目有android和Ios的工程,请参考android项目移植项目的鸿蒙下,如果您的项目没有andriod工程,只有Ios工程,请使用如下方法移植,移植时大部分内容和安卓一样,只是复制的文件的路径不一致,以下只介绍不同部分,相同部分请参考android移植步骤。1,复制Xcode的Ios工程目录下的Staging/www目录下的所有文件到鸿蒙工程entry/src/main/resources/目录下。 2,Xcode工程的config.xml文件在Staging目录下,Xcode工程的该文件不能直接被鸿蒙版cordova使用,需要进行转换,该文件主要记录的是插件的名称和初始化的类,因为鸿蒙版是根据android的config.xml进行插件初始化的,因此需要将Xcode工程config.xml转为安卓的config.xml,请将Xcode工程使用node加入安卓平台,系统会自动生成android版的config.xml。然后将文件复制到鸿蒙版工程的entry/src/main/resources/下。附加说明:本人认为使用cordoca跨平台研发,一般至少都会包含android和ios两大平台,很少只有ios平台,没有android平台的,所以大部分移植鸿蒙参考android移植步骤,后续升级SDK会兼容Ios工程的config.xml,无需转换就可以使用。新项目,一次开发适用于andriod、Ios和Harmony三大平台由于cordova官方当前并不支持HarmonyOS平台,使用node无法直接将HarmonyOS加入到cordova,也无法直接安装插件到HarmonyOS,因此对于新项目要一次开发满足三大平台的话,建议先通过node加入Android和Ios平台和安装插件,后续研发可以使用Android studio研发和调试,待研发成功后,然后再在Xcode和DevEco做跨平台适配。Xcode适配请参考cordova的官方文档,HarmonyOS适配请参考以上Android的移植步骤。特别说明当前版本不支持使用者自定义插件研发,如果该版本没有包含您要使用的插件,或者您的项目中有Android或Ios的自定义插件,需要移植到HarmonyOS平台,请您和本开发者联系,获取技术支持。使用鸿蒙版cordova sdk在开发测试阶段务必将bundle name修改为com.example.myapplication,如果将bunlde name改为正式的Id,鸿蒙版cordova sdk会读取entry/src/main/resources/目录的cordova.crt证书文件,用于验签,如果该文件不存在,启动应用后,应用会闪退。如果应用的bundle name为com.example.myapplication,鸿蒙版 cordova sdk会跳过验签,不检测cordova.crt文件。但是上架鸿蒙应用市场,必须将bundle name改为正式的id,所以请联系开发者申请cordova.ert证书,另外由于操作系统之间的差异,虽然保持了cordova的插件接口不变,但是返回值会有所调整,后续文档会逐步完善,在使用本插件跨平台研发时请联系开发者提供技术服务。
-
如题,PutObject开始后如何停止。尝试调用了request的cancel方法,看起来没起作用,仍然能接到uploadProgressBlock回调。又看了看任务返回值OBSBFTask,也没有找到有什么能取消任务的方式,有大佬遇到过类似的问题么,求解决办法!~
-
https://bbs.huaweicloud.com/forum/thread-0281121402742044007-1-1.html 之前有人发过这个帖子,但是我没有找到具体的文档,我下载了开发指南也没有看到如何通过cocoapod来引入SDK
-
首先想到的是多端框架,写了两个 demo ,测试了下 Flutter 和 React Native 在 UI 复杂的情况下都很卡,毫无用户体验可言,貌似只剩下 Xamarin 一个选择了,但好像用这个开发 Widget 之类的功能还是需要在 macOS 用 Swift + Xcode 原生开发,但我短期内没购买 Mac 的计划,而且实在不喜欢 Swift 语言的设计(不是重点,不要讨论这个防止楼歪)。(调试设备不缺,我有 iPad Pro 2021 / iPad mini 6 / iPhone Xr 真机,不知道能不能完全代替模拟器)(编译完上传包应该可以参考 Bitwarden 的 iOS 客户端直接用 GitHub 的 CI 上传到 App Store )
-
报错如下内容,怎么解决?报错以下内容:[2023-02-27 09:14:31 CST] <main> INFO: Logging level set to informational[2023-02-27 09:14:31 CST] <main> INFO: Logging configured successfully.[2023-02-27 09:14:32 CST] <main> INFO: kxapp [1.0.0][2023-02-27 09:14:32 CST] <main> INFO: OS identifier: Windows 10 10.0 (amd64); jvm=GraalVM 21.0.0 Java 11; jre=null[2023-02-27 09:14:32 CST] <main> INFO: Upload mode selected.[2023-02-27 09:14:32 CST] <main> INFO: Examining the package at: C:\Users\鍟嗙鐟瀄AppData\Local\Temp\au_8564049820444212912\1568223082.itmsp[2023-02-27 09:14:32 CST] <main> INFO: Ensuring that package has well formed metadata file...[2023-02-27 09:14:32 CST] <main> INFO: Gathering the list of valid files from the package ...[2023-02-27 09:14:32 CST] <main> INFO: Finished gathering the list of valid files from the package.[2023-02-27 09:14:32 CST] <main> INFO: Verify progress: Analyzing metadata (Step 0/?)[2023-02-27 09:14:32 CST] <main> INFO: Performing authentication of package 1568223082.itmsp ...[2023-02-27 09:14:32 CST] <main> INFO: Verify progress: Analyzing metadata (Step 1/2)[2023-02-27 09:14:32 CST] <main> INFO: id = 20230227091432-317[2023-02-27 09:14:33 CST] <main> INFO: id = 20230227091433-619[2023-02-27 09:14:34 CST] <main> INFO: The list of files requested for upload by Apple is: [metadata.xml, 1568223082.ipa]. These are the files that will be uploaded.[2023-02-27 09:14:34 CST] <main> INFO: Starting media analysis of assets[2023-02-27 09:14:34 CST] <main> INFO: Verify progress: Adding asset to analyze: 1568223082.ipa (Step 2/3)[2023-02-27 09:14:34 CST] <main> INFO: Asset media analysis has completed[2023-02-27 09:14:34 CST] <main> INFO: id = 20230227091434-739[2023-02-27 09:14:35 CST] <main> INFO: Signiant TransferEngine build 10.6.0.59156[2023-02-27 09:14:35 CST] <main> INFO: Using Signiant UDP transport[2023-02-27 09:14:35 CST] <main> INFO: Transfer engine: 10.6.0.59156[2023-02-27 09:14:35 CST] <Transport Protocol> INFO: The Signiant transfer engine's status is DISCONNECTED[2023-02-27 09:14:35 CST] <Transport Protocol> INFO: The Signiant transfer engine's status is CONNECTING[2023-02-27 09:14:35 CST] <Transport Protocol Seeker> INFO: Processing URL list: mxwan://sgr802.apple.com:44001/[2023-02-27 09:14:35 CST] <Connector: UDP sgr802.apple.com/17.133.233.102:44001> INFO: Trying UDP sgr802.apple.com/17.133.233.102:44001...[2023-02-27 09:14:36 CST] <Transport Protocol Seeker> INFO: Connected to UDP sgr802.apple.com/17.133.233.102:44001[2023-02-27 09:14:37 CST] <Transport Protocol> INFO: Authenticating[2023-02-27 09:14:54 CST] <Transport Protocol> INFO: The Signiant transfer engine's status is AWAITING_DATA_STREAMS[2023-02-27 09:14:54 CST] <Transport Protocol> INFO: Setting up data streams[2023-02-27 09:14:58 CST] <Data Stream 0 (0)> INFO: The Signiant transfer engine's status is CONNECTED[2023-02-27 09:14:58 CST] <Data Stream 0 (0)> INFO: Connected using WAN accelerator[2023-02-27 09:14:58 CST] <File Progress Event Thread> INFO: File: a4810215-9452-4bd5-bca5-39ae297f5cc02644872023157440642.txt 11/11, 100% completed[2023-02-27 09:14:59 CST] <Transport Protocol> INFO: The Signiant transfer engine's status is DISCONNECTED[2023-02-27 09:14:59 CST] <Transport Protocol> INFO: The Signiant transfer engine's status is DISCONNECTED[2023-02-27 09:14:59 CST] <main> INFO: Verify progress: Validating assets (Step 3/3)[2023-02-27 09:14:59 CST] <main> INFO: Verify progress: Validating assets (Step 4/3)[2023-02-27 09:14:59 CST] <main> INFO: id = 20230227091459-285[2023-02-27 09:15:01 CST] <main> ERROR: An error occurred saving your changes to the Apple database. This problem may be a transient issue on the Apple side. If the problem persists for more than an hour, please contact your iTunes representative. (1015)[2023-02-27 09:15:01 CST] <main> INFO: Done performing authentication.[2023-02-27 09:15:01 CST] <main> INFO: Verify progress: Operation failed (Step 3/3)Package Summary:1 package(s) were not uploaded because they had problems:C:\Users\鍟嗙鐟瀄AppData\Local\Temp\au_8564049820444212912\1568223082.itmsp - Error Messages:An error occurred saving your changes to the Apple database. This problem may be a transient issue on the Apple side. If the problem persists for more than an hour, please contact your iTunes representative. (1015)使用工具:http://gui.applicationloader.net:8480/index.html#/home。
-
【功能模块】算子分析:找出张量的唯一元素。当提供可选属性“轴”时,将返回沿“轴”切片的唯一子张量。否则,输入张量被展平,并返回展平张量的唯一值。第一个输出张量“Y”包含输入的所有唯一值或子张量。第二个可选输出张量'idx'包含'Y'元素在'X'中第一次出现的索引。第三个可选输出张量'inverse_idx'包含'X'的元素,它在'Y'中的相应索引。"。第四个可选输出张量 'counts' 包含输入中 'Y' 的每个元素的计数。算子属性:Attributesout_idx: int(default is int64)(Optional) 输出idx的type。默认值int64。sorted : int (default is True)(Optional) 在作为输出返回之前是否按升序对唯一元素进行排序。必须是 true(默认值) 或 false之一。return_inverse(default is false)(Optional) 是否输出inverse_idx,默认false。InputsX (non-differentiable) : T 要处理的 ND 输入张量。axis : A 1D tensor (default is -1)(Optional) 要应用唯一的维度。如果未指定,则返回扁平化输入的唯一元素。负值意味着从后面计算尺寸。可接受的范围是 [-r, r-1] 其中 r = rank(input)。默认值-1。Outputs (1 - 4)Y (non-differentiable) : T与“X”类型相同的张量,包含沿“X”中提供的“轴”切片的所有唯一值或子张量,按照它们在输入“X”中出现的相同顺序排序或维护idx(non-differentiable) : tensor(int64)一个一维 INT64 张量,包含在 'X' 中第一次出现的 'Y' 元素的索引。当提供“axis”时,它包含“axis”上输入“X”中子张量的索引。当未提供 'axis' 时,它包含扁平输入张量中值的索引。inverse_idx (optional, non-differentiable) : tensor(int64)一维 INT64 张量,对于 'X' 的元素,包含其在 'Y' 中的相应索引。当提供“axis”时,它包含“axis”上输出“Y”中子张量的索引。如果未提供“axis”,则它包含输出“Y”中值的索引。counts (non-differentiable) : tensor(int64)一维 INT64 张量,包含输入“X”中“Y”的每个元素的计数Type ConstraintsT : tensor(uint8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(int8), tensor(int16), tensor(int32), tensor(int64), tensor(float16), tensor(float), tensor(double), tensor(string), tensor(bool), tensor(complex64), tensor(complex128)算子案例:Example 1: input_X = [2, 1, 1, 3, 4, 3] attribute_sorted = false attribute_axis = Noneoutput_Y = [2, 1, 3, 4] output_indices = [0, 1, 3, 4] output_inverse_indices = [0, 1, 1, 2, 3, 2] output_counts = [1, 2, 2, 1]Example 2: input_X = [[1, 3], [2, 3]] attribute_sorted = true attribute_axis = Noneoutput_Y = [1, 2, 3] output_indices = [0, 2, 1] output_inverse_indices = [0, 2, 1, 2] output_counts = [1, 1, 2]Example 3: input_X = [[1, 0, 0], [1, 0, 0], [2, 3, 4]] attribute_sorted = true attribute_axis = 0output_Y = [[1, 0, 0], [2, 3, 4]] output_indices = [0, 2] output_inverse_indices = [0, 0, 1] output_counts = [2, 1]【操作步骤&问题现象】自己写的思路,不清楚输入张量X按照输入的axis轴展开的代码怎么写?希望能提供下用到的API和参考代码,谢谢1.新建变量,输入的X、axis、idx(type)、sorted、return_inverse,输出的Y,idxout,inverse_idxout,counts_out。2.注意axis,for循环X,for循环y,判断y中有无X的元素,没有就加入,有就跳过;判断sorted,然后对y进行排序,如果有需要的话3.循环y,设置flag=false,index=0,count=0循环x,检查第一次出现y,flag=0了,记录index=i,然后继续循环,出现一次,就count++,把index和count保存到idxout和counts_out中。4.判读return_inverse循环,循环x,设置flag=false,index=0,循环y,检查第一次出现y,flag=0了,记录index=i,保存到inverse_idxout。【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
动画 Flutter中的动画系统基于Animation对象的,和之前的手势不同,它不是一个Widget,这是因为Animation对象本身和UI渲染没有任何关系。Animation是一个抽象类,就相当于一个定时器,它用于保存动画的插值和状态,并执行数值的变化。widget可以在build函数中读取Animation对象的当前值, 并且可以监听动画的状态改变。AnimationController AnimationController用于控制动画,它包含动画的启动forward()、停止stop() 、反向播放 reverse()等方法。AnimationController会在动画的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字。AnimationController controller = AnimationController( duration: const Duration(milliseconds: 2000), //动画时间 lowerBound: 10.0, //生成数字的区间 upperBound: 20.0, //10.0 - 20.0 vsync: this //TickerProvider 动画驱动器提供者);Ticker Ticker的作用是添加屏幕刷新回调,每次屏幕刷新都会调用TickerCallback。使用Ticker来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源。因为Flutter中屏幕刷新时会通知Ticker,锁屏后屏幕会停止刷新,所以Ticker就不会再触发。最简单的做法为将SingleTickerProviderStateMixin添加到State的定义中。import 'package:flutter/material.dart';void main() => runApp(AnimationApp());class AnimationApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "animation", home: Scaffold( appBar: AppBar( title: Text('animation'), ), body: AnimWidget(), ), ); }}// 动画是有状态的class AnimWidget extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimWidgetState(); }}class _AnimWidgetState extends State<AnimWidget> with SingleTickerProviderStateMixin { AnimationController controller; bool forward = true; @override void initState() { super.initState(); controller = AnimationController( // 动画的时长 duration: Duration(milliseconds: 2000), lowerBound: 10.0, upperBound: 100.0, // 提供 vsync 最简单的方式,就是直接混入 SingleTickerProviderStateMixin // 如果有多个AnimationController,则使用TickerProviderStateMixin。 vsync: this, ); //状态修改监听 controller ..addStatusListener((AnimationStatus status) { debugPrint("状态:$status"); }) ..addListener(() { setState(() => {}); }); debugPrint("controller.value:${controller.value}"); } @override Widget build(BuildContext context) { return Column( children: <Widget>[ Container( width: controller.value, height: controller.value, color: Colors.blue, ), RaisedButton( child: Text("播放"), onPressed: () { if (forward) { controller.forward(); } else { controller.reverse(); } forward = !forward; }, ), RaisedButton( child: Text("停止"), onPressed: () { controller.stop(); }, ) ], ); }}动画状态监听:在forword结束之后状态为completed。在reverse结束之后状态为dismissedTween 默认情况下,AnimationController对象值为:double类型,范围是0.0到1.0 。如果我们需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。要使用Tween对象,需要调用其animate()方法,然后传入一个控制器对象,同时动画过程中产生的数值由Tween的lerp方法决定。import 'package:flutter/material.dart';void main() => runApp(AnimationApp());class AnimationApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "animation", home: Scaffold( appBar: AppBar( title: Text('animation'), ), body: AnimWidget(), ), ); }}// 动画是有状态的class AnimWidget extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimWidgetState(); }}class _AnimWidgetState extends State<AnimWidget> with SingleTickerProviderStateMixin { AnimationController controller; bool forward = true; Tween<Color> tween; @override void initState() { super.initState(); controller = AnimationController( // 动画的时长 duration: Duration(milliseconds: 2000), // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin vsync: this, ); //使用Color tween = ColorTween(begin: Colors.blue, end: Colors.yellow); //添加动画值修改监听 tween.animate(controller)..addListener(() => setState(() {})); } @override Widget build(BuildContext context) { return Column( children: <Widget>[ Container( width: 100, height: 100, //获取动画当前值 color: tween.evaluate(controller), ), RaisedButton( child: Text("播放"), onPressed: () { if (forward) { controller.forward(); } else { controller.reverse(); } forward = !forward; }, ), RaisedButton( child: Text("停止"), onPressed: () { controller.stop(); }, ) ], ); }}Curve 动画过程默认是线性的(匀速),如果需要非线形的,比如:加速的或者先加速后减速等。Flutter中可以通过Curve(曲线)来描述动画过程。import 'package:flutter/material.dart';void main() => runApp(AnimationApp());class AnimationApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "animation", home: Scaffold( appBar: AppBar( title: Text('animation'), ), body: AnimWidget(), ), ); }}// 动画是有状态的class AnimWidget extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimWidgetState(); }}class _AnimWidgetState extends State<AnimWidget> with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; bool forward = true; @override void initState() { super.initState(); controller = AnimationController( // 动画的时长 duration: Duration(milliseconds: 2000), // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin vsync: this, ); //弹性 animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn); //使用Color animation = Tween(begin: 10.0, end: 100.0).animate(animation) ..addListener(() { setState(() => {}); }); } @override Widget build(BuildContext context) { return Column( children: <Widget>[ Container( //不需要转换 width: animation.value, height: animation.value, //获取动画当前值 color: Colors.blue, ), RaisedButton( child: Text("播放"), onPressed: () { if (forward) { controller.forward(); } else { controller.reverse(); } forward = !forward; }, ), RaisedButton( child: Text("停止"), onPressed: () { controller.stop(); }, ) ], ); }}AnimatedWidget 通过上面的学习我们能够感受到Animation对象本身和UI渲染没有任何关系。而通过addListener()和setState() 来更新UI这一步其实是通用的,如果每个动画中都加这么一句是比较繁琐的。AnimatedWidget类封装了调用setState()的细节,简单来说就是自动调用setState()。 Flutter中已经封装了很多动画,比如对widget进行缩放,可以直接使用ScaleTransitionimport 'package:flutter/material.dart';void main() => runApp(AnimationApp());class AnimationApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "animation", home: Scaffold( appBar: AppBar( title: Text('animation'), ), body: AnimWidget(), ), ); }}// 动画是有状态的class AnimWidget extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimWidgetState(); }}class _AnimWidgetState extends State<AnimWidget> with SingleTickerProviderStateMixin { AnimationController controller; Animation<double> animation; bool forward = true; @override void initState() { super.initState(); controller = AnimationController( // 动画的时长 duration: Duration(milliseconds: 2000), // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin vsync: this, ); //弹性 animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn); //使用Color animation = Tween(begin: 10.0, end: 100.0).animate(animation); } @override Widget build(BuildContext context) { return Column( children: <Widget>[ ScaleTransition( child: Container( width: 100, height: 100, color: Colors.blue, ), scale: controller, ), RaisedButton( child: Text("播放"), onPressed: () { if (forward) { controller.forward(); } else { controller.reverse(); } forward = !forward; }, ), RaisedButton( child: Text("停止"), onPressed: () { controller.stop(); }, ) ], ); }}Hero动画 Hero动画就是在路由切换时,有一个共享的Widget可以在新旧路由间切换,由于共享的Widget在新旧路由页面上的位置、外观可能有所差异,所以在路由切换时会逐渐过渡,这样就会产生一个Hero动画。import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', home: Scaffold( appBar: AppBar( title: Text("主页"), ), body: Route1()), ); }}// 路由Aclass Route1 extends StatelessWidget { @override Widget build(BuildContext context) { return Container( alignment: Alignment.topCenter, child: InkWell( child: Hero( tag: "avatar", //唯一标记,前后两个路由页Hero的tag必须相同 child: CircleAvatar( backgroundImage: AssetImage( "assets/banner.jpeg", ), ), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (_) { return Route2(); })); }, ), ); }}class Route2 extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Hero( tag: "avatar", //唯一标记,前后两个路由页Hero的tag必须相同 child: Image.asset("assets/banner.jpeg")), ); }}组合动画有些时候我们可能会需要执行一个动画序列执行一些复杂的动画。import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', home: Route(), ); }}class Route extends StatefulWidget { @override State<StatefulWidget> createState() { return RouteState(); }}class RouteState extends State<Route> with SingleTickerProviderStateMixin { Animation<Color> color; Animation<double> width; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController( // 动画的时长 duration: Duration(milliseconds: 2000), // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin vsync: this, ); //高度动画 width = Tween<double>( begin: 100.0, end: 300.0, ).animate( CurvedAnimation( parent: controller, curve: Interval( //间隔,前60%的动画时间 1200ms执行高度变化 0.0, 0.6, ), ), ); color = ColorTween( begin: Colors.green, end: Colors.red, ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.6, 1.0, //高度变化完成后 800ms 执行颜色编码 ), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("主页"), ), body: InkWell( ///1、不用显式的去添加帧监听器,再调用setState() ///2、缩小动画构建的范围,如果没有builder,setState()将会在父widget上下文调用,导致父widget的build方法重新调用,现在只会导致动画widget的build重新调用 child: AnimatedBuilder( animation: controller, builder: (context, child) { return Container( color: color.value, width: width.value, height: 100.0, ); }), onTap: () { controller.forward().whenCompleteOrCancel(() => controller.reverse()); }, ), ); }}打包 Flutter在打Release包时候回使用AOT,因此在对一个Flutter测试时候务必使用Release来进行测试。打包命令:flutter build apk 。当然我们需要打包时,还需要配置一些比如签名的内容。配置这些内容和普通Android工程没有区别,都是在build.gradle中进行,只是Flutter工程AS没有提供GUI。 在Flutter工程的android/app下面的build.gradle可以修改包名、版本等信息,这就不用多说了。获得签名文件之后,将它复制到flutter的android目录: 然后在app的build.gradle中配置:signingConfigs { release { keyAlias 'enjoy' keyPassword '123456' // 因为是放到父级的根目录,使用rootProject // 如果放在这个build.gradle的同级,直接使用file storeFile rootProject.file('enjoy.jks') storePassword '123456' } } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.release } }饼图https://github.com/google/chartsStack布局中的fit属性与Image的fit类似,表示内容的扩充情况。默认为StackFit.loose表示Stack与内容一样大。如果设置为StackFit.passthrough则表示Stack父Widget的约束会传给Stack内部非Positioned的子Widget。效果如代码中的StackFit.dart原文链接:https://blog.csdn.net/u010755471/article/details/124691809
-
调研公司StockApps公布的数据指,近两年来安卓系统占有的市场份额快速下跌,已跌去近一成,跌穿七成份额,苹果iOS系统的市场份额显著上涨,中间的差额可能被鸿蒙系统占去。调研公司StockApps公布的数据显示,今年1月安卓系统占有全球智能手机市场的份额为69.74%,较2018年的77.32%显著下滑了7.58个百分点或9.8%,显示出安卓系统遭受了重大挫败。与此同时,苹果iOS全球市场份额则增长了6个百分点或31.4%,从2018年的19.4%提高到了25.49%,苹果并未全数夺取安卓系统让出的市场份额,而这两者合计占有的份额为95.23%,中间的差额部分或为鸿蒙系统占有。这几年全球市场除安卓系统和iOS系统之外,其他手机操作系统大多都已被边缘化,iOS系统和安卓系统一度占有全球智能手机市场99%的市场份额,而2020年以来出现的操作系统无疑就是鸿蒙系统。华为的鸿蒙系统在2020年Q3应用于华为电视产品上,一年后的2021年6月正式向手机用户推送,在时间上恰好位于安卓系统市场份额急跌的阶段,故此可以认为安卓系统丢失的市场份额除了被iOS系统夺走之外,另外部分市场份额就是被鸿蒙系统占有。华为推出鸿蒙系统的原因之一就在于谷歌在2019年Q4暂停给华为授权谷歌GMS服务,华为随即推出HMS服务替代谷歌GMS服务,随后双方的合作遭受了更多困难,于是华为在2020年推出了鸿蒙系统。鸿蒙系统在2021年6月上线后,短短半年时间就获得了2亿多手机用户的支持,同时通过与美的等家电厂商的合作以及自己的生态链业务带来1亿多用户,由此鸿蒙系统总计获得了3.4亿多的用户,成为全球发展最快的移动操作系统。其实安卓手机企业对谷歌也颇为不满,近几年谷歌对安卓手机企业的控制日益加强,限制安卓手机企业对安卓系统开发差异化功能,甚至禁止安卓手机企业开发自己的手势操作,导致安卓手机日益同质化而陷入价格战之中;同时谷歌又要求安卓手机企业搭载越来越多的谷歌应用,导致安卓系统日益臃肿,卡顿严重。由此安卓手机企业都对谷歌颇为愤怒,不过目前来说由于谷歌的诸多应用在全球市场占据优势地位,海外消费者对谷歌的应用属于刚需,安卓手机企业只能敢怒不敢言,唯有华为手机敢于表示不同意见,而谷歌也对华为手机采取了措施。面对安卓系统在智能手机市场的挫败,不知如今的谷歌有没有后悔呢?安卓系统在智能手机市场的地位被削弱,可能会导致更多手机操作系统出现,谷歌在移动操作系统市场的领导地位或许就此崩解。 原文标题 : 谷歌遭重大挫折,疑似鸿蒙夺走不小份额,安卓系统领导地位或崩塌
-
iOS系统的封闭一直以来都毁誉参半,支持的人认为这是iOS系统安全稳定的基础,正是由于封闭,才有了iOS系统相对良好的体验,反对的人则认为这会导致一些安卓手机有的功能无法在iPhone上无法使用或体验较差,虽然可以通过系统越狱方式来绕过限制,但这并非主流方法,且对于玩机经验较差的iPhone用户来说,该方法可能会导致一些不可预知的问题出现,所以果粉们都希望苹果官方来放开某些限制。对于可能会被苹果官方逐步放开的限制,NFC功能算得上一个,实际上从iPhone 6开始就已经支持NFC功能,然而时至今日,NFC功能在iPhone上依然只是部分开放,这让很多用户不满,凭什么在安卓手机上体验良好的功能,到了iPhone上就要被阉割的不成样子呢?虽然iPhone上的NFC功能对第三方支持度不高,但对自家iOS系统中apple pay却有相当完美的支持,这使得很多人质疑苹果在NFC功能方面有垄断嫌疑,否则为什么不一视同仁公平对待呢?正是因为这个原因,苹果又被欧盟盯上了,近日欧盟指控苹果对第三方使用iPhone上NFC功能进行了限制,却对自家功能一路开绿灯放行,PayPal(国际支付平台)就在指控苹果方面非常积极,希望能在iPhone上让PayPal有更多基于NFC功能的访问权限,而苹果对此却一口回绝,声称apple pay并非欧洲用户唯一支付可选项,言下之意就是想放开限制,门都没有。欧盟打算对苹果这种行为进行反垄断诉讼,由于苹果目前没有任何放松迹象,如果双方僵持不下,这又将是一场旷日持久的反垄断官司,但就之前交锋经历来看,欧盟胜算似乎更大一些。苹果对于iPhone上NFC功能开放速度确实足够缓慢,iPhone 6就已经有的功能,到了iOS11才被允许开发者访问部分功能,可这也是最让人不满的,但迄今为止能使用相关功能API接口的应用程序少的可怜。直到iOS13,苹果才允许iPhone支持后台自动化读取NFC标签功能,但却限定iPhone XR及后续机型才能使用(可iPhone 6就已经支持NFC功能),反正就百般限制。实际上,苹果想要全面放开NFC功能,并没有任何技术上的障碍,而对于NFC功能在使用体验上的提升,苹果心里也是清楚的,但就是进展缓慢,更多可能是利益上的考虑。在座各位,你们觉得什么时候才会在iPhone上全面开放NFC功能呢? 原文标题 : 果粉许愿:iPhone NFC功能,早日全面放开吧!
-
截止至目前,华为鸿蒙系统已经覆盖了手机用户2.4亿,而搭载鸿蒙的生态设备发货量超过1.5亿台,合计超过了3.9亿了。更重要的是,鸿蒙的开源项目OpenHarmony,在全球下载量高达6300万次,已经有44款产品已经获得了OpenHarmony 生态产品兼容性证书,80余款软硬件产品正在通过兼容性测评,还有11款芯片支持OpenHarmony,另外35款芯片在适配中……可以说,鸿蒙已经是当前第三大生态体系了,不说PC端的的windows、MacOS的话,鸿蒙已经只比安卓、iOS这两大生态体系稍逊色一点,但已经形成了鼎立之势了。那么问题就来了,华为鸿蒙系统,与谷歌安卓、苹果iOS相比,优势和劣势是什么?首先对比苹果iOS,最重要的就是鸿蒙是一个开放的系统,鸿蒙的开源版所有人都可以用,苹果的iOS是闭源的,这个算是优点之一。另外鸿蒙系统是一个大一统的系统,就可以搞定手机、手表、平板、、汽车智能座舱等产品,苹果则是不同产品不同的系统。大一统的好处是,各大系统之间可以进行信息流转,比如华为的超级终端等,在iOS系统中就无法实现。此外,在测试中,大家发现鸿蒙系统在功耗上,比iOS控制得更好,更加省电。接着对比安卓,相比于安卓,由于鸿蒙采用微内核,同样也是一个大一统的系统,所以可以搞定手机、手表、物联网等各种各样的设备,这一点比安卓强,这也是谷歌要推FuchsiaOS的原因,因为FuchsiaOS也是一个大一统的系统,但如今进展缓慢。另外同样的,也因为鸿蒙是微内核,大一统,所以可以实现设备之间的信息无缝流转、切换,比如超级终端功能,谁用谁知道。另外鸿蒙相比于安卓,更为流畅、省电,这对于移动设备,特别是手机而言,特别重要。当然,最最重要的是,鸿蒙系统是属于我们自己的,而安卓、iOS是别人的,别人的东西再好也是别人的,自己的东西再不好也是自己的,有了它就不怕被断供,这个意义重大。不过,相比于安卓、iOS,鸿蒙最大的缺点就是生态,鸿蒙当前还是借助于安卓生态,自己的生态不够完善,专用APP非常少,还得靠安卓APP来实现自己的生态,这一点是鸿蒙最大的劣势。 原文标题 : 华为鸿蒙系统,相比谷歌安卓、苹果iOS,优势和劣势是什么?
-
最近一段时间,网上对于iPhone 14系列机型的讨论猜测那是相当热烈,相关消息上热搜简直不要太轻松,就在前天,iPhone 14有望实现息屏显示功能还上了热搜榜第一名,这项苹果“极为先进”的功能,果粉应该是期待已久了。对于这么一个在安卓手机上已经烂大街,甚至更早的诺基亚手机上就已经存在的功能,苹果在2022年才有可能推出,简直让人无语到了极点,虽然还只是猜测,可但凡苹果还要点脸面,息屏显示功能就不应该再拖延了。不管之前之前对于iPhone 14系列机型爆料到了什么程度,始终都处在猜测阶段,只有等到苹果官方自己放消息,某些信息才能算是得到正式确认,近日,苹果泰国官方放出了一则时长15秒的apple pay宣传视频,其中透露了一项很重要的内容,iPhone 14 Pro/Max的感叹号屏被实锤了。小智查了下账号简介,确实是以Apple Inc.结尾,确认是苹果官方账号没什么问题,苹果通过影响力较小的泰国媒体渠道来放风,这也符合苹果一贯以来的做事风格,一些新消息不会第一时间交给大媒体,总是将影响力控制在一定范围内,通过普通用户转发来将消息进行有限的传播,即达到目的,又不过分张扬。这应该算是自刘海屏首次在iPhone X上出现后,苹果手机的最大改变了,和刘海屏相比,这一大一小两个挖孔确实占比面积要小一些,但多出的屏幕空间似乎也难以得到有效利用,要是苹果没有像当年强制要求适配刘海屏一样要求APP开发者适配双挖孔屏,那基本能肯定这就是一种过渡性设计风格,为的就是在将镜头全部放入屏下之前做技术过渡。还有另一个细节问题值得思考,在这则苹果泰国官方宣传视频中,只出现感叹号屏的iPhone 14 Pro和apple watch,两款标准版并未现身,个人猜测应该是和去年一样,没必要拿出来说。外观不用再猜,接下来更多考虑应该是功能配置,有消息称iPhone 14 Pro/Max上的高配屏刷新率可被控制在1Hz-120Hz之间(上一代是10Hz-120Hz),以便更精确控制电量使用,而标准版依然无缘120Hz刷新率。至于丑不丑的问题,也没得算了,双挖孔实锤,不喜欢的就只能去买标准版了。 原文标题 : 苹果官宣iPhone 14,大家不用再猜了!
-
华为云会议V8.5.5版本新特性本月优化内容:PC端:1. Windows客户端1080P功能优化2. 网络研讨会Windows客户端虚拟背景性能优化3. Windows客户端麦克风异常提示优化4. Mac客户端支持开启原始声音5. Mac客户端支持隐藏非视频与会者6. 支持用户加入体验改进计划7. 华为云会议管理平台预约会议可添加直播地址移动端:1. iOS客户端支持会中麦克风浮窗2. iOS客户端支持画廊3*3画面3. 支持用户加入体验改进计划一、Windows客户端1080P功能优化适用场景:当企业开通了1080P功能,在硬件设备满足的情况下,Windows客户端企业用户入会后将收到设置提醒,可前往开启高清画质,让视频会议更清晰生动。二、网络研讨会Windows客户端虚拟背景性能优化适用场景:网络研讨会场景,Windows客户端开启虚拟背景时将减少CPU占用,提升会议体验。三、Windows客户端麦克风异常提示优化适用场景:Windows客户端与会者的麦克风缺失或异常时,进入会议、被主持人解除静音将收到麦克风异常提示,可前往检测或调试麦克风,更快恢复会议音视频状态。四、Mac客户端支持开启原始声音适用场景:Mac客户端支持开启原始声音,启用原始声音后,啸叫抑制和音频降噪等声音增加能力将被自动关闭。五、Mac客户端支持隐藏非视频与会者适用场景:当参会人数较多,想要保持会议中展示画面的美观和一致性时,可选择隐藏未开启视频画面与会者。六、支持用户加入体验改进计划适用场景:手机号码注册、微信注册、创建企业时,支持勾选《用户体验改进计划》。适用场景(桌面端):已注册用户登录客户端后,可选择加入或退出《用户体验改进计划》。适用场景(移动端):已注册用户登录客户端后,可选择加入或退出《用户体验改进计划》。七、华为云会议管理平台预约会议可添加直播地址适用场景:在华为云会议管理平台预约会议时,可添加并设置临时直播间,开启会议直播更便捷。八、iOS客户端支持会中麦克风浮窗适用场景:iOS客户端可开启会中麦克风浮窗,当与会者发言时,麦克风浮窗将显示当前会中音频状态,调整麦克风更方便。九、iOS客户端支持画廊3*3画面适用场景:iOS客户端多人入会时,支持画廊3*3画面,会中画面显示更加美观。(iPhone 7及以上设备支持)
-
21日凌晨一点举办的苹果春季发布会,大家都看了吗?除了iPad Pro、iMac和Apple TV产品以外,一直受到人们关注的AirTag智能硬件也出现在本次发布会上。某种意义上来说,本次春季发布会弥补了去年秋季发布会的不足,带来的新品也比上次“含金量”高了不少。值得一提的是,这次发布会依然是录播,延续苹果公司的一贯水准,整场发布会的运镜和制作十分精美。回归正题,我们还是先来回顾下本次发布会的重点内容。(本文图片截图自苹果发布会视频)紫色iPhone吸睛iPhone又迎来一款全新配色!苹果宣布推出采用紫色配色的iPhone12,将在本周五接受预定。当然,除了全新配色以外iPhone就暂时没有其他的亮点升级了。不过很多网友表示,看到了紫色版本的iPhone,好后悔此前买了绿色款的iPhone,真是欲哭无泪。AirTags终于来了AirTags终于现身,原来是这么一枚硬币大小的智能硬件。如此前猜测的一样,它可以帮助你寻找自己的物品,通过精确查找技术搭配U1芯片、iPhone陀螺仪和距离传感器技术等等,以后再也不怕东西弄丢了。AirTags通过纽扣电池供电,最长续航时间据说长达一年。可通过蓝牙及UWB超宽频技术跟手机连接,放在钥匙,书包甚至宠物身上,起到定位查找的效果。当然还有人们最关注的隐私问题,AirTag主要是追踪人,能够检测位置AirTag和更换物品提醒。苹果还对其设置了一系列的功能,假如你身边被人放了个AirTag,手机也会提醒告知你。不过这么一枚小东西也不便宜,售价29美元,打包四枚售价99美元。全新改款的iMac,七种配色新一代iMac终于来了,这次不仅仅升级了M1芯片,还在外形上实现了新的设计突破。新一代iMac的边框也不再如以往那般圆润,而是更加方正。窄边框带来的另一个变化是,屏幕尺寸提升到了24英寸,5K分辨率。新款的iMac只比iPhone 12的7.4毫米厚了4.1毫米,很难想象这是一款一体机的造型。更重要的是,新iMac去掉了上代尾部的凸出部分,机身变成了一块完完整整平板。相比上代产品,新iMac整体体积减少了 50%,全部重量不到5kg,轻了将近一半。最有竞争力的还是自研M1芯片的植入,其超低功耗和发热让产品性能瞬间拉满。以前可能没有人会想到,M1芯片出现在iMac上竟然会带来如此大的改变吧。最后要聊聊iMac的配色设计了,这次竟然带来了蓝、绿、粉、银、黄、橙、紫 7 种颜色,差不多可以召唤一道彩虹了。新iMac的配件也有所变化,比如带有触控ID指纹识别的妙控键盘等。新iMac还在电源适配器中加入了以太网口,用户可以直接使用电源线来传输网络数据,避免了绕线混乱和桌面摆放的问题。24英寸新款iMac起价9499元,将于5月中下旬发售。
-
按照文档一步一步集成OBS SDK,运行时报错:terminating with uncaught exception of type NSException代码如下: OBSStaticCredentialProvider *provider = [[OBSSTSCredentialProvider alloc] initWithAccessKey:@"xxxx" secretKey:@"xxx" stsToken:@"xxxx"]; provider.securityToken = @"xxxxx"; OBSServiceConfiguration *conf = [[OBSServiceConfiguration alloc] initWithURL:@"https://obs.cn-east-3.myhuaweicloud.com" credentialProvider:provider]; OBSClient *client = [[OBSClient alloc] initWithConfiguration:conf]; OBSPutObjectWithDataRequest *request = [[OBSPutObjectWithDataRequest alloc] initWithBucketName:@"isong" objectKey:@"test/ios.txt" uploadData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]]; request.uploadProgressBlock = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) { NSLog(@"%0.1f%%",(float)floor(totalBytesSent*10000/totalBytesExpectedToSend)/100); }; [client putObject:request completionHandler:^(OBSPutObjectResponse *response, NSError *error) { }];Swift 集成也是报同样的错误
推荐直播
-
华为AI技术发展与挑战:集成需求分析的实战指南
2024/11/26 周二 18:20-20:20
Alex 华为云学堂技术讲师
本期直播将综合讨论华为AI技术的发展现状,技术挑战,并深入探讨华为AI应用开发过程中的需求分析过程,从理论到实践帮助开发者快速掌握华为AI应用集成需求的框架和方法。
去报名 -
华为云DataArts+DWS助力企业数据治理一站式解决方案及应用实践
2024/11/27 周三 16:30-18:00
Walter.chi 华为云数据治理DTSE技术布道师
想知道数据治理项目中,数据主题域如何合理划分?数据标准及主数据标准如何制定?数仓分层模型如何合理规划?华为云DataArts+DWS助力企业数据治理项目一站式解决方案和应用实践告诉您答案!本期将从数据趋势、数据治理方案、数据治理规划及落地,案例分享四个方面来助力企业数据治理项目合理咨询规划及顺利实施。
去报名
热门标签