ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UIBeizerPath
    iOS 2020. 1. 9. 01:17
    728x90

    iOS 개발을 하다보면 심심치않게 UIBeizerPath를 사용해야 할 상황이 생깁니다. 
    간단하게는 원부터 시작해서 정형적이지 않은 뷰를 그리고자 할 때는 거의 이녀석을 사용해야 합니다. 
    이름부터 살벌한 이녀석.. 같이 한번 정복해보도록 하겠습니다.


    path는 알겠는데 Beizer 우선 발음부터 쉽지 않습니다. 주로 베지에 라고 번역하시는 것 같더라구요.
    베지에 곡선이라고 많이 알려져 있는데, 이 베지에 곡선을 그릴 경로를 지정해주는 역할을 UIBeizerPath가 해줍니다.

    워낙 많이 사용되고 있고.. 사실 편법으로 사용법만 익혀도 간단한 건 해결할 수 있겠지만 원리를 이해하지 못하면 그 이상은 할 수 없겠죠??
    우선 베지에 곡선이 뭔지부터 확인해서 제대로 알아보자구요!

    Beizer Curve 베지에 곡선 ?

    베지에 곡선에 대한 개념 잡기에 좋은 사이트가 있어 소개해드립니다. 이 글을 먼저 읽고 오시면 도움이 되실거에요 :)

    베지에 곡선이란 한 선분 위를 지나는 점의 궤도 입니다. 
    문제는 이 선분을 어떤 것으로 할지에 달려있습니다.

    먼저 2개의 점 P0 과 P1 을 잇는 직선을 생각해봅시다.
    이때 생각할 수 있는 선은 P0-P1 밖에 없습니다.
    P0에서 시작해 이 선 위를 지나는 점 M의 궤도가 1차 베지에 곡선입니다.
    그냥 직선이되겠죠!

    출처: https://blog.coderifleman.com/2016/12/30/bezier-curves/

    이번엔 점 3개를 생각해봅시다. 
    P0-P1 , P1-P2 를 연결하는 2개의 직선이 있습니다. 
    그렇다면 P0 -> P1, P1 -> P2로 이동하는 점을 M0, M1 이라고 할 수 있겠습니다.
    M0-M1 을 연결하는 직선을 하나 긋고 그 위를 이동하는 점을 B라고 한다면 이 점 B의 궤도가 베지에 곡선이 됩니다.
    이게 2차 베지에 곡선입니다.

    출처: https://blog.coderifleman.com/2016/12/30/bezier-curves/

    점 4개도 마찬가지입니다.
    4개의 점이라면 3개의 선이 생기겠죠? 
    3개의 선을 움직이는 3개의 점이 2개의 선을 만듭니다.
    이 2개의 선에서 다시 2개의 점이 생기고 또 1개의 선을 만듭니다.
    이 선을 움직이는 점이 베지에 곡선이 됩니다.
    이게 3차 베지에 곡선입니다. 

    출처: https://blog.coderifleman.com/2016/12/30/bezier-curves/

    이와같은 방법으로 4차, 5차, N차 베지에 곡선을 만들어나갈 수 있습니다.
    n개의 선위를 움직이는 n개의 점이 n-1개의 선을 만들고 다시 n-2, n-3개의 선을 만들어 나갑니다. 그렇게 해서 마지막에 최종적으로 남는 하나의 선이 n차 베지에 곡선이 됩니다.

    이제  어떤 원리인지 감이 잡히셨죠? 
    하지만 조절점을 많이 늘리더라도 3차 이상은 실용적으로 별로 필요가 없다고 하니 저희는 3차까지만 잘 알고 있으면 될 것 같습니다.
    그때문인지 역시 apple도 3차 베지에 곡선까지만 지원을 하고 있습니다.

    원리는 알겠는데 그렇다고 저희가 수학적으로 매번 전부 계산할 수는 없겠죠..?
    생각만해도 끔찍하네요... :(
    이를 위해 다음다음과 사이트들에서 원하는 도형을 그려볼 수가 있어요. 너무 감사합니다!!

    UIBeizerPath

    이제 베지에 곡선이 뭔지도 알겠고 원하는 좌표를 어떻게 계산할지도 대충 알겠으니 이제 진짜 iOS에서 어떻게 사용할 수 있는지 알아봐야겠습니다. 먼저 베지에 곡선을 그릴 때 필요한 핵심 알짜베기들을 하나씩 살펴볼게요. 애플 문서에 있는 다음 아이들을 살펴볼거에요.

    move

    도화지에 그림을 그리려고 한다고 생각해보세요. 그림을 그리려면 먼저 한 점에 점을 찍는 것부터 시작이죠?
    move to! 해당 점으로 가서 시작점을 찍어줍시다. 시작점을 의미합니다.

    addLine, close

    가장 기본은 역시 선긋기죠? addLine(to:)를 사용해서 다음 점을 추가해 선을 긋습니다.

    점, 선이면 충분히 도형을 그릴 수 있겠죠? 간단하게 도형을 하나 그려보고 갑시다.
    먼저 우리가 그려나갈 DemoView를 하나 만들겠습니다. 직사각형을 그려볼거에요.

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let width: CGFloat = 240.0
            let height: CGFloat = 160.0
            
            let demoView = DemoView(
            	frame: CGRect(x: self.view.frame.size.width/2 - width/2,
                              y: self.view.frame.size.height/2 - height/2,
                              width: width,
                              height: height))
    	}
    }

    직사각형에는 총 4개의 선분이 필요합니다. 하지만 저희는 3개의 선만 추가해줄거에요. 그렇게 해주면 시작점을 포함해 이미 4개의 점이 생겼기 때문에 close 를 통해서 마지막 선을 하나 더 그어줄 수 있습니다.

    func createRectangle() {
      // 먼저 path를 생성합니다.
      path = UIBezierPath()
    
      // 그려나갈 시작점을 찍습니다.
      path.move(to: CGPoint(x: 0.0, y: 0.0))
    
      // 시작점과 다음 점을 이어줍니다.
      path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))
    
      // 또 다음 점
      path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
    
      // 또 다음 점을 추가합니다.
      // 이제 3개의 선과 4개의 점이 생겼습니다.
      path.addLine(to: CGPoint(x: self.frame.size.width, y: 0.0))
    
      // 마지막 점과 시작점을 연결해줍니다.
      path.close()
    }
    
    override func draw(_ rect: CGRect) {
      createRectangle()
      
      UIColor.orange.setFill()
      path.fill()
    
      UIColor.purple.setStroke()
      path.stroke()
    }

    draw 함수에서 path를 잡아주고 색과 선을 채워줘볼까요?
    이렇게 우리가 베지에 곡선을 이용해 그런 첫 도형이 완성되었습니다.

    사각형을 그렸으니 삼각형은 더 쉽겠죠?
    연습삼아 삼각형도 한번 그려볼게요. 삼각형은 경로를 다음과 같이 수정해주면 되겠죠?

    func createTriangle() {
      path = UIBezierPath()
      path.move(to: CGPoint(x: self.frame.width/2, y: 0))
      path.addLine(to: .init(x: 0, y: self.frame.size.height))
      path.addLine(to: .init(x: self.frame.size.width, y: self.frame.size.height))
      path.close()
    }


    addArc

    다음은 원입니다. 원도 전혀 어렵지 않아요!
    먼저 addArc의 파라미터의 의미부터 하나씩 보죠.

    arcCenter: CGPoint  //  원의 중심
    radius: CGFloat  //  반지름
    startAngle: CGFloat // 시작 각도
    endAngle: CGFloat // 종료 각도
    clockwise: Bool // 시계방향인지 아닌지

    어려운 개념은 없죠?? 

    원을 그릴 때 각도는 3시를 기준으로 해서 시계방향으로 갑니다.

    출처: https://www.appcoda.com/bezier-paths-introduction/

    먼저 시작점을 90도, 끝점을 270도로 해서 왼쪽로 볼록한 반원을 만들어볼까요?

    path = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2),
                        radius: self.frame.size.height/2,
                        startAngle: 0,
                        endAngle: CGFloat.pi,
                        clockwise: true)

    짠 간단하죠?

    clockwise를 false로 하면 반시계 방향으로 그려지니까 오른쪽으로 볼록해지겠네요!

    이렇게 말이죠 ㅎ

    여기까지가 1차 베지에 곡선이라고 생각하시면 됩니다!!

    addQuadCurve

    오래 기다리셨습니다ㅠㅠ N차 베지에 곡선이 어떻고.. 어려운 말만 잔뜩하더니 정작 안써먹었죠? 이제 드디어 때가 됐습니다!
    앞서 실제로는 2차, 3차 베지에 곡선까지만 알면 된다고 말씀드렸습니다. 
    앞서 1차 베지에 곡선에서는 점 하나만 추가해주면 됐었어요. 그러면 시작점을 포함해 점이 2개가 되니깐요.

    그러면 2차는 점을 2개 포함해주나요???

    네 정답입니다. 
    앞의 1차 베지에 곡선들과 마찬가지로 to: 는 선이 그어질 다음 점인 걸 잘 기억하시면 됩니다.
    controlPoint는 연장선을 만들어줄 보조점입니다.

    func createQuadCurve() {
      path = UIBezierPath()
      path.move(to: CGPoint(x: 0, y: 0))
      path.addQuadCurve(to: CGPoint(x: self.frame.width, y: 0), controlPoint: CGPoint(x: self.frame.width/2, y: self.frame.height))
    }

    이렇게 간단하게 하나 그어보면

    짠 이런식으로 원하는 곡선을 그을 수 있습니다. 이렇게 2차함수의 모양을 띄게 되겠죠??ㅎㅎ

    addCurve

    네 마지막입니다. 이제 진짜 어렵고 중요한 녀석이 남아있네요.
    3차 베지에 곡선입니다. 여기서도 마찬가지로 to: 가 다음 점이 되겠구요 중간에 보조점 controlPoint1, controlPoint2가 있습니다.

    모든 베지에 곡선은 
    move -> to 로 선이 그어집니다.
    나머지는 중간의 보조점의 차이에요!

    사실 여기서부터는... 제 머리로는 그릴 수 없습니다. 선 3개를 움직이는 점을 이어서 2개의 선을 만들고 또 그 2개의 선에서 움직이는 점으로 하나의 선을 만들어서 그 선에서 움직이는 점의 궤도를 그려라?

    오마이갓 :<

    따라서 정교하고 어려운 곡선들은.. 다시한번 다음  다음과 사이트를 통해서 미리 모양 확인해서 그려볼 수가 있겠습니다. ㅎ
    저희는 수학자가 아니니깐요. 감사한 도구들을 적극 활용하면 되지 않겠습니까!

     

    이를 이용한 활용은 무궁무진한 방식으로 사용해볼 수 있습니다. 이런식으로 좌표를 찍고 그어서 이런 저런 모양도 그려볼 수 있겠구요.


    여기까지 UIBezierPath에 대해 알아보았습니다.
    그동안은 제대로 알고 사용하지 못하는 느낌이었는데 이번 포스팅을 하면서 많이 배우고 알게되었습니다.
    우리는 계산해서 수학문제를 푸는게 아니니깐요! 원리를 이해하고 도구를 활용할 수 있으면 되겠죠?! 그렇게 믿습니다...

    아 참! 포스팅에서는 override func draw를 사용해서 그려보았는데요 

    // Only override draw() if you perform custom dr awing.
    // An empty implementation adversely affects performance during animation.

    draw 함수 위에 친절하게 주석에도 달아주듯, draw로 직접 그리는 것은 퍼포먼스에 별로 좋지 못하다고 해요. 
    대신 CAShapeLayer()를 통해서 그림을 추가해주는 방식이 더 많이 사용되고 있는 것 같습니다.
    같이 다뤄보려 했으나.. 어쩌다 보니 이렇게 마무리를 해야겠네요.
    다음엔 CAShapeLayer()를 활용하는 방법으로 돌아오겠습니다 :) 

    조금이라도 도움이 되셨으면 좋겠습니다. 긴 글 읽어주셔서 감사합니다!

    참고

    https://www.appcoda.com/bezier-paths-introduction/

     

    A Beginner's Guide to Bezier Paths and Shape Layers | AppCoda

    The process of developing apps includes amongst other things the creation of the user interface (UI) and all those simple or complicated views that appear on screen. There are different ways and different approaches to draw a simple “screen” of an app: To

    www.appcoda.com

    https://cubic-bezier.com/#.17,.67,.84,.38

     

    cubic-bezier.com

    Library Import Export Click on a curve to compare it with the current one. Tip: Right click on any library curve and select “Copy Link Address” to get a permalink to it which you can share with others

    cubic-bezier.com

    https://www.desmos.com/calculator/cahqdxeshd

     

    Bezier Curves

     

    www.desmos.com

     

    댓글

Designed by Tistory.