Clubhouseのスーパー楕円をSwiftUIで作る
近年流行りだした音声SNS, Clubhouse。
デザインの特徴の1つにふわっとした丸みを帯びた楕円のアイコンがあります。
これは「スーパー楕円」と言われているそうです。
丸よりも丸みを感じる!? スーパー楕円の魅力とデザイン | Spinners Inc.
この楕円を@tokoromさんがUIKitで実装してました。
スーパー楕円UIをiOS+Swiftで実装する | Spinners Inc.
この記事を読んでSwiftUIでも作りたくなったので、作ってみます。
実行環境
- Xcode 12.2
- iOS 14.2
実装方針
tokoromさんと同じようにもと記事の「簡易的な描き方(Vector Draw Tool)」で書かれていたアンカーポイントを移動する方法で実装します。
clipShape修飾子
SwiftUIではViewを任意の二次元図形でクリップするときはclipShape
修飾子を使います。
引数にShape
プロトコルに準拠した型を指定します。
今回はSuperEllipseShape
という型を作ってスーパー楕円を表現します。
まずは全体像のコードです。
struct SuperEllipse: View {
var body: some View {
Image(systemName: "moon")
.resizable()
.frame(width: 100, height: 100)
.background(Color.yellow)
.clipShape(SuperEllipseShape(rate: 0.75))
}
}
画像に対して、clipShape
をすることでクリッピングしてます。
SuperEllipseShape
の実装をみていきます。
SuperEllipseShape
実装はこちらです。
struct SuperEllipseShape: Shape {
let rate: CGFloat
func path(in rect: CGRect) -> Path {
let handleX: CGFloat = rect.size.width * rate / 2
let handleY: CGFloat = rect.size.height * rate / 2
let left = CGPoint(x: rect.minX, y: rect.midY)
let top = CGPoint(x: rect.midX, y: rect.minY)
let right = CGPoint(x: rect.maxX, y: rect.midY)
let bottom = CGPoint(x: rect.midX, y: rect.maxY)
var path = Path()
path.move(to: left) //
path.addCurve(to: top,
control1: CGPoint(x: left.x, y: left.y - handleY),
control2: CGPoint(x: top.x - handleX, y: top.y))
path.addCurve(
to: right,
control1: CGPoint(x: top.x + handleX, y: top.y),
control2: CGPoint(x: right.x, y: right.y - handleY)
)
path.addCurve(
to: bottom,
control1: CGPoint(x: right.x, y: right.y + handleY),
control2: CGPoint(x: bottom.x + handleX, y: bottom.y)
)
path.addCurve(
to: left,
control1: CGPoint(x: bottom.x - handleX, y: bottom.y),
control2: CGPoint(x: left.x, y: left.y + handleY)
)
return path
}
}
Shape
プロトコルにはpath
メソッドの準拠が必要です。
実装内容はtokoromさんの記事のSuperellipse
の実装とほぼほぼ同じです。
handleX
とhandleY
でベジエ曲線の調整数値を割り出しています。
left
,top
,right
,bottom
は矩形の辺のそれぞれ真ん中の座標です。
addCurve
メソッドはパスに3次ベジエ曲線を追加するメソッドです。
Endpointの座標とControl point1, 2を使って曲線を作成します。
SwiftUIのaddcurveメソッドのドキュメントよりUIBezierPathのほうが情報が詳しいです。
path.move(to: left)
path.addCurve(to: top,
control1: CGPoint(x: left.x, y: left.y - handleY),
control2: CGPoint(x: top.x - handleX, y: top.y))
このように書くことで
leftの座標からtopの座標までの直線でhandleX
とhandleY
分ずらしたcontrol pointで曲線を作ります。
addCurve
を実行したあとは現在位置はto
引数の座標、すなわちtop
に移動します。
これをtopからright, rightからbottom, bottomからleftにaddCurve
メソッドを一周させるとpathの完成です。
実行するとこのようなViewが表示されます。
まとめ
clipShape
修飾子とShape
プロトコルを使ってClubhouseのスーパー楕円をSwiftUIで実装してみました。
思ったよりも簡単でびっくりしています。
柔らかな印象のアイコンを作れるのでアプリで取り入れていきたいですね。
コード
本記事のコードは以下のgistに載せています。
https://gist.github.com/SatoTakeshiX/c7ac3d6c82258aae693a997b86036b69
参考文系
- 丸よりも丸みを感じる!? スーパー楕円の魅力とデザイン | Spinners Inc.
- スーパー楕円UIをiOS+Swiftで実装する | Spinners Inc.
- addCurve(to:controlPoint1:controlPoint2:) | Apple Developer Documentation
- Paths vs shapes in SwiftUI - a free Hacking with iOS: SwiftUI Edition tutorial
- How to mask/clip the bottom part of an Image in SwiftUI? - Stack Overflow
宣伝
インプレスR&D社より、「1人でアプリを作る人を支えるSwiftUI開発レシピ」発売中です。
「SwiftUIでアプリを作る!」をコンセプトにSwiftUI自体の解説とそれを組み合わせた豊富なサンプルアプリでどんな風にアプリ実装すればいいかが理解できる本となっています。
iOS 14対応、Widgetの作成も一章まるまるハンズオンで解説しています。
SwiftUIを学びたい方、ぜひこちらのリンクをチェックしてください!