I figured out a pure SwftUI animation from image array :
It is using an animatable modifier that handle the images to be displayed in place of a view (here an button with an image). When you tap a button its image animates. If you tap again image stop animating and default button image
is displayed:
//
// ImageArrayAnimationView
//
// Created by Pitt Xav
//
import SwiftUI
struct ImageArrayAnimationView: View {
// List of images
private let images: [Image] = [
Image(systemName: "icloud.and.arrow.up"),
Image(systemName: "icloud.and.arrow.down"),
Image(systemName: "cloud"),
Image(systemName: "cloud.drizzle"),
Image(systemName: "cloud.rain"),
Image(systemName: "cloud.heavyrain")
]
// Images and percent variable use for animating rain down
private let imagesOrderDown = [2, 4, 5]
@State var percentDown: Float = 0
// Images and percent variable use for animating rain up
@State var percentUp: Float = 0
private let imagesOrderUp = [5, 3, 2]
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
// tapping a button set percent to :
// 0 = no animation (Animation.default)
// 100 = animation with images
Button {percentDown = percentDown < 50 ? 100 : 0} label: {
Image(systemName: "icloud.and.arrow.down")
.resizable()
.frame(width: 150, height: 150, alignment: .top)
.imageArrayAnimation(images: images, imagesOrder: imagesOrderDown, percent: percentDown)
.animation(percentDown <= 0 ? Animation.default : .linear(duration: 1).repeatForever(autoreverses: false), value: percentDown)
.border(.foreground, width: 2)
Spacer()
}
Button {percentUp = percentUp < 50 ? 100 : 0} label: {
Image(systemName: "icloud.and.arrow.up")
.resizable()
.frame(width: 150, height: 150, alignment: .top)
.imageArrayAnimation(images: images, imagesOrder: imagesOrderUp, percent: percentUp)
.animation(percentUp <= 0 ? Animation.default : .linear(duration: 1).repeatForever(autoreverses: false), value: percentUp)
.border(.foreground, width: 2)
Spacer()
}
}
.frame(width: 100, height: 100, alignment: .center)
Spacer()
}
}
}
struct ImageAnimation: AnimatableModifier {
// Animation through th percent property
// percent is converted into a position in an index array
// this index value enable to choose whch image in the array
// to display
var animatableData: Float {
get {
percent
}
set {
percent = newValue
}
}
var images: [Image]
var imgesOrder: [Int]
var percent: Float
var currentImageIndex: Int {
if percent > 0 {
let index = Int((percent / 100) * Float(imgesOrder.count))
print("% \(percent) \(index)")
return imgesOrder[min(index, imgesOrder.count-1)]
} else {
return 0
}
}
func body(content: Content) -> some View {
ZStack(alignment: .top) {
// for the animation to work content and image
// needs to always be there
// opacity enable to choose what to actually display
content
.opacity(percent == 0 ? 1 : 0)
images[currentImageIndex]
.resizable()
.scaledToFit()
.opacity(percent == 0 ? 0 : 1)
}
}
}
// extension to have mnamed modifier
extension View {
func imageArrayAnimation(images: [Image], imagesOrder: [Int], percent: Float) -> some View {
self.modifier(ImageAnimation(images: images, imgesOrder: imagesOrder, percent: percent))
}
}
struct ImageArrayAnimationView_Previews: PreviewProvider {
static var previews: some View {
ImageArrayAnimationView()
}
}
private var image = Image(uiImage: images[index])
(It's not needed I guess) – Kara