はじめに
今回はカスタムアニメーションションの作り方を書きたいと思います。
前回ViewModifierを使った付箋を作りましたが、今回はその付箋部分をアニメーションさせたいと思います。
アニメーション部分に特化するため、形状は角丸のないシンプルなものを土台にします。
カスタムアニメーション実装の手順
今回のアニメーションで変更させなければならない値をまずみてみましょう。
struct StickyNoteModifier: ViewModifier {
var ceaseSize: CGFloat = 50.0
ViewModifier
内のceaseSize: CGFloat
の値を変更すればアニメーションすることになります。
で、どうやって?
このViewModifier
をアニメーションさせるためには下記の実装が必要となります。
-
ViewModifier
をAnimatable
にする。 -
ceaseSize: CGFloat
がアニメーションの対象であることを宣言する。
の以上の2つです。
まず1つ目、『ViewModifier
をAnimatable
にする』ですが、超簡単です。ViewModifier
をAnimatableModifier
に書き換えるだけです。これでこのViewModifier
はAnimatable
になります。
struct StickyNoteModifier: AnimatableModifier {
var ceaseSize: CGFloat = 50.0
2つ目はのceaseSize: CGFloat
ですが、computed property
のanimatableData
を実装するだけです。
この辺りは次回ですこし詳しく説明したいともいますが、とりあえず実装してみましょう。
var animatableData: CGFloat {
get { ceaseSize}
set {ceaseSize = newValue}
}
これで終わりです。
あとはViewの設定部分でModifierとアニメーションの設定で終わりです。
下記の例では1秒間隔で折り返しの幅を0<->50の間でアニメーションさせています。
struct StickyNoteView: View {
@State private var widthCease:Bool = true
var body: some View {
Text("This is AnimatableStickyNoteView Sample")
.stickyNote(color: .green, ceaseSize: widthCease ? 50 : 0, shadowLength: 10)
.onAppear() {
withAnimation(Animation.easeInOut(duration: 1.0).repeatForever()) { self.widthCease.toggle() }
}
}
}
とても簡単で独自のアニメーションの実装が可能ですね。
コード全文
import SwiftUI
struct StickyNoteView: View {
@State private var widthCease:Bool = true
var body: some View {
VStack{
Spacer()
Text("If today were the last day of my life, would I want to do what I am about to do today")
.font(.headline)
.lineLimit(5)
.frame(width: UIScreen.main.bounds.width/2)
.stickyNote(color: .green, ceaseSize: widthCease ? 50 : 0, shadowLength: 10)
.onAppear() {
withAnimation(Animation.easeInOut(duration: 1.0).repeatForever()) { self.widthCease.toggle() }
}
Spacer()
}
}
}
extension View {
func stickyNote(color: Color, ceaseSize: CGFloat, shadowLength: CGFloat)->some View {
self.modifier(StickyNoteModifier(color: color, ceaseSize: ceaseSize, shadowLength: shadowLength))
}
}
struct StickyNoteModifier: AnimatableModifier {
var color: Color = .green
var ceaseSize: CGFloat = 50.0
var shadowLength: CGFloat = 10.0
private let divide: CGFloat = 2
private let paddingHorizontal: CGFloat = 30.0
private let paddingVertical: CGFloat = 40.0
var animatableData: CGFloat {
get { ceaseSize}
set {ceaseSize = newValue}
}
func body(content:Content) -> some View {
ZStack(content: {
content
.padding(.horizontal, 30)
.padding(.vertical, 40)
.background(StickyNote())
.shadow(radius: shadowLength)
})
}
// StickyNote drawing
func StickyNote() -> some View {
return GeometryReader { geometry in
ZStack {
Path { path in
let w = geometry.size.width
let h = geometry.size.height
let d = min(w/self.divide, h/self.divide)
let m = min(d, self.ceaseSize)
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: h))
path.addLine(to: CGPoint(x: w-m, y: h))
path.addLine(to: CGPoint(x: w, y: h-m))
path.addLine(to: CGPoint(x: w, y: 0))
path.addLine(to: CGPoint(x: 0, y: 0))
}
.fill(self.color)
Path { path in
let w = geometry.size.width
let h = geometry.size.height
let d = min(w/self.divide, h/self.divide)
let m = min(d, self.ceaseSize)
path.move(to: CGPoint(x: w-m, y: h))
path.addLine(to: CGPoint(x: w-m, y: h-m))
path.addLine(to: CGPoint(x: w, y: h-m))
path.addLine(to: CGPoint(x: w-m, y: h))
}
.fill(Color.black).opacity(0.4)
}
}
}
}
struct StickyNoteView_Previews: PreviewProvider {
static var previews: some View {
StickyNoteView()
}
}