Skip to content

Latest commit

ย 

History

History
182 lines (137 loc) ยท 9.79 KB

TIL_2022:10:19_uibezierpath.md

File metadata and controls

182 lines (137 loc) ยท 9.79 KB

Today I Leanred


2022.10.19 (์ˆ˜)

UIBezierPath

A path that consists of straight and curved line segments that you can render in your custom views.

view์—์„œ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ง์„ ๊ณผ ๊ณก์„ ์œผ๋กœ ๊ตฌ์„ฑ๋œ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” view์˜ ํ˜•ํƒœ๋ฅผ ์ง์ ‘ ๊ทธ๋ ค์ฃผ๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ณก์„ , ์•„์น˜ํ˜•, ์ง์„ ํ˜• ๋“ฑ์œผ๋กœ ๋ชจ์–‘์„ ์ •์˜ํ•ด์ค€ ํ›„, ํ˜„์žฌ ๊ทธ๋ ค์ง€๋Š” context์— ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€์ ์ธ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UIBezierPath ๊ฐ์ฒด๋Š” ๋ Œ๋”๋ง๋  ๋•Œ ๊ฒฝ๋กœ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ์†์„ฑ๋“ค๊ณผ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ์˜ ๊ฒฝ๋กœ๋“ค์„ ํ•ฉ์นฉ๋‹ˆ๋‹ค. ์ฆ‰, ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ๋ฐ ์†์„ฑ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ์„œ๋กœ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด๋ฅผ ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•œ ํ›„์—๋Š” ํ˜„์žฌ context์—์„œ ๊ฐ์ฒด๋ฅผ ๊ทธ๋ฆฌ๋„๋ก ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ, ๊ตฌ์„ฑ ๋ฐ ๋ Œ๋”๋ง ํ”„๋กœ์„ธ์Šค๋Š” ๋ชจ๋‘ ๋ณ„๊ฐœ์˜ ๋‹จ๊ณ„๋กœ Bezier ๊ฒฝ๋กœ ๊ฐ์ฒด๋ฅผ ์ฝ”๋“œ์—์„œ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๋„ํ˜•์„ ์—ฌ๋Ÿฌ๋ฒˆ ๋ Œ๋”๋งํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ์—ฐ์† ๊ทธ๋ฆฌ๊ธฐ ํ˜ธ์ถœ๊ฐ„์— ๋ Œ๋”๋ง์˜ต์…˜์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฝ๋กœ์˜ ํ˜„์žฌ ์ง€์ ์„ ์กฐ์ž‘ํ•˜์—ฌ ๊ฒฝ๋กœ์˜ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ๊ฒฝ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉด ํ˜„์žฌ ์ ์ด ์ ์˜๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ทธ๋ฆฌ์ง€ ์•Š๊ณ  ํ˜„์žฌ ์ ์„ ์ด๋™ํ•˜๋ ค๋ฉด move(to:) ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ํ•จ์ˆ˜๋Š” ๊ฒฝ๋กœ์— ์„  ๋˜๋Š” ์ปค๋ธŒ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์„ธํฌ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํ•ญ์ƒ ํ˜„์žฌ ์ง€์ ์—์„œ ์‹œ์ž‘ํ•˜์—ฌ ์ง€์ •ํ•œ ์ƒˆ ์ง€์ ์—์„œ ๋๋‚˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์„ธ๊ทธ๋จผํŠธ์˜ ๋์ ์ด ์ž๋™์œผ๋กœ ํ˜„์žฌ ํฌ์ธํŠธ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ Bezier ๊ฒฝ๋กœ ๊ฐ์ฒด๋Š” ์—ด๋ ค์žˆ๊ฑฐ๋‚˜ ๋‹ซํžŒ ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ์–ผ๋งˆ๋“ ์ง€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ํ•˜์œ„ ๊ฒฝ๋กœ๋Š” ์—ฐ๊ฒฐ๋œ ์ผ๋ จ์˜ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. close() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ˜„์žฌ ํฌ์ธํŠธ์—์„œ ํ•˜์œ„ ๊ฒฝ๋กœ์˜ ์ฒซ ๋ฒˆ์งธ ํฌ์ธํŠธ๊นŒ์ง€ ์ง์„  ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ๋‹ซํž™๋‹ˆ๋‹ค. move(to:) ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ˜„์žฌ ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ (๋‹ซ์ง€ ์•Š๊ณ ) ๋‹ค์Œ ํ•˜์œ„ ๊ฒฝ๋กœ์˜ ์‹œ์ž‘์ ์ด ์„ค์ •๋ฉ๋‹ˆ๋‹ค. Bezier ๊ฒฝ๋กœ ๊ฐ์ฒด์˜ ํ•˜์œ„ ๊ฒฝ๋กœ๋Š” ๋™์ผํ•œ ๋„๋ฉด ํŠน์„ฑ์„ ๊ณต์œ ํ•˜๋ฏ€๋กœ ๊ทธ๋ฃน์œผ๋กœ ์กฐ์ž‘ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ํŠน์„ฑ์„ ๊ฐ€์ง„ ํ•˜์œ„ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ๊ฐ ํ•˜์œ„ ๊ฒฝ๋กœ๋ฅผ ์ž์ฒด UIBezierPath๊ฐ์ฒด์— ๋„ฃ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

Bezier ๊ฒฝ๋กœ์˜ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ๋ฐ ์†์„ฑ์„ ๊ตฌ์„ฑํ•œ ํ›„์—๋Š” stoke() ์™€ fill() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ๊ทธ๋ž˜ํ”ฝ context์—์„œ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. stoke() ํ•จ์ˆ˜๋Š” ํ˜„์žฌ stroke ์ƒ‰์ƒ๊ณผ Bezier๊ฒฝ๋กœ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฝ๋กœ์˜ ์œค๊ณฝ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ fill() ํ•จ์ˆ˜๋Š” ํ˜„์žฌ ์ƒ‰์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฝ๋กœ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ์˜์—ญ์„ ์ฑ„์›๋‹ˆ๋‹ค.

Bezier ๊ฒฝ๋กœ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„ํ˜•์„ ๊ทธ๋ฆด๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ƒˆ ์ž๋ฅด๊ธฐ ์˜์—ญ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. addClip() ํ•จ์ˆ˜๋Š” ๊ฒฝ๋กœ ๊ฐ์ฒด๋กœ ํ‘œํ˜„๋œ ๋„ํ˜•์„ ๊ทธ๋ž˜ํ”ฝ ์ปจํ…์ŠคํŠธ์˜ ํ˜„์žฌ clipping ์˜์—ญ๊ณผ ๊ต์ฐจํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๊ต์ฐจ๋กœ ์˜์—ญ ๋‚ด์— ์žˆ๋Š” ์ฝ˜ํ…์ธ ๋งŒ ๊ทธ๋ž˜ํ”ฝ์— ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•ด๋ณด๊ธฐ

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let bview: BezierView = .init(frame: view.frame)
        bview.backgroundColor = .clear
        view.addSubview(bview)
    }
}

class BezierView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {}
}

BezierPath๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ๊ฐํ˜•

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(rect: CGRect(x: bounds.midX-50, y:bounds.midY-50, width: 100, height: 100))
    UIColor.systemRed.setFill()
    UIColor.systemYellow.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
}

ํƒ€์›ํ˜•

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(ovalIn: CGRect(x: bounds.midX-100, y:bounds.midY-100, width: 200, height: 300))
    UIColor.blue.setFill()
    UIColor.red.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
}

๋‘ฅ๊ทผ ์‚ฌ๊ฐํ˜•

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(roundedRect: CGRect(x: bounds.midX-100, y:bounds.midY-100, width: 100, height: 200), cornerRadius: 20)
    UIColor.blue.setFill()
    UIColor.red.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
}

์•„์น˜ํ˜•

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(roundedRect:  CGRect(x: bounds.midX-50, y:bounds.midY-50, width: 100, height: 100), byRoundingCorners: [.topLeft,.topRight], cornerRadii: .init(width: 50, height: 50))
    UIColor.blue.setFill()
    UIColor.red.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
}

๊ณก์„ 

override func draw(_ rect: CGRect) {
    let path = UIBezierPath(arcCenter: center, radius: 50, startAngle: 0, endAngle: .pi, clockwise: false)
    UIColor.blue.setFill()
    UIColor.red.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
}

Initializer ์ •๋ฆฌ

๊ฒฝ๋กœ ๋งŒ๋“ค๊ธฐ

์œ„์™€ ๊ฐ™์ด Initializer์„ ํ†ตํ•ด ๋ชจ์–‘์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ ์ง์ ‘ ์›ํ•˜๋Š” ๋ชจ์–‘์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

override func draw(_ rect: CGRect) {
    let path = UIBezierPath()
    path.move(to: center)
    path.addCurve(to: .init(x: center.x + 100, y: center.y - 100),
                  controlPoint1: .init(x: center.x + 1, y: center.y - 97),
                  controlPoint2: .init(x: center.x + 82, y: center.y - 7))
    path.addLine(to: .init(x: center.x + 100, y: center.y))
    path.addLine(to: .init(x: center.x, y: center.y))
    UIColor.blue.setFill()
    UIColor.red.setStroke()
    path.lineWidth = 10
    path.stroke()
    path.fill()
    path.close() // ๋ชจ์–‘์ด ์™„์„ฑ๋˜๋ฉด ๋Š๊ธฐ 
}

๊ณก์„ ์„ ๊ทธ๋ฆด ๋•Œ ์ฐธ๊ณ ํ•  ๋งŒํ•œ ์‚ฌ์ดํŠธ https://cubic-bezier.com/#.17,.67,.83,.67

์—ฌ๋Ÿฌ ํ”„๋กœํผํ‹ฐ๋“ค

  • path.stroke() ๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ€์žฅ์ž๋ฆฌ(ํ…Œ๋‘๋ฆฌ)๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • path.lineWidth = 10์„ ์ด์šฉํ•ด์„œ ์„ ์˜ ๋‘๊ป˜๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • setStroke()๋ฅผ ์ด์šฉํ•ด์„œ ์›ํ•˜๋Š” ์„  ์ƒ‰๊น”์„ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • path.lineCapStyle = .round๋ฅผ ์ด์šฉํ•˜๋ฉด ์„ ์˜ ๋์ ์ด ๋‘ฅ๊ธ€์–ด ์ง‘๋‹ˆ๋‹ค. (์‹œ์ž‘๊ณผ ๋์—๋งŒ ์ ์šฉ)
  • path.lineJoinStyle = .round๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ชจ๋“  ๋ชจ์„œ๋ฆฌ๊ฐ€ ๋‘ฅ๊ธ€์–ด ์ง‘๋‹ˆ๋‹ค.
  • path.fill()์„ ํ†ตํ•ด ์ƒ‰์„ ์ฑ„์›Œ์ค๋‹ˆ๋‹ค.