-
[ios - Swift] Phi Chart View (CALayer)ios 2021. 1. 12. 10:18
()
이번 글에서는 지난번에 배운 UIBezierPath, CAShapeLayer를 사용해 파이 차트를 만들어 보도록 하겠습니다.
import UIKit class UIPieChatView: UIView { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { super.init(coder: coder) } override func setNeedsDisplay() { super.setNeedsDisplay() definePieChatLayer() } private func definePieChatLayer() { } }
UIView를 상속받은 기본적인 클래스 구성입니다.
앞으로 이 클래스에 사용자가 원하는 값을 넣게 되면 우리는 setNeedsDisplayer()를 호출해 뷰를 다시 그려줄 수 있도록 하면 됩니다.
class UIPieChatView: UIView { private let pieBezierPath = UIBezierPath() var colors: [CGColor]? { didSet { setNeedsDisplay() } } var values: [CGFloat]? { didSet { setNeedsDisplay() } } 중략 ....
파이 차트의 원하는 컬러와 밸류 값을 배열 형태로 추가해 줄 수 있도록 합니다.
파이 차트를 채워줄 베지어 패스도 마찬가지로 추가하고 이제 setNeedsDisplayer()를 호출 시 definePieChatLayer()를 호출해서 레이어를 추가해주면 됩니다.
private func definePieChatLayer() { guard let values = values else { return } guard let colors = colors else { return } let center = CGPoint(x: bounds.midX, y: bounds.midY) var sAngle: CGFloat = 0 var eAngle: CGFloat = 0 for i in 0..<values.count { sAngle = eAngle eAngle = sAngle + values[i] pieBezierPath.removeAllPoints() pieBezierPath.move(to: center) pieBezierPath.addArc(withCenter: center, radius: 150, startAngle: sAngle * 2 * CGFloat.pi, endAngle: eAngle * 2 * CGFloat.pi, clockwise: true) pieBezierPath.close() let pieLayer = CAShapeLayer() pieLayer.path = pieBezierPath.cgPath pieLayer.lineWidth = 5 pieLayer.strokeEnd = 1 pieLayer.fillColor = colors[i] pieLayer.strokeColor = UIColor.white.cgColor layer.addSublayer(pieLayer) } 중략 ....
베지어 패스에 values 값을 계산해 호를 그리고 컬러의 값을 채울 수 있도록 하였습니다.
strokeColor를 화이트로 변경해 구분선을 표시할 수 있도록 하였습니다.
definePieChatLayer()를 사용하려면 Values, colors를 설정해 줘야겠죠?
ViewContoller에 가서 UIView를 추가하고 Class를 UIPieChatView 변경한 후 아래 코드를 추가해 줍니다.
@IBOutlet weak var piChat: UIPieChatView! override func viewDidLoad() { piChat.colors = [UIColor.red.cgColor, UIColor.orange.cgColor, UIColor.yellow.cgColor, UIColor.green.cgColor] piChat.values = [0.25, 0.45, 0.10, 0.20] }
각 순서에 맞는 %로 합이 1의 값을 가질 수 있도록 했습니다.
중간 결과를 보면 아래와 같이 나타나는 것을 볼 수 있습니다.
이제 텍스트 CATextLayer를 이용하여 각 부분에 해당하는 비율이 얼마인지 보여주도록 합니다.
private func definePieChatLayer() { ... 중략 for i in 0..<values.count { sAngle = eAngle eAngle = sAngle + values[i] mAngle = sAngle + ((eAngle - sAngle) / 2) // ---- 추가 ... 중략 let y:CGFloat = sin((mAngle * 2 * CGFloat.pi)) * 100 // ---- 추가 let x:CGFloat = cos((mAngle * 2 * CGFloat.pi)) * 100 // ---- 추가 let pieFontLayer = CATextLayer() pieFontLayer.frame = CGRect(x: center.x + x, y: center.y + y, width: 0, height: 0).insetBy(dx: -50, dy: -7.5) pieFontLayer.foregroundColor = UIColor.white.cgColor pieFontLayer.string = "\(values[i] * 100)%" pieFontLayer.alignmentMode = .center pieFontLayer.fontSize = 15 pieFontLayer.font = "System" as CFTypeRef pieFontLayer.isWrapped = true layer.addSublayer(pieLayer) layer.addSublayer(pieFontLayer) // ---- 추가 } }
mAngle이라는 변수는 각 호의 중간 부분 라디언값을 계산해 텍스트를 추가해 줄 좌표를 구한 것입니다.
구한 좌표값에서 insetBy를 사용해 텍스트 프레임을 구하도록 했습니다.
각 영역에 해당하는 부분에 텍스트가 자리한 걸 확인할 수 있습니다.
이제 애니메이션을 사용해 파이가 그려지는 것처럼 보일 수 있도록 변경하겠습니다.
class UIPieChatView: UIView { private let maskLayer = CAShapeLayer() private let maskBezierPath = UIBezierPath() ... 중략 private func definePieChatLayer() { ... maskBezierPath.removeAllPoints() maskBezierPath.addArc(withCenter: center, radius: 75, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true) maskLayer.path = maskBezierPath.cgPath maskLayer.lineWidth = 150 maskLayer.strokeEnd = 0 maskLayer.fillColor = UIColor.clear.cgColor maskLayer.strokeColor = UIColor.black.cgColor layer.mask = maskLayer }
마스크로 사용할 레이어와 베지어 패스를 선언하고 호의 반지름을 75로 설정합니다.
파이의 반지름이 150이어서 마스킹할 호를 75로 설정 후 라인 굵기를 150으로 선언해서 굵은 선이 파이를 채운다고 보면 됩니다.
func phiAnimate(duration: CFTimeInterval) { maskLayer.strokeEnd = 0 maskLayer.removeAnimation(forKey: "maskAnimation") pieAnimation.keyPath = "strokeEnd" pieAnimation.duration = duration pieAnimation.toValue = 1 pieAnimation.fillMode = .forwards pieAnimation.isRemovedOnCompletion = false maskLayer.add(pieAnimation, forKey: "maskAnimation") }
뷰 컨트롤러에서 phiAnimate()를 호출해주도록 합니다.
'ios' 카테고리의 다른 글
[ios - SwiftUI] Widget 사용하기 (1/2) (0) 2021.02.04 [ios-Swift] Share Extension 사용하기 (0) 2021.01.27 [ios - Swift] Circle Progressbar 만들기 (CALayer) (0) 2021.01.08 [ios - Swift] FileManager 사용한 저장, 불러오기 (0) 2021.01.05 [ios - Swift] MapView 사용하기 (MKMapView 2 / 2) (0) 2020.12.23