swiftUI 中的逐帧动画
frame by frame animation in swiftUI
我很喜欢使用 swiftUI,但感觉它的动画引擎有限。我有一个图像,我想通过一系列 png 图像制作动画,但我找不到这个的默认实现(或使用隐式、显式或过渡动画的方式)。我是否需要构建一个自定义系统来为该动画的每一帧重绘整个 UI,或者是否有内置的方法来执行此操作?预先感谢您的帮助
好的,所以我在本机 swift 情节提要中做到了这一点,但它的概念相同。在 SwiftUI 中,我从情节提要中翻译的内容与此类似:
var index = 0
var imageArray = ["image1", "image2", "image3"]
Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block:
(timer) in {
index++
if (index >= imageArray.count) {
index = 0
}
self.background(Image("\(imageArray[index])").resizable()
}
)
这将每 2 秒根据之前定义的数组切换放入的任何内容的背景图像。
希望这能解决您的问题!
对于动画,如果您想要 逐帧 可操作性,我建议您使用 CADisplayLink
将函数调用同步到屏幕的帧速率.这是一个非常强大的工具,尤其是对于动画,因此有了 CA - CoreAnimation。
虽然灵活,但您不能直接在 SwiftUI View
上使用它,因为 View
是一个协议而不是 class
,它不允许 [=15] =] 功能。所以你必须创建一个 class/VM 来管理动画。一个低级的例子如下
class ImageAnimator: ObservableObject {
// Image to add animation/transitions
@Published var image: Image = Image()
private var link: CADisplayLink!
init() {
// Adding the link to a runloop
link = CADisplayLink(target: self, selector: #selector(update))
link.add(to: .current, forMode: RunLoop.Mode.default)
}
@objc func update() {
// Do something
}
deinit {
// Removes link from all runloops
link.invalidate()
}
}
这是一个基于 Timer
的解决方案,它有助于循环遍历图像数组并仅 cancel
计时器停止动画。这里的过渡类型 .scale
用于演示。
struct ContentView: View {
let images = ["img1", "img2", "img3", "img1"]
@State private var idx = 0
private let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var transition: AnyTransition = .scale
var body: some View {
ZStack {
ForEach(images.indices, id: \.self) { index in
if self.idx == index {
Image(self.images[index])
.transition(transition)
}
}
}.onReceive(timer) { _ in
withAnimation {
self.idx = self.idx < (self.images.count - 1) ? self.idx + 1 : 0
}
}
}
}
我很喜欢使用 swiftUI,但感觉它的动画引擎有限。我有一个图像,我想通过一系列 png 图像制作动画,但我找不到这个的默认实现(或使用隐式、显式或过渡动画的方式)。我是否需要构建一个自定义系统来为该动画的每一帧重绘整个 UI,或者是否有内置的方法来执行此操作?预先感谢您的帮助
好的,所以我在本机 swift 情节提要中做到了这一点,但它的概念相同。在 SwiftUI 中,我从情节提要中翻译的内容与此类似:
var index = 0
var imageArray = ["image1", "image2", "image3"]
Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block:
(timer) in {
index++
if (index >= imageArray.count) {
index = 0
}
self.background(Image("\(imageArray[index])").resizable()
}
)
这将每 2 秒根据之前定义的数组切换放入的任何内容的背景图像。
希望这能解决您的问题!
对于动画,如果您想要 逐帧 可操作性,我建议您使用 CADisplayLink
将函数调用同步到屏幕的帧速率.这是一个非常强大的工具,尤其是对于动画,因此有了 CA - CoreAnimation。
虽然灵活,但您不能直接在 SwiftUI View
上使用它,因为 View
是一个协议而不是 class
,它不允许 [=15] =] 功能。所以你必须创建一个 class/VM 来管理动画。一个低级的例子如下
class ImageAnimator: ObservableObject {
// Image to add animation/transitions
@Published var image: Image = Image()
private var link: CADisplayLink!
init() {
// Adding the link to a runloop
link = CADisplayLink(target: self, selector: #selector(update))
link.add(to: .current, forMode: RunLoop.Mode.default)
}
@objc func update() {
// Do something
}
deinit {
// Removes link from all runloops
link.invalidate()
}
}
这是一个基于 Timer
的解决方案,它有助于循环遍历图像数组并仅 cancel
计时器停止动画。这里的过渡类型 .scale
用于演示。
struct ContentView: View {
let images = ["img1", "img2", "img3", "img1"]
@State private var idx = 0
private let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
var transition: AnyTransition = .scale
var body: some View {
ZStack {
ForEach(images.indices, id: \.self) { index in
if self.idx == index {
Image(self.images[index])
.transition(transition)
}
}
}.onReceive(timer) { _ in
withAnimation {
self.idx = self.idx < (self.images.count - 1) ? self.idx + 1 : 0
}
}
}
}