Reputation: 4731
I am trying to create an animation where a small heart icon is pumping. I have the two images I believe are sufficient to create the effect, but I have no idea how to create this animation effect. I have tried several things and none of them seem to work.
Any help you can offer will be greatly appreciated.
Here's what I have so far:
@State var show : Bool = false
var body: some View {
VStack(alignment: .trailing){
ZStack{
BlackView()
if(show){
Image("heartOrgan1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height:50)
.hidden()
Image("heartOrgan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
} else {
Image("heartOrgan1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
Image("heartOrgan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.hidden()
}
}
.onAppear(){
withAnimation { self.show.toggle()}
}
}
}
The general idea is to loop the switching between two heart images that represent a heart beating. I am interested in using these particular heart images as they look like actual hearts, and I like that.
Upvotes: 4
Views: 4284
Reputation: 4425
Check out this code from Apple Documentation. I am pretty sure it works.
This is not using any images.
import SwiftUI
struct Heart: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.maxY ))
path.addCurve(to: CGPoint(x: rect.minX, y: rect.height/4),
control1:CGPoint(x: rect.midX, y: rect.height*3/4),
control2: CGPoint(x: rect.minX, y: rect.midY))
path.addArc(center: CGPoint( x: rect.width/4,y: rect.height/4),
radius: (rect.width/4),
startAngle: Angle(radians: Double.pi),
endAngle: Angle(radians: 0),
clockwise: false)
path.addArc(center: CGPoint( x: rect.width * 3/4,y: rect.height/4),
radius: (rect.width/4),
startAngle: Angle(radians: Double.pi),
endAngle: Angle(radians: 0),
clockwise: false)
path.addCurve(to: CGPoint(x: rect.midX, y: rect.height),
control1: CGPoint(x: rect.width, y: rect.midY),
control2: CGPoint(x: rect.midX, y: rect.height*3/4))
return path
}
}
struct ResetHeart: View {
var body: some View {
Heart()
.frame(width: 100, height: 100)
.foregroundColor(.red)
.shadow(color: .pink, radius: 10)
.frame(width: 300, height: 300)
}
}
struct PulsingHeart: View {
@State private var heartPulse: CGFloat = 1
var body: some View {
Heart()
.frame(width: 100, height: 100)
.foregroundColor(.red)
.scaleEffect(heartPulse)
.shadow(color: .pink, radius: 10)
.onAppear{
withAnimation(.easeInOut.repeatForever(autoreverses: true)) {
heartPulse = 1.25 * heartPulse
}
}
}
}
struct HeartPulseView: View {
@State private var pulsing = false
var body: some View {
VStack {
Spacer()
ZStack {
if pulsing {
PulsingHeart()
} else {
ResetHeart()
}
}
Spacer()
PlayResetButton(animating: $pulsing)
}
.navigationTitle("Basic Animation")
.navigationBarTitleDisplayMode(.inline)
}
}
struct HeartPulseView_Previews: PreviewProvider {
static var previews: some View {
HeartPulseView()
}
}
Refer the following link for more examples https://developer.apple.com/tutorials/sample-apps/animatingshapes
Upvotes: 0
Reputation: 21
I would slightly modify the great answer provided by Jame to make the animation a little bit more realistic, just by changing the linear animation for a spring animation like this:
struct ContentView: View {
@State private var animationAmount: CGFloat = 1
var body: some View {
ZStack {
Color.black
Image(systemName: "heart.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.red)
.scaleEffect(animationAmount)
.animation(
.spring(response: 0.2, dampingFraction: 0.3, blendDuration: 0.8) // Change this line
.delay(0.2)
.repeatForever(autoreverses: true),
value: animationAmount)
.onAppear {
animationAmount = 1.2
}
}
}
}
You can play with repetition, dampingFraction and blenDuration parameters to get a more customised animation.
:)
Upvotes: 1
Reputation: 645
You don't necessarily need two images for this. You can use one image and apply the scale effect on it. Make the one image scale up and add a delay to it. Then make it repeat.
Example:
@State private var animationAmount: CGFloat = 1
var body: some View {
ZStack {
Color.black
Image(systemName: "heart.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.red)
.scaleEffect(animationAmount)
.animation(
.linear(duration: 0.1)
.delay(0.2)
.repeatForever(autoreverses: true),
value: animationAmount)
.onAppear {
animationAmount = 1.2
}
}
}
You can also change the decimal value in inside the delay()
to have different heartbeats. The heartbeat looks consistent with delays between 0.1 - 0.4.
Upvotes: 10