Path
와 Shape
에 대해 알아보도록 합시다.
Path
SwiftUI는 사용자가 원하는 Custom Shape
를 그릴 수 있도록 Path
를 제공하고 있습니다.
Color
, grandient
및 shape
와 마찬가지로 Path
는 그 자체로 View
입니다. 이것은 우리가 TextField
와 Image
처럼 사용할 수 있다는 소리죠.
Paths
는 위치 값을 가진 선, 곡선 및 기타 정보를 가진 목록입니다. 하지만 Shape
는 다른 정보를 미리 알 수 없죠. Shape
내부에 path(in:)
메서드가 호출이 끝나야 최종 적인 사이즈를 알 수 있습니다.
Paths
는 절 때 경로 안에서 좌표값에 맞춰 도형을 그리지만 Shape
는 path(in:)
에서 주어진 Rect
를 기반으로 상대 경로를 받아서 그리게 됩니다.
Path
를 만드는 방법에는 몇 가지 방법이 있습니다. 사각형, 원, 사다리꼴 등 모양을 자유롭게 만들 수 있죠.
기본 도형
SwiUI에서는 앱에서 사용할 수 있는 몇 가지 기본 모양을 제공하고 있습니다.
- Rectangle (직사각형)
- RoundedRectangle (둥근 모서리 직사각형)
- Ellipse (타원)
- Circle (원)
- Capsule (캡슐)

자 이제 Path
를 사용하여 몇 가지 도형을 만들어 보겠습니다. Path
인스턴스와 해당 클로저를 삽입해 보겠습니다. 기본적으로 SwiftUI는 검은색으로 색을 채웁니다. 일단 Path
에 대해 더 배우기 쉽게 바깥쪽 테두리만 보이게 하겠습니다.
struct PathView: View { var body: some View { Path { path in } .stroke() } }
사각형
Path
에 여러 개의 선을 추가하여 사각형을 그려보도록 하겠습니다. 좌표인 X
및 Y
를 사용하여 이것을 그릴 수 있습니다. 첫 번째 선을 그리기 전에 가상의 사각형의 오른쪽 상단 모서리로 커서를 이동하고 오른쪽 하단 모서리로 가는 선을 추가해야 합니다. 그런 다음 addLine
을 이용하여 실제 선을 그려줘야 합니다.
struct PathView: View { var body: some View { Path { path in // 1. 커서 이동 path.move(to: CGPoint(x: 200, y: 0)) // 2. path.addLine(to: CGPoint(x: 200, y: 200)) // 3. path.addLine(to: CGPoint(x: 0, y: 200)) } .stroke() } }
여기까지 작성하면 SwiftUI에 두 개의 선이 추가된 것을 확인할 수 있습니다. 왼쪽 상단 모서리를 가리키는 세 번째 선을 추가하여 사각형을 완성해야 합니다. 시작 위치를 가리키는 마지막 줄을 추가하여 사각형을 닫을 수 있지만 .closeSubPath
수정자를 사용하여 '자동으로' Path
를 닫을 수 도 있습니다.
struct PathView: View { var body: some View { Path { path in // 1. 오른쪽 모서리로 커서 이동 path.move(to: CGPoint(x: 200, y: 0)) // 2. path.addLine(to: CGPoint(x: 200, y: 200)) // 3. path.addLine(to: CGPoint(x: 0, y: 200)) // 4. 왼쪽 모서리로 커서 이동 path.addLine(to: CGPoint(x: 0, y: 0)) // 5. 자동으로 경로를 닫음 path.closeSubpath() } .stroke() } }

자 이렇게 정사각형의 Path
를 정의 했으므로 .stroke
수정자를 삭제하여 결과를 확인해 보겠습니다.
var body: some View { Path { path in // ... } // .stroke 삭제 }
.fill()
수정자를 사용하여 도형의 색을 채울 수 있습니다.
var body: some View { Path { path in // ... } .fill(Color.blue) }
결과

삼각형
삼각형을 만들어 보겠습니다.
struct PathView: View { var body: some View { Path { path in // 1. 커서 이동 path.move(to: CGPoint(x: 200, y: 100)) // 2. path.addLine(to: CGPoint(x: 100, y: 300)) // 3. path.addLine(to: CGPoint(x: 300, y: 300)) // 4. 시작한 경로로 이동 path.addLine(to: CGPoint(x: 200, y: 100)) } .stroke() } }

자 언듯 봐서는 정확히 삼각형이 만들어진 것처럼 보이지만 Stroke
를 사용해서 결과를 보겠습니다.
struct PathView: View { var body: some View { Path { path in ... } .stroke(Color.blue, linewidth: 20) } }

상단에 모서리 부분이 깨져있습니다. 이 문제를 해결하기 위해서는 두 가지 방법이 있는데, 첫 번째 선을 다시 그리거나 두 번째 colseSubpath()
수정자를 사용하면 됩니다.
struct PathView: View { var body: some View { Path { path in // 1. 커서 이동 path.move(to: CGPoint(x: 200, y: 100)) // 2. path.addLine(to: CGPoint(x: 100, y: 300)) // 3. path.addLine(to: CGPoint(x: 300, y: 300)) // 4. path.addLine(to: CGPoint(x: 200, y: 100)) // 5. path.closeSubpath() } .stroke(Color.blue, lineWidth: 20) //.stroke(Color.blue.opacity(0.3)) } }

모서리 부분 둥글게
도형의 모서리 부분을 둥글게 만들어 줄 수 돼있습니다. 바로 StrokeStyle
구조체를 사용하는 것이죠.
struct PathView: View { var body: some View { Path { path in ... } .stroke(Color.blue, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round)) } }

Stroke StylelineJoin
은 선들이 만나는 모서리의 모양을 설정함. lineCap
은 선의 끝 모양을 설정함
결과

Shape
Shpae
도 Path
로 구성됩니다. Shape
프로토콜을 채택한 Struct
를 선언하여 Square
Path
를 아래와 같은 Shpae
로 간단히 변환할 수 있습니다.
struct MySquare: Shape { }
struct MySquare: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: 200, y: 0)) path.addLine(to: CGPoint(x:200, y: 200)) path.addLine(to: CGPoint(x: 0, y: 200)) path.addLine(to: CGPoint(x: 0, y: 0)) path.closeSubpath() return path } } struct MyShape: View { var body: some View { MySquare() } }
이런 식으로 함수를 만들어서 Shape
를 사용할 수 있습니다. 그런데 MySquare
의 Shape
는 이전에 정의했던 절대 좌표를 계속 사용하고 있습니다. 이거 대신에 SwiftUI View
내의 MySquare
인스턴스에 .frame
수정자를 추가하여 변환할 수 있도록 동적으로 만들어 줄 수 있습니다.
struct MySquare: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: rect.size.width, y: 0)) path.addLine(to: CGPoint(x: rect.size.width, y: rect.size.width)) path.addLine(to: CGPoint(x: 0, y: rect.size.width)) path.addLine(to: CGPoint(x: 0, y: 0)) path.closeSubpath() return path } }
자 이제 이 함수를 body
에 호출해보겠습니다.
struct MyShape: View { var body: some View { MySquare() .frame(width: 250, height: 250) } }

위에 보이는 것처럼 왼쪽 MyShape
에 .frame
를 할당해서 오른쪽과 같은 결과를 생성했습니다.
우리는 커서를 보이지 않는 직사각형의 오른쪽 상단 가장자리로 이동했고, 그런 다음 X
및 Y
좌표 모두에 대해 rect
의 너비를 사용하여 path
를 다른 점으로 그리도록 지시했습니다. 그리고 다음 하위 path
를 닫기 전에 왼쪽 하단 모서리로 돌아갔습니다. 이렇게 동적인 MySquare
Shape
를 만들었고 그렇기 때문에 .frame
수정자를 사용하여 크기를 조정할 수 있는 거죠!
곡선 모양
이제 조금 더 복잡한 곡선 모양을 만드는 법에 대해 알아보겠습니다. Raindrop
이라는 struct
를 선언하고 Shape
프로토콜을 준수합니다.
struct Raindrop: Shape { func path(in rect: CGRect) -> Path { Path { path in } } }
일단 body
에서 Raindrop
을 호출하고 이곳에 .stroke
와 .frame
수정자를 적용해주겠습니다.
struct MyShape: View { var body: some View { Raindrop() .stroke(lineWidth: 4) .frame(width: 200, height: 200) } }
다시 Raindrop
경로 내에서 커서를 직사각형의 위쪽 가장자리 중간으로 이동하면서 시작하도록 하겠습니다.
Path { path in path.move(to: CGPoint(x: rect.size.width/2, y: 0)) }
다음으로 오른쪽 곡선을 아래쪽으로 그려야 합니다. 이를 위해서 addQuadCurve
메서드를 사용해야 합니다. 이 기능은 path
에 곡선을 추가하는 기능입니다. 이 곡선의 경우에는 곡선의 끝점을 정의해야 하지만 실제로 곡선을 수행하려면 Control Point을 제공해 줘야 합니다. 이러한 Control Point은 곡선의 강도와 방향을 계산하는 데 사용됩니다.

곡선의 방향과 강도는 해당 Control Point을 배치하는 위치에 따라 달라집니다. Control Point을 더 멀리 배치할수록 곡선이 더 많이 구부러지게 되는 것이죠.
addQuadCurve
함수에 대한 지식을 살펴보도록 하겠습니다. 커브가 직사각형의 아래쪽 가장자리 중간에서 끝나야 한다는 path
를 지정해주고 Control Point를 직사각형의 오른쪽 아래 가장자리에 배치합니다.
Path { path in path.move(to: CGPoint(x: rect.size.width/2, y: 0)) path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: rect.size.height), control: CGPoint(x: rect.size.width, y: rect.size.height)) }

이제 처음 시작했던 지점을 가리키는 다른 곡선을 그려서 빗방울 모양을 완성시켜 줍니다.
struct Raindrop: Shape { func path(in rect: CGRect) -> Path { Path { path in path.move(to: CGPoint(x: rect.size.width/2, y: 0)) path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: rect.size.height), control: CGPoint(x: rect.size.width, y: rect.size.height)) path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: 0), control: CGPoint(x: 0, y: rect.size.height)) } } }

이렇게 해서 빗방울 모양이 만들어졌습니다! 이제 .stroke
수정자 대신 .fill
을 사용하여 색을 채울 수 있습니다. 저는 Gradient
로 채워주겠습니다.
struct MyShape: View { var body: some View { Raindrop() .fill(LinearGradient(gradient: Gradient(colors: [Color.white, Color.blue]), startPoint: .topLeading, endPoint: .bottom)) .frame(width: 200, height: 200) } }
결과

읽어주셔서 감사합니다🤟
본 게시글의 전체 코드 GitHub 👇🏻
Seogun95/SwiftUI_Path_TUT
SwiftUI에서 Path를 사용하여 도형만들기. Contribute to Seogun95/SwiftUI_Path_TUT development by creating an account on GitHub....
github.com
[SwiftUI 기초/Image] - SwiftUI : Shape (Rectangle, Circle, Capsule...)
[Swift/문법 및 이론] - CGSize와 CGRect의 차이점과 CGPoint
[SwiftUI 기초/Image] - SwiftUI : trim( ) - Shape의 일부 그리기 (Timer)
'SWIFTUI > Image' 카테고리의 다른 글
SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] (0) | 2022.01.07 |
---|---|
SwiftUI : AspectRatio / GeometryReader / GeometryProxy (3) | 2021.04.04 |
SwiftUI : ContainerRelativeShape (위젯에서만 사용가능) (0) | 2021.03.13 |
SwiftUI : trim( ) - Shape의 일부 그리기 (Timer) (0) | 2021.03.13 |
SwiftUI : Shape (Rectangle, Circle, Capsule...) (0) | 2021.01.22 |