ํ๋ก์ ํธ ์ค ํฅ๋ฏธ๋กญ๊ฒ ํ์ตํ ๋ถ๋ถ์ด ์์ด ๊ธฐ๋กํด๋ณด์๋ค.
์ฐธ๊ณ : https://www.tnoda.com/blog/2019-06-18/
๋ฒ ์ง์ด ๊ณก์ ์ด๋?
- n๊ฐ์ ์ ์ผ๋ก๋ถํฐ ์ป์ด์ง๋ n-1์ฐจ ๊ณก์
- ์ ํ์์ ์ ๊ณตํ๋
UIBezierPath
๋ฅผ ํ์ฉํ์ฌ ์ง์ถ์ ๋ํ ํ์ด์ฐจํธ๋ฅผ ๊ทธ๋ ค๋ณด๊ธฐ๋ก ํ์๋ค. - ์ํ๊ธฐ ๊ตฌํ ํ๋ก์ ํธ ๋ ํ์ฉํ๋ ๋ฐฉ๋ฒ๋ณด๋ค๋, ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ์ฌ activeํ UI๋ฅผ ๋ํ๋ด๋ณด๊ณ ์ ํ๋ค.
VC์์ animate ๋ฉ์๋ ํธ์ถ
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
reportPieChartView.slices = setupSlices()
reportPieChartView.animateChart()
}
- viewDidAppear ๋ฉ์๋ ๋ด๋ถ์์, ํ๋ฉด์ด ๋ณด์ฌ์ง ๋๋ง๋ค slice(์ฐจํธ์ ํํํ ๋ชจ๋ธ)์ setupํ๊ณ animateChart ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
func animateChart() {
sliceIndex, // ํ์ฌ ๊ทธ๋ฆฌ๊ณ ์ ํ๋ slice์ index
currentPercent, // radian์ผ๋ก ๋ฐ๊ฟ์ค percentage
superView layer์ sublayers,
์ด์ ์ ๋จ์์๋ percentageLabels ์ด๊ธฐํ
if sliceIndex < slices.count {
addSlice(firstSlice)
addLabel(firstSlice)
}
}
- ํ์, CAAnimationDelegate์ ์ ๋๋ฉ์ด์ ์ด ๋๋ฌ์ ๋ ์ฐ์์ ์ผ๋ก layer๋ฅผ ๊ทธ๋ฆด ์ ์๋๋ก ์ฒซ๋ฒ์งธ slice๋ง addํด์ค๋ค.
// ๊ฐ๊ฐ์ ํผ์ผํธ์ ์์ด ๋ด๊ธด ์ฌ๋ผ์ด์ค๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์
ํ์ฌ ๊ทธ๋ฆผ
private func addSlice(_ slice: Slice) {
// strikeEndํค๋ก ์ ๋๋ฉ์ด์
์์ฑ.
let animation = CABasicAnimation(keyPath: "strokeEnd")
// strokeEnd์ ๊ฒฝ์ฐ value ๋ฒ์๋ 0~1๊น์ง. ๋ชจ๋ ๋ฒ์์ ํด๋นํ๋ ์ ๋๋ฉ์ด์
animation.fromValue = 0
animation.toValue = 1
// ๊ฐ ์ฌ๋ผ์ด์ค์ ์ ๋๋ฉ์ด์
์๊ฐ์ ๋ฐ์์ด. ์ ์ฒด durationd์์ percentage์ ํด๋นํ๋ ์๊ฐ
animation.duration = getDuration(slice)
// CAMediaTimingFunction.linear -> ์ผ์ ํ ์๋
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.delegate = self
- CABasicAnimation์ keypath๋ฅผ strokeEnd๋ก ํ์ฌ ์ ๋๋ฉ์ด์ ์ ์์ฑํด์ค๋ค.
- CABasicAnimation์ ์์ฃผ ์ฐ๋ animate ํจ์๋ณด๋ค ๋ค์ํ ์ต์
์ ์ค ์ ์๊ณ , ์ค์ ํ strokeEnd ์ด์ธ์๋ ๋ค์ํ keyPath๊ฐ ์กด์ฌํ๋ค
- Opacity : ํฌ๋ช ๋์ ๋ํ ์ ๋๋ฉ์ด์
- backgroundColor: ๋ฐฐ๊ฒฝ์์ ๋ํ ์ ๋๋ฉ์ด์
- value ๊ฐ์ CGColor ํ์ ์ผ๋ก ์ค๋ค.
animation.fromValue = NSColor.red.cgColor
animation.toValue = NSColor.blue.cgColor
-
- position: value๋ฅผ ์์น ๋ฐฐ์ด๋ก ๋ฐ๋ ์์น ๋ณํ์ ๋ํ ์ ๋๋ฉ์ด์ (์ฐธ๊ณ : ์ ํ ๊ณต์๋ฌธ์)
- ๋ํ, ์ ๋๋ฉ์ด์ ์ ์ข ๋ฃ ์์ ์ ํธ์ถํ ํจ์๋ฅผ ์ ์ํ๊ธฐ ์ํด delegate๋ฅผ ์ฑํํ๋ค.
let canvasWidth = superView.frame.width
let path = UIBezierPath(arcCenter: superView.center,
radius: canvasWidth * 3 / 8,
// 0 ๋ผ๋์๋ถํฐ ํผ์ผํธ์ ํด๋นํ๋ ๋งํผ์ ๋ผ๋์๊น์ง path ์์ฑ
startAngle: percentToRadian(currentPercent),
endAngle: percentToRadian(currentPercent + slice.percent - 0.000001),
clockwise: true)
// ๋ค๊ฐํ์ ๊ทธ๋ฆฌ๊ธฐ ์ํ ๋ ์ด์ด ์์ฑ
let sliceLayer = CAShapeLayer()
// path ๋ฑ๋ก
sliceLayer.path = path.cgPath
// ์ฌ๊ธฐ์ ์ฑ์์ง๋ ์ปฌ๋ฌ๋ path์ ์์๊ณผ ๋์ ์ ์ด์ด ๋ง๋ ๋ฒ์
sliceLayer.fillColor = nil
sliceLayer.strokeColor = UIColor(named: slice.category.imageName + "-color")?.cgColor
// ์ ์์ ์ฑ์ฐ๋๊ฒ ์๋๋ผ ๋ผ์ธ์ ๋๊ป๊ฒ ๊ทธ๋ฆผ
sliceLayer.lineWidth = canvasWidth * 2 / 8
// layer๋ฅผ ๊ทธ๋ฆฌ๋ ๋ฒ์. 1์ด ๋งฅ์๋ฉ์ด๊ณ ๊ทธ๊ฒ๋ณด๋ค ์์ ๊ฒฝ์ฐ ๋น์จ๋งํผ๋ง ๊ทธ๋ ค์ง
sliceLayer.strokeEnd = 1
// ์ ๋๋ฉ์ด์
๋ฑ๋ก
sliceLayer.add(animation, forKey: animation.keyPath)
// ๋ง๋ ๋ ์ด์ด๋ฅผ ์ถ๊ฐํด์ค
superView.layer.addSublayer(sliceLayer)
}
- UIBezierPath๋ฅผ ํตํด startAngle, endAngle์ ๋ํ
ํธ
๋ฅผ ๊ทธ๋ ค์ค๋ค. - ๋ค๊ฐํ layer๋ก CAShapeLayer๋ฅผ ์์ฑํด์ฃผ๋๋ฐ.. ๊ทธ ์ ์ layer์ ๋ํด ์กฐ๊ธ ์์๋ณด๋ฉด
- CALayer๋ ์ค์ ๋ก UIView์ ์ํ๋ฉฐ UIView๋ฅผ ์ง์ํด์ฃผ๋ ์ญํ ์ ํ๋ค. ๊ฐ ๋ทฐ๋ง๋ค ๋ฃจํธ layer๋ ํ๋์ฉ ์กด์ฌํ๊ณ ์ด ๋ฃจํธ layer๋ ๊ฐ๊ฐ Sublayer๋ค์ ๊ฐ๋๋ค. ์ฐธ๊ณ
- view ๋ด๋ถ(์๋?)์์ core animation, ๊ทธ๋ํฝ๋ฑ์ ์ฒ๋ฆฌํ๋ ์ญํ ์ ํ๋ค.
์ค์ ๋ก ๋ทฐ ๋๋ฒ๊ฑฐ๋ฅผ ์ฐ์ด๋ณด๋ ๋ ์ด๋ธ ๋ทฐ์ฒ๋ผ ์ถ๊ฐ๋์ง ์๊ณ ๋ค์๊ณผ ๊ฐ์ด ํด๋น ๋ทฐ์ ๋ ์ด์ด์ ๋ฑ ๋ถ์ด ๋ํ๋๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค!
- ๋ค์, ์์ฑํ ๋ ์ด์ด์ path์ ์์ฑํ
ํธ
๋ฅผ ๋ฑ๋กํ๊ณ , color, width ๋ฑ์ ์ค์ ํ๋ค. - ๋ง์ง๋ง์ผ๋ก ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํ๊ณ sublayer๋ก ๋ฑ๋ก์ ํด์ฃผ๋ฉด ๋๋ค.
๋ค์ ์ฌ๋ผ์ด์ค๋ ์ด๋ป๊ฒ ๊ทธ๋ฆฌ์ง?
- ์์ ์ธ๊ธํ๋ฏ์ด, ์ ๋๋ฉ์ด์ ์ด ์ข ๋ฃ๋๋ฉด ์ฑํ๋ CAAnimationDelegate์ ์ํด animationDidStop ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
- ์ด ๋ฉ์๋์์ ๋ค์ slice์ ๋ํ ์ถ๊ฐ ์์ ์ ์ฐ์์ ์ผ๋ก ์งํํ๊ฒ ๋๋ค.
extension ReportPieChartView: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag {
currentPercent += slices[sliceIndex].percent
sliceIndex += 1
if sliceIndex < slices.count {
let nextSlice = slices[sliceIndex]
addLabel(nextSlice)
addSlice(nextSlice)
}
}
}
}
percentage ๋ ์ด๋ธ ํ์
- addSlice์ ๋ฌ๋ฆฌ, addLabel์ ๋น์ฐํ์ง๋ง ๋ ์ด์ด๊ฐ ์๋ ๋ทฐ๋ฅผ ์๋ก ์ถ๊ฐํด์ค๋ค.
- ์ถ๊ฐํ ๋ ์ด๋ธ์ ์์น๋ฅผ ์ฐพ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค
- startAngle๊ณผ endAngle์ ์ค๊ฐ ์ง์ ๊น์ง UIBezierPath์ ํตํด
ํธ
๋ชจ์์ path๋ฅผ ์์ฑํ๋ค. - path๋ฅผ ๋ซ๊ณ currentPoint๋ฅผ ์ฐพ์ผ๋ฉด ๊ทธ ๊ณณ์ด ๋ ์ด๋ธ์ด center๋ฅผ ๊ธฐ์ค์ผ๋ก ์์นํ๊ฒ ๋ ๋ฐฉํฅ์ด๋ค.
- UILabel์ ์์ฑํ๊ณ text๋ฅผ ์ค์ ํ๊ณ constraint๋ฅผ ์ก์์ค๋ค.
- ๋ ์ด๋ธ์ด ์์นํ๊ฒ ๋ point๋ฅผ ์๊ณ ์์ผ๋ฏ๋ก, ๊ฐ๊ฐ์ x, y ์ขํ๋ก๋ถํฐ center๋ฅผ ๋นผ์ค ๊ฐ์ centerAnchor๋ก ์ก์์ฃผ๋ฉด ๋๋ค.
- startAngle๊ณผ endAngle์ ์ค๊ฐ ์ง์ ๊น์ง UIBezierPath์ ํตํด
๊ฒฐ๊ณผ
- ๋ ์ด๋ธ์ ํญ๋ชฉ๋ช ๋ ์ถ๊ฐํด์ผ ํ๋ค.
- ๋ ์ด์ด์ Core Animation์ ๋ํ ํ์ต์ด ๋ ํ์ํ๋ค...
ํ๊ณ ์ถ์๊ฒ : ์๋ฉ ํฌ๋ ๋ง ์ฝ์ด ์ ๋๋ฉ์ด์ ์ผ๋ก ๊ตฌํ ํด๋ณด๊ธฐ ใ ใ ใ
๊ธ ์ข ์์ฃผ ์จ์ผ๊ฒ ๋ค ์ ๋ น๋ธ๋ก๊ทธ๊ฐ ๋์ด ๋ฒ๋ ธ๋ค ํํ
๊ทธ๋์ ๋ ์ ํ ํ๋ฆฟ ๋งํฌ๋ค์ด ์์ฑ์ด ๋๋ฌด ๋ณ๋ก๋ค.. ๋ค๋ฅธ ํ ํ๋ฆฟ ์ฐพ์๋ด์ผ๊ฒ ๋๊ฑธ
'๐ฉ๐ปโ๐ป > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Swift Card Flip ์ ๋๋ฉ์ด์ ๊ตฌํํ๊ธฐ (3) | 2020.09.12 |
---|---|
UIKit๋? (0) | 2020.07.12 |
Cocoa Touch ํ๋ ์์ํฌ๋? (0) | 2020.07.08 |
AVFoundation, Timer๋? (0) | 2020.07.08 |
UIButton, UISlider, UILabel์ด๋? (0) | 2020.07.07 |