SwiftUI - 図形の作成と効果とグラデーション

1. 円

alt

Circle()
  .frame(width: 200, height: 200)

2. 楕円

alt

Ellipse()
  .frame(width: 200, height: 400)

3. 四角

alt

Rectangle()
  .frame(width: 200, height: 200)

4. 角丸

alt

RoundedRectangle(cornerRadius: 25.0)
  .frame(width: 200, height: 200)

5. カプセル

alt

Capsule()
  .frame(width: 200, height: 400)

6. 三角

alt

三角はないようなのでパスで作成

Path { path in
  path.move(to: CGPoint(x:100, y:0))
  path.addLine(to: CGPoint(x: 200, y: 200))
  path.addLine(to: CGPoint(x: 0, y: 200))
  path.addLine(to: CGPoint(x: 100, y: 0))
}
.frame(width: 200, height: 200)

path.move - サブパスを閉じずに終了し、新しいポイントを定義する
CGPoint - 2次元座標系の点
addLine(to:) - 現在のポイントから指定されたポイントまでの直線セグメントを追加

違う書き方

Path { path in
  path.addLines([
    CGPoint(x: 100, y: 0),
    CGPoint(x: 200, y: 200),
    CGPoint(x: 0, y: 200),
    CGPoint(x: 100, y: 0)
  ])
}
.frame(width: 200, height: 200)

addLines(_:) - 接続された一連の直線セグメントをパスに追加

7. 回転

alt

Rectangle()
  .rotationEffect(.degrees(45), anchor: .center)
  //.rotationEffect(.degrees(角度), anchor: 起点)

8. 3D回転

alt

Y軸を起点にした回転

Rectangle()
  .rotation3DEffect(
    .degrees(45), axis: (x: 0.0, y: 1.0, z: 0.0)
  )
  //.degrees(角度), axis: (x: 起点, y: 起点, z: 遠近法)

alt

X軸を起点にした回転

Rectangle()
  .rotation3DEffect(
    .degrees(45), axis: (x: 1.0, y: 0.0, z: 0.0)
  )

9. 色

alt

Rectangle()
  .stroke(Color.black, lineWidth: 10)
  .background(Rectangle().fill(Color.gray))
  .frame(width: 200, height: 200)

//.fill(Color.black)や、.foregroundColor(.black)でも変更できる

10. 影

alt

Rectangle()
  .shadow(color: .black, radius: 1, x: 15, y: 15)
  .foregroundColor(.gray)
//.shadow(color: 影の色, radius: 影のぼかし量, x: 影のX位置, y: 影のY位置)
//影のぼかし量 - 数字が低いほど濃い

11. 円錐グラデーション

alt

AngularGradient(gradient: Gradient(colors: [Color.green, Color.red, Color.blue, Color.yellow]), center: .center)
  .frame(width: 200, height: 200)

12. 線形グラデーション

alt

LinearGradient(gradient: Gradient(colors: [Color.green, Color.red, Color.blue, Color.yellow]), startPoint: .leading, endPoint: .trailing)
  .frame(width: 200, height: 200)

13. 放射状グラデーション

alt

RadialGradient(gradient: Gradient(colors: [Color.green, Color.red, Color.blue, Color.yellow]), center: .center, startRadius: 1, endRadius: 100)
  .frame(width: 200, height: 200)

14. ついでに

alt

SwiftUIチュートリアルにパスとシェイプを使って謎のバッジを作成するというのがあったので、形は少し違いますが簡略化したものを作成してみました。

import SwiftUI

struct BackView: View {
  static let gradientStart = Color(red: 239.0 / 255, green: 120.0 / 255, blue: 221.0 / 255)
  static let gradientEnd = Color(red: 239.0 / 255, green: 172.0 / 255, blue: 120.0 / 255)
  
  var body: some View {
    RoundedRectangle(cornerRadius: 45.0)
      .fill(LinearGradient(
        gradient: Gradient(colors: [Self.gradientStart, Self.gradientEnd]),
        startPoint: UnitPoint(x: 0.5, y: 0),
        endPoint: UnitPoint(x: 0.5, y: 0.6)
      ))
      .frame(width: 200, height: 200)
  }
}

struct BackView_Previews: PreviewProvider {
  static var previews: some View {
    BackView()
  }
}
import SwiftUI

struct FrontView: View {
  static let symbolColor = Color(red: 79.0 / 255, green: 79.0 / 255, blue: 191.0 / 255)
  
  let angle: Angle
  
  var body: some View {
    Path { path in
      path.addLines([
        CGPoint(x: 20, y: 20),
        CGPoint(x: 40, y: 100),
        CGPoint(x: 0, y: 100),
        CGPoint(x: 20, y: 20)
      ])
    }
    .fill(Self.symbolColor)
    .frame(width: 40, height: 200)
    .rotationEffect(angle, anchor: .center)
  }
}

struct FrontView_Previews: PreviewProvider {
  static var previews: some View {
    FrontView(angle: Angle(degrees: 5))
  }
}
import SwiftUI

struct ContentView: View {
  var body: some View {
    GeometryReader { geometry in
      ZStack {
        BackView()
        ForEach(0..<15) { i in
          FrontView(
            angle: .degrees(Double(i) / Double(15)) * 360
          )
        }
        .opacity(0.4)
      }
      .position(
        x: geometry.size.width / 2,
        y: geometry.size.height / 4
      )
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

15. GeometryReader

上記で使用しているGeometryReader { geometry in ... }というものは、公式ドキュメントでは、「親レイアウトに柔軟な優先サイズを返す」と書いてあり、body直下に配置すればディスプレイサイズを取得してくれます。使う機会がありそうなので書いておこうと思います。

var body: some View {
  GeometryReader { geometry in
    VStack {
      Text("width: \(Int(geometry.size.width))")
      Text("height: \(Int(geometry.size.height))")
    }
  }
}

GeometryReader | Apple Developer Documentation