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 设置动画?

如何在 SwiftUI 中为按下时的导航链接设置动画?

如何在swift / SwiftUI中为滑动手势设置菜单栏图标的操作

在 SwiftUI 中为 UITextView 设置初始视图偏移量

缩放后在滚动视图中设置新图像时出现问题