SwiftUI 无法动画化图像过渡
SwiftUI Can't Animate Image Transitions
我正在尝试制作一个简单的图像选择器,我希望从
将一个图像转换为另一个图像以进行动画处理。看起来是一件非常简单的事情,但是
我一直无法找到单一的解决方案。我一定真的错过了什么
这里很简单
这是一个简化版本:
struct ContentView: View {
let photos = ["gear", "person", "person.2", "car", "leaf"]
@State private var currentIndex: Int = 0
@State private var showAnimation: Bool = true
private var scalingFactor: CGFloat = 0.5
var body: some View {
VStack {
Text("Image Detection")
.font(.system(size: 30))
.fontWeight(.heavy)
.padding(.top, 30)
Spacer()
Image(systemName: photos[currentIndex])
.resizable()
.frame(width: 250, height: 250)
//this does nothing
//.transition(.move(edge: .bottom))
//this does nothing
//.animation(.spring(), value: currentIndex)
//this does nothing
//.animation(.easeInOut, value: showAnimation)
//this does nothing
//.transition(AnyTransition.opacity.animation(.easeInOut(duration: 1.0)))
//this does nothing
// .animation(showAnimation ? Animation.easeOut(duration: 2.0) : Animation.easeOut(duration: 1.0), value: showAnimation)
//this works - but alternates scaled and not with each image
//and is not really an animation
.scaleEffect(showAnimation ? 1.0 : 0.5)
Spacer()
HStack {
Button(action: {
if self.currentIndex >= self.photos.count {
self.currentIndex = self.currentIndex - 1
} else {
self.currentIndex = 0
}
withAnimation {
self.showAnimation.toggle()
}
}, label: {
Image(systemName: "arrowtriangle.backward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
Spacer()
Button(action: {
if self.currentIndex < self.photos.count - 1 {
self.currentIndex = self.currentIndex + 1
} else {
self.currentIndex = self.photos.count - 1
}
withAnimation {
self.showAnimation.toggle()
}
}, label: {
Image(systemName: "arrowtriangle.forward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
}
.padding(.horizontal, 50)
Spacer()
}//v
}//body
}//content view
如有任何指导,我们将不胜感激。 Xcode 13.2.1 iOS 15.2
SwiftUI 当前的方法是使用 .transition()
,因为 .animation()
已被弃用。
需要理解的重要一点是 .transition()
在视图出现或消失时触发。您的视图不会完全 re-drawn 只是因为您更改了一个 @State
变量:在您的代码中,Image
发生了变化,但它始终保留在视图中。
一种解决方案是触发图像完全消失并创建一个新图像re-appear。下面的代码根据 showAnimation
的状态执行此操作。看到我只用了.transition()
,但是为了一个不错的效果:
- 不对称
withAnimation()
闭包也包装了 currentIndex
的变化
struct Example: View {
let photos = ["gear", "person", "person.2", "car", "leaf"]
@State private var currentIndex: Int = 0
@State private var showAnimation: Bool = true
private var scalingFactor: CGFloat = 0.5
var body: some View {
VStack {
Text("Image Detection")
.font(.system(size: 30))
.fontWeight(.heavy)
.padding(.top, 30)
Spacer()
if showAnimation {
image
} else {
image
}
Spacer()
HStack {
Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex >= self.photos.count {
self.currentIndex = self.currentIndex - 1
} else {
self.currentIndex = 0
}
}
}, label: {
Image(systemName: "arrowtriangle.backward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
Spacer()
Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex < self.photos.count - 1 {
self.currentIndex = self.currentIndex + 1
} else {
self.currentIndex = self.photos.count - 1
}
}
}, label: {
Image(systemName: "arrowtriangle.forward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
}
.padding(.horizontal, 50)
Spacer()
}//v
}//body
private var image: some View {
Image(systemName: photos[currentIndex])
.resizable()
.frame(width: 250, height: 250)
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
}
我正在尝试制作一个简单的图像选择器,我希望从 将一个图像转换为另一个图像以进行动画处理。看起来是一件非常简单的事情,但是 我一直无法找到单一的解决方案。我一定真的错过了什么 这里很简单
这是一个简化版本:
struct ContentView: View {
let photos = ["gear", "person", "person.2", "car", "leaf"]
@State private var currentIndex: Int = 0
@State private var showAnimation: Bool = true
private var scalingFactor: CGFloat = 0.5
var body: some View {
VStack {
Text("Image Detection")
.font(.system(size: 30))
.fontWeight(.heavy)
.padding(.top, 30)
Spacer()
Image(systemName: photos[currentIndex])
.resizable()
.frame(width: 250, height: 250)
//this does nothing
//.transition(.move(edge: .bottom))
//this does nothing
//.animation(.spring(), value: currentIndex)
//this does nothing
//.animation(.easeInOut, value: showAnimation)
//this does nothing
//.transition(AnyTransition.opacity.animation(.easeInOut(duration: 1.0)))
//this does nothing
// .animation(showAnimation ? Animation.easeOut(duration: 2.0) : Animation.easeOut(duration: 1.0), value: showAnimation)
//this works - but alternates scaled and not with each image
//and is not really an animation
.scaleEffect(showAnimation ? 1.0 : 0.5)
Spacer()
HStack {
Button(action: {
if self.currentIndex >= self.photos.count {
self.currentIndex = self.currentIndex - 1
} else {
self.currentIndex = 0
}
withAnimation {
self.showAnimation.toggle()
}
}, label: {
Image(systemName: "arrowtriangle.backward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
Spacer()
Button(action: {
if self.currentIndex < self.photos.count - 1 {
self.currentIndex = self.currentIndex + 1
} else {
self.currentIndex = self.photos.count - 1
}
withAnimation {
self.showAnimation.toggle()
}
}, label: {
Image(systemName: "arrowtriangle.forward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
}
.padding(.horizontal, 50)
Spacer()
}//v
}//body
}//content view
如有任何指导,我们将不胜感激。 Xcode 13.2.1 iOS 15.2
SwiftUI 当前的方法是使用 .transition()
,因为 .animation()
已被弃用。
需要理解的重要一点是 .transition()
在视图出现或消失时触发。您的视图不会完全 re-drawn 只是因为您更改了一个 @State
变量:在您的代码中,Image
发生了变化,但它始终保留在视图中。
一种解决方案是触发图像完全消失并创建一个新图像re-appear。下面的代码根据 showAnimation
的状态执行此操作。看到我只用了.transition()
,但是为了一个不错的效果:
- 不对称
withAnimation()
闭包也包装了currentIndex
的变化
struct Example: View {
let photos = ["gear", "person", "person.2", "car", "leaf"]
@State private var currentIndex: Int = 0
@State private var showAnimation: Bool = true
private var scalingFactor: CGFloat = 0.5
var body: some View {
VStack {
Text("Image Detection")
.font(.system(size: 30))
.fontWeight(.heavy)
.padding(.top, 30)
Spacer()
if showAnimation {
image
} else {
image
}
Spacer()
HStack {
Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex >= self.photos.count {
self.currentIndex = self.currentIndex - 1
} else {
self.currentIndex = 0
}
}
}, label: {
Image(systemName: "arrowtriangle.backward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
Spacer()
Button(action: {
withAnimation {
showAnimation.toggle()
if self.currentIndex < self.photos.count - 1 {
self.currentIndex = self.currentIndex + 1
} else {
self.currentIndex = self.photos.count - 1
}
}
}, label: {
Image(systemName: "arrowtriangle.forward.fill")
})
.padding()
.foregroundColor(Color.blue)
.font(.largeTitle)
}
.padding(.horizontal, 50)
Spacer()
}//v
}//body
private var image: some View {
Image(systemName: photos[currentIndex])
.resizable()
.frame(width: 250, height: 250)
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
}