ben*_*lis 5 ios swift apple-watch watchkit swiftui
我正在将 Apple Watch 应用程序从 WatchKit 转换为 SwiftUI。
该应用程序利用动画WKInterfaceImage以高性能的方式循环显示一系列图像。
该应用程序有 16 张图像的序列,这些图像在 0.66 秒内循环播放。图像是小 png(~8kb 256 x 256 像素)。
可以在此处找到资产目录中图像的完整 zip 文件。
为此,我创建了一个简单的FrameAnimatedImageView如下:
struct FrameAnimatedImageView: View{
let imageName: String
let frameCount: Int
let duration: TimeInterval
@State private var currentFrameIndex: Int
private var currentImageName: String {
"\(imageName)\(currentFrameIndex)"
}
@State private var timer: Timer?
init(imageName: String, frameCount: Int, duration: TimeInterval) {
self.imageName = imageName
self.frameCount = frameCount
self.duration = duration
self.currentFrameIndex = 0
}
var body: some View {
Image(currentImageName)
.resizable()
.aspectRatio(contentMode: .fit)
.onAppear(perform: startAnimationTimer)
.onDisappear(perform: stopAnimationTimer)
}
private func startAnimationTimer() {
stopAnimationTimer()
timer = Timer(timeInterval: duration / Double(frameCount), repeats: true) { (Timer) in
currentFrameIndex = (currentFrameIndex + 1) % frameCount
}
RunLoop.main.add(timer!, forMode: .common)
}
private func stopAnimationTimer() {
timer?.invalidate()
timer = nil
}
}
Run Code Online (Sandbox Code Playgroud)
这给出了相同的结果,但使用了更多的 CPU(S6 Apple Watch 上大约 15%,而WKInterfaceImage动画则使用 <1%)。
我做了一些分析,发现 swiftUI 重绘是主要活动,其次是图像加载。
基于此,我做了一些优化,以使用 UImages(和内置缓存)来填充 SwiftUI 图像视图。
struct FrameAnimatedImageView: View{
let imageName: String
let frameCount: Int
let duration: TimeInterval
@State private var currentFrameIndex: Int
private var currentImageName: String {
"\(imageName)\(currentFrameIndex)"
}
private var currentImage: UIImage { // Slight optimisation
UIImage(named: currentImageName)!
}
@State private var timer: Timer?
init(imageName: String, frameCount: Int, duration: TimeInterval) {
self.imageName = imageName
self.frameCount = frameCount
self.duration = duration
self.currentFrameIndex = 0
}
var body: some View {
Image(uiImage: currentImage) // Slight optimisation
.resizable()
.aspectRatio(contentMode: .fit)
.onAppear(perform: startAnimationTimer)
.onDisappear(perform: stopAnimationTimer)
}
private func startAnimationTimer() {
stopAnimationTimer()
timer = Timer(timeInterval: duration / Double(frameCount), repeats: true) { (Timer) in
currentFrameIndex = (currentFrameIndex + 1) % frameCount
}
RunLoop.main.add(timer!, forMode: .common)
}
private func stopAnimationTimer() {
timer?.invalidate()
timer = nil
}
}
@available(watchOSApplicationExtension 7.0, *)
struct FrameAnimatedImageView_Previews: PreviewProvider {
static var previews: some View {
FrameAnimatedImageView(imageName: "running", frameCount: 16, duration: 0.66)
}
}
Run Code Online (Sandbox Code Playgroud)
性能(该动画图像上使用了 10-15% 的手表 CPU)仍然远非理想,我并不热衷于采用这种方法。
我已经尝试/考虑过以下选项:
通过将 WKImageView 包装在 WKInterfaceObjectRepresentable 中来使用它。
这似乎不可能,因为 WKImageView 没有公开公共初始化。详细信息:如何为 SwiftUI 包装自定义 WKInterfaceImage
使用 SwiftUI Canvas 绘制图像
使用 SwiftUI Canvas 并绘制图像,而不是使用 SwiftUi Image。从快速尝试来看,这似乎并没有减少 CPU 使用率(实际上它使情况变得更糟)。
创建一个 UIImage 动画图像并将其传递给 Swift Image
使用 UIImage 上的内置动画支持仅拥有一个 UIImage 对象,然后将其传递到 SwiftUI Image 视图。这不起作用,SwiftUI 仅绘制第一帧。
还要别的吗?
我不确定是否有更好的方法?
Rob*_*Rob -2
为了获得更好的性能,恐怕你必须包装 UIKit。试一试:
struct FrameAnimatedImageView: View {
let imageName: String
let duration: TimeInterval
var body: some View {
AnimatedImageView(name: imageName, duration: duration)
.aspectRatio(contentMode: .fit)
}
}
struct AnimatedImageView: UIViewRepresentable {
let name: String
let duration: TimeInterval
func makeUIView(context: Self.Context) -> UIView {
let view = UIImageView()
view.contentMode = .scaleAspectFit
view.image = .animatedImageNamed(name, duration: duration)
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<AnimatedImageView>) {
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
957 次 |
| 最近记录: |