SwiftUI——iOS15新版Animation制作动画介绍

从iOS 15.0开始,苹果废弃了之前的.animation(Animation?),建议开发者使用.animation(Animation?, value: Equatable)或者withAnimation替代。

个人感觉改版之后虽然可能有些不习惯,但是开发的可能性和自由度更大了。

但是在讲二者区别之前,我们需要了解一下UI动画。如果你了解这部分可以跳过。

何为动画

动画是由各种位移、颜色变化、大小变化等属性变化的过程。
具体到SwiftUI的View,就是它们的.offset.foregroundColor.frame等属性逐渐变化的时候产生的效果。
但是如果你直接改变变量,属性会直接变化,非常生硬,这不能被称为动画。苹果提供了非常简单的实现过度动画的办法,那就是.animation

可以理解成我们设定好关键帧,然后设置中间帧就可以做出动画啦!

不同样式的过度动画(中间帧)

苹果提供了很简单的方式,但是或许是太简单了,就没有人详细说说这个。

首先动画运动节奏也分很多种,例如缓进缓出、匀速运动等,所以这里介绍一下苹果提供的几种模式(如果是nil就是无动画):
请添加图片描述
具体的代码后面会列出来,因为有一个特性这里还没提,所以这里先不列出代码。

新旧版本不同之处

现在我们具体说明一下.animation(Animation?,).animation(Animation?, value: Equatable)二者之间的不同之处。
老版的.animation(Animation?)是放在需要运动或者改变的View下面,当View改变的时候,出现过渡动画,过渡动画包括运动、样式、颜色等属性。
新版.animation(Animation?, value: Equatable)比老版多了一个参数,那就是value
这个参数value的作用是当这个值发生改变的时候,才会出现应用我们指定的过度动画,过渡动画也包括运动、样式、颜色等属性。如果View的其他属性发生变化,则不会出现动画。
所以上面那个动画的代码如下:

struct AnimationView: View {
    @State private var offsetX: CGFloat = 150
    var body: some View {
        VStack {
            VStack {
                Text(".default(默认)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    /* 如果是`nil`就是无动画 */
                    .animation(.default, value: offsetX)
                
                Text(".linear(匀速)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.linear, value: offsetX)
                
                Text(".easeIn(开头缓慢,逐渐加速)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeIn, value: offsetX)
                
                Text(".easeOut(结尾逐渐减速,缓慢结束)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeOut, value: offsetX)
                
                Text(".easeInOut(缓出缓进,上面的融合体)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeInOut, value: offsetX)
            }
            
            Button(action: {
                offsetX = -150
            }, label: {
                Text("开始运动")
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(lineWidth: 3)
                    )
            })
        }
    }
}

但是由于这个value,我们只能当这个值发生变化的时候才能有动画,那么如果我们需要在观察到几个值当中的某一个发生变化就有动画的话,那该怎么办呢?

如果你要观察的值是同一个类型的话,那么可以使用数组,例如[offsetX, offsetY],这样的话就相当于观察这个数组有没有变化了,只要有一个值发生变化,那么数组就会变化。这样可以达到我们需要的效果。

需要观察多个值该怎么办

如果需要观察的值不是同一个类型的,例如我们要观察位移和颜色,一个是CGFloat类型,一个是Color类型。这样就很麻烦了。所以这里列举有几个解决方案,以防在某种情况下,某种方法会更加麻烦:

第一种:
可以搞个数据结构,然后观察数据。或许某种情况你用这种方法很方便,
首先新建一个结构体,协议是Equatable,如下:

struct Test: Equatable {
    var offsetX: CGFloat
    var color: Color
}

然后是View部分:

.animation(.default, value: Test(offsetX: offsetX, color: color))

这种方法相对来说通用一些,而且可以观察多次变化,就是有点麻烦。

第二种:
由于value是符合Equatable协议的,也就是说这里的value其实是个Bool值,我们虽然写作value: color这种,但是实际上它是在判断这个值有没有发生变化,相当于color == 之前的color,通过这个来返回一个Bool值(很经典的C语言编程技巧,如果你熟悉C语言应该很容易就明白了)。
但是我们不能直接放个Bool值在这,就想它必须出动画或者不出动画,那是不会的。它是必须要判断这个值有没有发生变化的。
所以我们可以写成这样:

.animation(.default, value: offsetX == 150 && color == Color.pink)

这种方法的写法相对简单,但是由于只对比一个值,所以某些情况下可能不好用。

动画时间长度

我们可以设置过渡动画的时长,来控制动画的快慢和节奏,这个很简单。老版本的可能很多人都会用,但是新版本由于官方没提,网上也没人说,所以这里讲一下。

在动画样式的后面加个(duration: 时长)即可,时长的单位是

//这里表示动画时长为3秒
.animation(.linear(duration: 3), value: offsetX)

总结

看到这里你应该就可以做出简单的动画啦,不过我还会写一些相关的概念、技巧的博客,来帮助需要的人。

希望能帮到有需要的人~

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS 15 中,可以使用 AVFoundation 中的 AVAssetReader 来解析视频每帧,然后将每帧呈现到 SwiftUI 中的 View 上。 首先,您需要创建一个 AVAssetReader 对象,并为其提供一个 AVAsset 对象。然后,创建一个 AVAssetReaderTrackOutput 对象,并将其添加到 AVAssetReader 上,以便从视频中读取帧。 接下来,您可以使用 AVAssetReaderTrackOutput 的 copyNextSampleBuffer() 方法来获取视频中的下一帧。将返回的 CMSampleBuffer 转换为 CGImage,然后将其呈现到 SwiftUI View 上的 UIImageView 中。 以下是一个简单的示例代码,它演示了如何在 SwiftUI 中解析视频每帧: ```swift import SwiftUI import AVFoundation struct VideoPlayerView: View { let videoURL: URL var body: some View { VideoPlayer(url: videoURL) .onAppear { let asset = AVAsset(url: videoURL) let reader = try! AVAssetReader(asset: asset) let track = asset.tracks(withMediaType: .video)[0] let output = AVAssetReaderTrackOutput(track: track, outputSettings: nil) reader.add(output) reader.startReading() while let sampleBuffer = output.copyNextSampleBuffer() { let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! let ciImage = CIImage(cvPixelBuffer: imageBuffer) let cgImage = CIContext().createCGImage(ciImage, from: ciImage.extent)! let uiImage = UIImage(cgImage: cgImage) let imageView = UIImageView(image: uiImage) // Do something with imageView CMSampleBufferInvalidate(sampleBuffer) } } } } ``` 注意,此示例仅演示了如何解析视频每帧,并将其呈现到 UIImageView 中。您需要根据自己的需求适当地调整代码,以便将每帧呈现到您的 SwiftUI View 上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值