SwiftUI - 在当前视图中为新图像设置动画
Posted
技术标签:
【中文标题】SwiftUI - 在当前视图中为新图像设置动画【英文标题】:SwiftUI - animating a new image inside the current view 【发布时间】:2020-04-04 18:00:45 【问题描述】:我有一个 View,我使用 Picture(image) 子视图来显示图像,该图像可以有不同的高度和宽度格式。
对图像的引用是从数组中提取的,这允许我通过改变引用在我的视图中显示不同的图像。 SwiftUI 重新排列每个新图像的视图内容
我想在这张图片上做一个动画,比如显示图片时的缩放效果
1) 我需要第一个 .animation(nil) 以避免对前一个图像进行动画处理(否则我会出现难看的淡出和纵横比变形)。看起来不错
2) 但是后来我遇到了 scaleEffect 修饰符的问题(即使我把它设置为 scale = 1,它应该什么也不做) 动画从图像 1 移动到图像 2,通过强制图像 2 的左上角从图像 1 的左上角位置开始,具有不同的宽度和高度,会引起图像中心的不必要的平移 这在下面的代码中重现,出于演示目的,我使用系统映像(不容易出现错误 1))
我怎样才能避免这种情况?
3) 在下面的演示代码中,我用一个按钮触发了新图像,这使我可以使用一个动作并处理“缩放”修改并明确实现所需的效果。但是在我的真实代码中,图像修改是由另一个视图中的另一个更改触发的。
Swift 知道这一点,因此我可以使用隐式 .animation 修饰符。
但是,我不知道如何为任何新图像强制重置“比例”并执行我想要的效果。
如果我使用 onAppear(我的代码),它只适用于显示的第一张图片,不适用于以下图片。
在实际代码中,我有一个 Picture(image) 视图,而 Picture(image.animation()) 没有编译。
知道如何在隐式动画的 Button 中实现以下代码中的操作吗?
谢谢
import SwiftUI
let portrait = Image(systemName: "square.fill")
let landscape = Image(systemName: "square.fill")
struct ContentView: View
@State var modified = false
@State var scale: CGFloat = 1
var body: some View
return VStack(alignment: .center)
Pictureclip(bool: $modified)
.animation(nil)
.scaleEffect(scale)
.animation(.easeInOut(duration: 1))
Button(action:
self.modified.toggle()
self.scale = 1.1
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
self.scale = 1
)
Text("Tap here")
.animation(.linear)
struct Pictureclip: View
@Binding var bool: Bool
var body: some View
if bool == true
return portrait
.resizable()
.frame(width: 100, height: 150)
.foregroundColor(.green)
else
return landscape
.resizable()
.frame(width: 150, height: 100)
.foregroundColor(.red)
【问题讨论】:
您使用哪个 Xcode 版本?没有那个.animation(nil)
,这里的动画效果很好。使用 Xcode 11.4 / ios 13.4 测试。所以我不确定我是否达到了你想要达到的效果。
是的,没有 .animation(nil),如果你使用非 jpeg 图片,它的动画效果很好,但如果你放了真实的图片(我不能在这里演示),它不会。 (11.4)。您可以尝试使用 let Portrait = Image("joconde") let Landscape = Image("venus")
仍然可以很好地处理几张经过测试的 jpeg 图片。
您在横向和纵向模式下使用过 jpeg 吗?我肯定有一个不保持纵横比的动画,一个像 Botero 一样寻找一秒钟的 Joconde 和一个像 Giacometti 一样的 Venus
这发生在画布和模拟器中
【参考方案1】:
我的问题有一个半答案,即第 1 点和第 2 点(此处参考资产目录中的两个 jpeg 图像)
import SwiftUI
let portrait = Image("head")
let landscape = Image("sea")
struct ContentView: View
@State var modified = false
@State var scale: CGFloat = 0.95
var body: some View
VStack()
GeometryReader geo in
VStack
Picture(bool: self.modified)
.frame(width: geo.size.width * self.scale)
Spacer()
Button(action:
self.scale = 0.95
self.modified.toggle()
withAnimation(.easeInOut(duration: 0.5))
self.scale = 1
)
Text("Tap here")
struct Picture: View
var bool: Bool
var body: some View
if bool == true
return portrait
.resizable().aspectRatio(contentMode: .fit)
.padding(.all,6.0)
else
return landscape
.resizable().aspectRatio(contentMode: .fit)
.padding(.all,6.0)
此解决方案可以在动画期间不扭曲新图像的纵横比进行缩放。但它不适用于在另一个视图中触发图像更新的代码。我想我必须重组我的代码,要么解决我的问题,要么更清楚地暴露它。
编辑:一个快速而肮脏的解决方案是将触发代码(这里是按钮中的操作代码)放在另一个视图中。即,将动画视图 A 的代码放入视图 B,并传递一个状态变量(此处为“缩放”)。我敢肯定有更清洁的方法,但至少这是可行的。
【讨论】:
【参考方案2】:我不确定,但也许对你有帮助。 使用 DataBinding 结构。我是这样使用的:
let binding = Binding<String>(get:
self.storage
, set: newValue in
self.textOfPrimeNumber = ""
self.storage = newValue
let _ = primeFactorization(n: Int(self.storage)!, k: 2, changeable: &self.textOfPrimeNumber)
)
【讨论】:
以上是关于SwiftUI - 在当前视图中为新图像设置动画的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中为 Start->Max->Start 设置动画?
如何在swift / SwiftUI中为滑动手势设置菜单栏图标的操作