iOS 14 から登場した自動角丸 ContainerRelativeShape
を、特定の角にだけ利用したいという記事です。
ContainerRelativeShape とは
iPhone 11 の端末自体や、 iOS14 から登場した Widget など、各箇所固有の角丸UIが増えてきています。
それぞれの箇所で適切な角丸半径で表示してくれるのが、ContainerRelativeShape
です。
SwiftUI gives you ContainerRelativeShape() in iOS 14, which automatically makes a corner radius concentric to its parent shape without needing to manually specify corner radius values ✨ pic.twitter.com/MO2IQuE7nx
— Jordan Singer (@jsngr) June 27, 2020
目的
左上に配置して | ContainerRelativeShape 適用 | 拡張して左上だけ角丸にする! |
---|---|---|
- 特定の角だけ角丸にしたい
- なおかつただの角丸ではなく自動計算の ContainerRelativeShape を使いたい
これを実現した、一番右の画像の実装を解説します。
コード
カスタム Shape
Shape
Protocol を用い、
角の情報を扱うちょうどよい UIRectCorner
OptionSet があるので利用して、
独自の ContainerRelativeShapeSpecificCorner
Struct を定義します。
struct ContainerRelativeShapeSpecificCorner: Shape {
private let corners: [UIRectCorner]
init(corner: UIRectCorner...) {
self.corners = corner
}
func path(in rect: CGRect) -> Path {
var p = ContainerRelativeShape().path(in: rect)
if corners.contains(.allCorners) {
return p
}
if !corners.contains(.topLeft) {
p.addPath(Rectangle().path(in: CGRect(x: rect.origin.x, y: rect.origin.y, width: rect.width / 2, height: rect.height / 2)))
}
if !corners.contains(.topRight) {
p.addPath(Rectangle().path(in: CGRect(x: rect.origin.x + rect.width / 2, y: rect.origin.y, width: rect.width / 2, height: rect.height / 2)))
}
if !corners.contains(.bottomLeft) {
p.addPath(Rectangle().path(in: CGRect(x: rect.origin.x, y: rect.origin.y + rect.height / 2, width: rect.width / 2, height: rect.height / 2)))
}
if !corners.contains(.bottomRight) {
p.addPath(Rectangle().path(in: CGRect(x: rect.origin.x + rect.width / 2, y: rect.origin.y + rect.height / 2, width: rect.width / 2, height: rect.height / 2)))
}
return p
}
}
利用例
// 本来
Image("camera")
.clipShape(ContainerRelativeShape())
// 今回のやつ (特定の角だけ角丸)
Image("camera")
.clipShape(ContainerRelativeShapeSpecificCorner(corner: .topLeft, .topRight))
// 全コードサンプル
struct SampleView: View {
var body: some View {
Group {
Image("camera")
.resizable()
.scaledToFill()
.frame(width: 80, height: 80)
.clipShape(ContainerRelativeShapeSpecificCorner(corner: .topLeft))
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding(8)
}
}