iOS 17+で使えるSwiftUI用のAPIに .contentTransition(.numericText())
というのがある。
使い方は簡単で
Text("\(value)")
.contentTransition(.numericText(value: value))
こうやって書けば、Textの中身が変わる時にアニメーションしてくれる。withAnimation {}
経由でStateは変えないといけないことに注意。
試してみる
でっかい乱数を生成して4桁ずつスペースで区切って表示する。
struct ContentView: View {
@State private var number: Int = 0
var body: some View {
VStack {
Text(format(number: number))
.font(Font(UIFont.monospacedDigitSystemFont(ofSize: 32, weight: .bold)))
.contentTransition(.numericText(countsDown: true))
Button("Random") {
withAnimation {
number = Int.random(in: 1...10000000000000000)
}
}
.buttonStyle(.borderedProminent)
}
}
func format(number: Int) -> String {
let formatter = NumberFormatter()
formatter.groupingSeparator = " "
formatter.groupingSize = 4
formatter.usesGroupingSeparator = true
formatter.minimumIntegerDigits = 16
return formatter.string(from: NSNumber(value: number)) ?? ""
}
}
実用性がある感じで試す
カウンターを実装すれば早いけど、すぐできちゃうので別の実装をしてみる。
struct CreditCardView: View {
@State private var cardNumber: String = "4111 1111 1111 1111"
@State private var show: Bool = false
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(Color(.lightGray).shadow(.drop(color: .black.opacity(0.2), radius: 4, x: 0, y: 0)))
.stroke(Color(.border), style: StrokeStyle(lineWidth: 1))
HStack {
HStack(spacing: 16) {
Image(systemName: "creditcard")
.resizable()
.scaledToFit()
.frame(width: 20)
.foregroundStyle(Color(.border))
Text(cardNumber)
.font(Font(UIFont.monospacedSystemFont(ofSize: 16, weight: .bold)))
.contentTransition(.numericText(countsDown: show))
}
Spacer()
Button(action: {
withAnimation {
show.toggle()
if show {
cardNumber = "4111 1111 1111 1111"
} else {
cardNumber = "**** **** **** 1111"
}
}
}, label: {
if show {
Image(systemName: "eye.fill")
.resizable()
.scaledToFit()
.frame(width: 20)
.foregroundStyle(Color.gray)
} else {
Image(systemName: "eye.slash.fill")
.resizable()
.scaledToFit()
.frame(width: 20)
.foregroundStyle(Color.gray)
}
})
}
.padding([.leading, .trailing], 16)
}
.frame(height: 60)
.padding([.leading, .trailing], 16)
}
}
これを実行するとこうなる
いい感じにアニメーションする処理が簡単なコードでかけてめでたい。
.numericText(countsDown: show)
でshowの値をみることで下がるアニメーションと上がるアニメーションをトグルできるようにしているので、隠す時と表示する時で対応した動きができるようになっている。