Circle Timer 만들기(SwiftUI, CoreGraphics, Paths)
Circle Timer 만들기(SwiftUI, CoreGraphics, Paths)
목표
- 타임 타이머 형태의 원형 뷰를 만든다.
- 사용자의 움직임에 따라서 각도를 변경한다.
원 그리기
타임 타이머 Mod 의 핵심은 시각적으로 붉은색 원이 줄어드는데 있다. 따라서 자유 자제로 저 원을 그릴 수 있어야 한다. 원을 그리는 방법에는 다양한 방법이 있지만 코어 그래픽을 사용해서 그려야 자유롭게 그릴 수 있다고 판단했다.
전통적(UIKit)으로 저런 형태의 도형을 그리는 방법은 이미 잘 알려져 있고 별반 다르지 않을거라 생각했다.
Core Graphics Tutorial Part 1: Getting Started
코어 그래픽을 사용해 커스텀 뷰를 만드는 방법으로는 2가지가 있다.
- Shape를 상속해 만들기
- Path를 사용해 만들기
Path를 사용할지 Shape를 사용할지 고민이라면 다른 포스트를 보고 정리한 포스트가 있으니 확인해 보자.
apple의 SwiftUI 가이드에서는 도형을 그리는 방법으로 Path를 사용해 그리는 방법이 나와있다.
이 포스트에서는 Path를 사용해 화면을 그리는 방법을 확인한다.
Custom View
먼저 커스텀 뷰를 만든다. 이 뷰는 화면을 그리는 역할만 수행하려고 한다.
struct CircleView: View {
var body: some View {
// 뷰 구현
}
}
우리가 그릴 원은 360도의 각도에서 어디서 어디까지 원을 채울지 알아야 한다. 그리고 원의 색상은 뭔지, 원을 채우지 않은 부분의 색상은 무슨 색인지 알 필요가 있다.
// 시작하는 각도
var startPercent: CGFloat = 0
// 끝나는 각도
var endPercent: CGFloat = 50
// 원의 색상
var color: Color = .red
// 원의 배경 색상
var backgroundColor: Color = .black
시계와 마찬가지로 12시 방향을 0이라고 생각하고 50까지 늘리도록 했다.
Path를 이용해 위와 같은 화면을 그리려면 어떻게 해야 할까?
GeometryReader { geometryProxy in
ZStack(alignment: .center) {
Circle()
.foregroundColor(self.color)
Path { path in
let size = geometryProxy.size
let center = CGPoint(x: size.width / 2.0,
y: size.height / 2.0)
let radius = min(size.width, size.height) / 2.0
path.move(to: center)
path.addArc(center: center,
radius: radius,
startAngle: .init(degrees: Double(self.startPercent)),
endAngle: .init(degrees: Double(self.endPercent)),
clockwise: true)
}
.rotation(.init(degrees: 270))
.foregroundColor(self.backgroundColor)
.frame(width: geometryProxy.size.width,
height: geometryProxy.size.height,
alignment: .center)
}
}
GeometryReader 를 사용해 할당된 화면의 사이즈를 읽어온다. Shape의 경우 path(in:) 메소드에서 Rect 를 가져오기 때문에 사이즈를 알 수 있지만 Path에서는 사이즈를 알 수 없으니 GeometryReader를 사용해야 한다.
Path의 내부를 확인해보면 사이즈와 Center 값을 가져와 path를 이용해 원을 그린다.
그냥 그리면 원이 약간 틀어져서 실행된다. 그래서 뷰를 조금 돌려줬다.
.rotation(.init(degrees: 270))
다음 포스트에서는 Shape를 이용해 똑같이 그려보려한다.