SwiftUI 在批量文件上传期间显示刷新进度
SwiftUI display refresh progresses during batch files uploading
上下文(macOS 应用程序):
用户可以 select 一堆图像,然后,我开始并行地一次至少上传 5 个文件。问题是我想显示从服务器接收到的每个图像的进度。我对该数组使用 @State
指标来观察变化。
进度状态可以是:待处理、进行中 (%) 和完成。
我需要显示每张图片的百分比。
问题:
进度更新非常频繁,我需要在 ScrollView 中反映这些更改。
图像的布局显示为网格视图(正方形显示),但这会导致 3 个问题:
var body: some View { ... }
每秒调用 2 - 3 次
- 滚动视图在所有这些刷新过程中卡住
- 如此多的视图刷新也导致内存飙升至 1GB
上传完成后,内存恢复到正常水平 (50 - 70mb)
我什至尝试用计时器更新视图,每秒仅反映一次更改,但这并没有太大帮助。
为了改进渲染,我为每个网格视图和滚动视图添加了 .id()
,我可以看到资源使用方面的小改进,但对用户来说感觉还不够好。
我的问题很简单,使用 SwiftUI (1.0) 的最佳实践是什么 - 我们还需要支持 macOS 10.15,而不仅仅是 macOS 11 - 要实现这种 UX,您可以在其中看到进度正在上传文件,但您仍然可以滚动查看待处理的文件或已上传的文件。
由于您没有提供任何代码,因此很难真正深入研究,但这里有几点需要考虑:
调用 var body: some View { ... } about 2 - 3 times per second
本身不应成为瓶颈。我有 SwiftUI 代码,它以每秒 60 次刷新的速度运行而没有打嗝。然而,它 是 一个指标,表明 body
调用中的某些东西是昂贵的。 (没有代码无法判断)
尽可能使用 Equatable
-- 尤其是对于昂贵的视图。这将确保在不需要时不会重新呈现昂贵的视图。如果您只有一个视图的一部分是昂贵的,那么值得将其重构为自己的视图以成为 Equatable
。更多阅读:https://www.hackingwithswift.com/example-code/language/how-to-conform-to-the-equatable-protocol
您的数据模型可以重构为 ObservableObject
或 StateObject
和 Publisher
,您可以在其中使用 Combine 做一些巧妙的事情来减慢速度更新。首先想到的是使用 throttle
来确保您发布的更新仅以 X 间隔反馈到您的视图中。更多阅读:https://rhonabwy.com/2019/12/15/combine-throttle-and-debounce/
确保您的上传工作是在后台线程上完成的,并且您的 UI 更新是在主线程上进行的。根据您进行上传的方式,Combine 可能会再次帮助您,让您在更新进入时连续回调主线程(也可能没有 Combine,但 Combine 使事情变得非常简单)。更多阅读:http://trycombine.com/posts/subscribe-on-receive-on/
打开 Instruments,看看是什么造成了您所看到的所有内存使用。这应该会提示您在哪里进行分配以及在哪里进行优化。
最后,SwiftUI List
在 Catalina 上的表现充其量是值得怀疑的。在大苏尔更好。在 SwiftUI 2.0 中,您可以更多地依赖 LazyVStack
之类的东西,但这不是针对 Catalina 的解决方案。如果你真的绝望了,你可以回到 AppKit 并在那里制作你的列表,将它包装在 NSViewRepresentable
上下文(macOS 应用程序):
用户可以 select 一堆图像,然后,我开始并行地一次至少上传 5 个文件。问题是我想显示从服务器接收到的每个图像的进度。我对该数组使用 @State
指标来观察变化。
进度状态可以是:待处理、进行中 (%) 和完成。
我需要显示每张图片的百分比。
问题: 进度更新非常频繁,我需要在 ScrollView 中反映这些更改。 图像的布局显示为网格视图(正方形显示),但这会导致 3 个问题:
var body: some View { ... }
每秒调用 2 - 3 次- 滚动视图在所有这些刷新过程中卡住
- 如此多的视图刷新也导致内存飙升至 1GB 上传完成后,内存恢复到正常水平 (50 - 70mb)
我什至尝试用计时器更新视图,每秒仅反映一次更改,但这并没有太大帮助。
为了改进渲染,我为每个网格视图和滚动视图添加了 .id()
,我可以看到资源使用方面的小改进,但对用户来说感觉还不够好。
我的问题很简单,使用 SwiftUI (1.0) 的最佳实践是什么 - 我们还需要支持 macOS 10.15,而不仅仅是 macOS 11 - 要实现这种 UX,您可以在其中看到进度正在上传文件,但您仍然可以滚动查看待处理的文件或已上传的文件。
由于您没有提供任何代码,因此很难真正深入研究,但这里有几点需要考虑:
调用
var body: some View { ... } about 2 - 3 times per second
本身不应成为瓶颈。我有 SwiftUI 代码,它以每秒 60 次刷新的速度运行而没有打嗝。然而,它 是 一个指标,表明body
调用中的某些东西是昂贵的。 (没有代码无法判断)尽可能使用
Equatable
-- 尤其是对于昂贵的视图。这将确保在不需要时不会重新呈现昂贵的视图。如果您只有一个视图的一部分是昂贵的,那么值得将其重构为自己的视图以成为Equatable
。更多阅读:https://www.hackingwithswift.com/example-code/language/how-to-conform-to-the-equatable-protocol您的数据模型可以重构为
ObservableObject
或StateObject
和Publisher
,您可以在其中使用 Combine 做一些巧妙的事情来减慢速度更新。首先想到的是使用throttle
来确保您发布的更新仅以 X 间隔反馈到您的视图中。更多阅读:https://rhonabwy.com/2019/12/15/combine-throttle-and-debounce/确保您的上传工作是在后台线程上完成的,并且您的 UI 更新是在主线程上进行的。根据您进行上传的方式,Combine 可能会再次帮助您,让您在更新进入时连续回调主线程(也可能没有 Combine,但 Combine 使事情变得非常简单)。更多阅读:http://trycombine.com/posts/subscribe-on-receive-on/
打开 Instruments,看看是什么造成了您所看到的所有内存使用。这应该会提示您在哪里进行分配以及在哪里进行优化。
最后,SwiftUI List
在 Catalina 上的表现充其量是值得怀疑的。在大苏尔更好。在 SwiftUI 2.0 中,您可以更多地依赖 LazyVStack
之类的东西,但这不是针对 Catalina 的解决方案。如果你真的绝望了,你可以回到 AppKit 并在那里制作你的列表,将它包装在 NSViewRepresentable