LoginSignup
4
3
お題は不問!Qiita Engineer Festa 2023で記事投稿!

【SwiftUI】傾きに応じて模様が変わる万華鏡を作る方法

Last updated at Posted at 2023-06-18

はじめに

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は表現の幅がさらに広がりそうです!

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3