はじめに
iOS17からSwiftUIでMetal Shaderが使えるようになり、より複雑な表現が可能になりました。
この記事では、用意されたShaderを使って簡単にその魅力を体験できるライブラリ「MetalUI」を紹介します。
作るもの
本物の万華鏡のように傾きに応じて模様が変わる万華鏡を作ります。
デモ
準備
MetalUIをSwift Package Managerでインストールします。
コード全体
import SwiftUI
import MetalUI // MetalUIをインポート
import CoreMotion // センサーを使うためにインポート
// 万華鏡で使う図形を定義
struct Kaleidoscope: Shape {
let sides: Int
let angle: Angle
func path(in rect: CGRect) -> Path {
let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
let radius = min(rect.width, rect.height) / 2
var path = Path()
for i in 0..<sides * 2 {
let sinAngle = sin(angle.radians * Double(i))
let cosAngle = cos(angle.radians * Double(i))
let rotation = CGAffineTransform(rotationAngle: CGFloat(angle.radians * Double(i)))
let position = CGPoint(x: center.x + sinAngle * radius, y: center.y + cosAngle * radius)
if i == 0 {
path.move(to: position)
} else {
path.addLine(to: position)
}
path.addLine(to: position.applying(rotation))
}
return path
}
}
// 図形を組み合わせる
struct KaleidoscopeView: View {
private let colors: [Color] = [.green, .green, .blue, .pink, .orange, .purple, .yellow, .red]
var body: some View {
ZStack {
ForEach(0..<8) { i in
Kaleidoscope(sides: 6, angle: Angle.degrees(Double(i) * 45))
.fill(self.colors[i % self.colors.count].opacity(0.9))
}
}
}
}
struct ContentView: View {
// 傾きを保持する変数
@State private var x: CGFloat = 0
private let motionManager = CMMotionManager()
var body: some View {
ZStack {
Rectangle().fill(Color.black)
KaleidoscopeView()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea()
.kaleidoscope(angle: Angle(radians: x)) // MetalUIのkaleidoscopeを使う
.onAppear() {
// センサーの更新間隔を設定
motionManager.accelerometerUpdateInterval = 0.1
// センサーの値を取得
motionManager.startAccelerometerUpdates(to: .main) { data, error in
if let data = data {
x = data.acceleration.x
}
}
}
.onDisappear() {
// センサーを止める
motionManager.stopAccelerometerUpdates()
}
}
}
おわりに
たったこれだけで万華鏡が完成しました。
これからのSwiftUIは表現の幅がさらに広がりそうです!