🔋
SwiftUIでバッテリーアニメーションを作成する方法
はじめに
この記事では、SwiftUIを使用してバッテリーの充電レベルを表示するアニメーションを作成する方法を説明していきます。
完成したバッテリーアニメーションは以下のようになります。
環境
- Xcode: 14.3.1
- iOS: 16.4
- Swift: 5.8.1
実装
バッテリーの充電状態を表示
まずはバッテリーを表示するソースコードをBatteryAnimationView.swiftにまとめていきます。
充電量(progress)を与えられた時に、残りの充電量をバッテリーの見た目とテキストで表現出来るところまで実装していきましょう。
title=BatteryAnimationView.swift
import SwiftUI
struct BatteryAnimationView: View {
@Binding var progress: Double
let size: CGFloat
var body: some View {
ZStack {
HStack(spacing: 0) {
// Battery frame
Rectangle()
.stroke(.black, lineWidth: size / 50)
.frame(width: size, height: size / 2)
.background(
// Battery gauge
Rectangle()
.fill(.green)
.scaleEffect(
x: progress,
y: 1,
anchor: .leading
)
)
// Battery head
Rectangle()
.fill(.black)
.frame(width: size / 20, height: size / 5)
}
Text("\(Int(self.progress * 100))%")
.foregroundColor(.black)
.font(.system(size: size / 6))
}
}
}
struct BatteryAnimationView_Previews: PreviewProvider {
static var previews: some View {
BatteryAnimationView(progress: .constant(0.7), size: 300)
}
}
ContentView.swiftでBatteryAnimationView.swiftを使用します。
充電量の値をランダムに代入するボタンを追加して、テスト出来るようにしましょう。
title=ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var progress = 1.0
var body: some View {
VStack {
BatteryAnimationView(progress: $progress, size: 300)
Button(action: randomizeProgress) {
Text("Update")
.font(.headline)
.foregroundColor(.white)
}
.padding()
.background(Color.blue)
.cornerRadius(10)
}
}
private func randomizeProgress() {
self.progress = Double.random(in: 0.0...1.0)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
挙動は以下のようになります。
色を動的に変更
充電量が残り10%のときは充電ゲージを赤色、残り20%のときは黄色、それ以外の場合は緑色になるようにしましょう。
ゲージの色を表す変数、gaugeColorを導入します。
title=BatteryAnimationView.swift
import SwiftUI
struct BatteryAnimationView: View {
@Binding var progress: Double
let size: CGFloat
+ private var gaugeColor: Color {
+ if progress <= 0.1 {
+ return .red
+ } else if progress <= 0.2 {
+ return .yellow
+ } else {
+ return .green
+ }
+ }
var body: some View {
ZStack {
HStack(spacing: 0) {
// Battery frame
Rectangle()
.stroke(.black, lineWidth: size / 50)
.frame(width: size, height: size / 2)
.background(
// Battery gauge
Rectangle()
- .fill(.green)
+ .fill(gaugeColor)
.scaleEffect(
x: progress,
y: 1,
anchor: .leading
)
)
// Battery head
Rectangle()
.fill(.black)
.frame(width: size / 20, height: size / 5)
}
Text("\(Int(self.progress * 100))%")
.foregroundColor(.black)
.font(.system(size: size / 6))
}
}
}
struct BatteryAnimationView_Previews: PreviewProvider {
static var previews: some View {
BatteryAnimationView(progress: .constant(0.7), size: 300)
}
}
挙動は以下のようになります。
アニメーションの追加
最後にアニメーションを追加します。
withAnimationを使用して、onAppearには画面の表示時のアニメーションの処理、randomizeProgressに充電量が変化したときのアニメーションの処理を追加します。
title=ContentView.swift
import SwiftUI
struct ContentView: View {
@State private var progress = 0.0
var body: some View {
VStack {
BatteryAnimationView(progress: $progress, size: 300)
Button(action: randomizeProgress) {
Text("Update")
.font(.headline)
.foregroundColor(.white)
}
.padding()
.background(Color.blue)
.cornerRadius(10)
}
+ .onAppear {
+ withAnimation(.interpolatingSpring(
+ stiffness: 20.0,
+ damping: 8.0)) {
+ self.progress = 1.0
+ }
+ }
}
private func randomizeProgress() {
- self.progress = Double.random(in: 0.0...1.0)
+ withAnimation(.interpolatingSpring(stiffness: 20.0, damping: 8.0)) {
+ self.progress = Double.random(in: 0.0...1.0)
+ }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
テキストのアニメーションは必要ないので、テキストのみをアニメーション処理の対象外とします。
title=BatteryAnimationView.swift
import SwiftUI
struct BatteryAnimationView: View {
@Binding var progress: Double
let size: CGFloat
private var gaugeColor: Color {
if progress <= 0.1 {
return .red
} else if progress <= 0.2 {
return .yellow
} else {
return .green
}
}
var body: some View {
ZStack {
HStack(spacing: 0) {
// Battery frame
Rectangle()
.stroke(.black, lineWidth: size / 50)
.frame(width: size, height: size / 2)
.background(
// Battery gauge
Rectangle()
.fill(gaugeColor)
.scaleEffect(
x: progress,
y: 1,
anchor: .leading
)
)
// Battery head
Rectangle()
.fill(.black)
.frame(width: size / 20, height: size / 5)
}
Text("\(Int(self.progress * 100))%")
.foregroundColor(.black)
.font(.system(size: size / 6))
+ .animation(nil)
}
}
}
struct BatteryAnimationView_Previews: PreviewProvider {
static var previews: some View {
BatteryAnimationView(progress: .constant(0.7), size: 300)
}
}
挙動は以下のようになります。(最初に示した動画と同じ)
さいごに
GitHubからソースコードを得る場合はこちら
参考URL
Flutterにおけるバッテリーアニメーションの実装
SwiftUIにおけるバッテリーアニメーションの実装
Discussion