티스토리 뷰

iOS

UIBezierPath 클래스로 도형 그리기

할루루 2018. 9. 11. 14:16
Bezier Path

시작

애플리케이션을 개발하는 과정은 UI를 만드는 것과 스크린에 보여지는 그것의 간단하거나 복잡한 뷰를 만드는 것들을 포함합니다. 애플리케이션의 간단한 '스크린'을 그리는 데에는 다른 방법들과 다른 접근법들이 있습니다: 디자이너에 의해 이미 만들어진 그래픽을 사용, 코드로 UI를 구현, 인터페이스 빌더 사용, 이것들을 조합하여 사용하기, 등등. 그러나, 당신은 프로그래밍적으로 사용자 정의 형태를 만들 필요가 있을 때가 항상 있을 것이고, 어떻게 하는지 모른다면, 그 때에 문제가 되기 시작합니다.

하지만 이러한 문제들은 문서에 따르면 벡터 기반 경로를 만드는 데 필요한 UIBezierPath 클래스를 사용하여 해결할 수 있습니다. 간단하게 말해서, 이 클래스를 사용하여 어떠한 형태를 묘사하는 사용자 정의 경로를 정의할 수 있고, 당신이 원하는 어떠한 사용자 정의 결과를 달성하기 위해 그 경로들을 사용할 수 있습니다. 직사각형, 정사각형, 타원, 원 같은 간단한 형태에서부터, 매우 복잡한 형태도 점 집합 사이에 직선과 곡선을 더하여 경로에 선을 더함으로써 만들 수 있습니다.

위의 클래스에 의해 만들어지는 베지어 경로는 그 자신 홀로 설 수 없습니다. 그것은 그것이 렌더링되는 곳에서 Core Graphics 컨텍스트를 필요로 합니다. 컨텍스트를 얻는 세 가지 방법이 있습니다.

  1. CGContext 컨텍스트를 사용합니다.
  2. 사용자 정의 형태를 그리고 싶은 클래스를 UIView 를 상속받게 하고, 기본으로 제공되는 draw(_:) 메소드를 사용합니다. 그러면 필요한 컨텍스트는 자동으로 주어집니다.
  3. CAShapeLayer 오브젝트라고 불리는 특별한 레이어를 만듭니다.

세 가지 선택지 모두 이 튜토리얼의 마지막 두 가지를 만족시키므로, Core Graphics 프로그래밍 세부 정보가 포함된 스코프를 놓치지 않습니다.

방금 언급한 CAShapeLayer 클래스 또한 이 포스트의 주제이며, 우리는 그것에 대해서 몇 가지를 볼 것입니다. 이 클래스는 CALayer 로부터 상속받았고, 그것의 객체는 모든 뷰가 가지고 있는 기본 레이어로 사용됩니다. 대부분의 경우 형태 레이어 객체는 기본 레이어 객체의 최상단에 추가적은 레이어로서 추가되지만, 마스크로서도 사용될 수 있습니다. 나중에 두 가지의 경우에 대해서 논의할 것입니다. 추가적으로, 형태 레이어 객체의 몇 가지 가장 중요한 프로퍼티들에 대해, 그리고 그것들의 의미에 대해 논의할 것입니다.

이 튜토리얼에서 나의 목표는 당신에게 베지어 경로를 만들고 그것들을 따라 형태 레이어 객체를 사용하는 방법에 대해 실용적인 가이드라인을 제공하는 것입니다. 우리는 일련의 과정을 작지만 직설적인 예제를 만날 것입니다. 그러므로 당신이 이 튜토리얼을 완료했을 때 두 가지 개념의 기초에 대해 익숙해져 있을 것이고, 스스로 좀더 복잡한 경우에 대해 생각할 수 있게 될 것입니다. 이미 베지어 경로와 형태 레이어에 대해 몇 가지 지식을 가지고 있다면, 이 튜토리얼은 그 지식에 새로운 무언가를 추가해 줄 것입니다. 이러한 것을 처음 접해본다면, iOS 개발에서 정말로 흥미로운 부분을 배우게 될 것입니다.

 

프로젝트 준비하기

Single View Application 프로젝트를 생성하고, UIView 를 상속받는 클래스 파일을 하나 만들어 줍니다.

ViewController.swift , DemoView.Swift 파일에 원문에 등장하는 코드 작성하기

준비는 끝났고, 첫 번째 베지어 경로를 만들기 위해 가봅시다.

 

경로와 형태 만들기

수영을 배우는 가장 좋은 방법은 물로 뛰어드는 것이라고 사람들은 말합니다. 그리고 나는 같은 원리가 베지어 경로와 함께 작업하는 것에도 적용된다고 믿습니다. 그러므로, 많이 말할 것 없이 어떻게 UIBezierPath 객체가 만들어지고, 구성되고, 사용될 수 있는지 명백하게 할 수 있을 특정 예제를 봐봅시다. 사실, 우리는 여러 개의 경로를 만들 것인데, 각각은 다른 종류의 형태의 결과물일 것입니다. 준비하고, 숨을 크게 들이쉬고, DemoView.swift 파일을 여십시오. 모든 구현은 이 클래스 안에서 이루어질 것입니다.

 

직사각형

간단하지만, 꽤 유익한 것으로 시작할 것입니다.

본문에 등장하는 코드를 DemoView.swift 에 작성하기

위의 것은 뷰의 주위를 감싸는 직사각형을 만듭니다. 하지만 우리가 그것이 렌더링될 경로에 대해 컨텍스트를 제공하지 않았기 때문에 여전히 아무런 효과도 없습니다. 나중에 해볼 것입니다.

먼저, 모든 코드 줄이 무슨 일을 하는지 자세하게 살펴봅시다.

  • path = UIBezierPath()

    • 먼저, UIBezierPath 객체를 초기화합니다. 이 클래스가 제공하는 다양한 생성자들이 있고 서로 다른 매개변수의 숫자와 종류를 갖지만, 지금은 간단한 것이 우리가 필요로 하는 것입니다.
  • path.move(to: CGPoint(x: 0.0, y: 0.0))

    • 경로 객체가 준비되면, 우리가 항상 해야 하는 첫 번째 일은 형태가 그려지기 시작할 포인트를 정의하는 것입니다. 포인트는 CGPoint 값으로 표현되며, move(to:) 메소드는 경로에게 그 포인트에 대해 알려줍니다. 우리의 샘플 코드에서, (0.0, 0.0) 좌표는 우리 뷰의 좌측 상단 모서리를 의미합니다.
  • path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))

    • 우리가 원하는 다음 것은 시작점과 우리가 특정해야 하는 또다른 포인트 사이에 선을 긋는 것입니다. 우리는 그 포인트를 제공하고, 같은 시간에 addLine(to:) 메소드를 사용하여 시스템에게 선을 그리라고 일러줍니다. 이전에 한 것처럼, 이 메소드 또한 매개변수로 CGPoint 값에 접근하는데, 이것은 명백하게 첫 번째 포인트와 선을 사용하여 연결하기 원하는 두 번째 포인트입니다. 그 선은 뷰의 좌측 상단 모서리에서 시작하여 우측좌측 하단 모서리에서 끝납니다.
  • path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height)) / path.addLine(to: CGPoint(x: self.frame.size.width, y: 0))

    • 이어지는 두 개의 명령은 이전의 것처럼 같은 로직이고, 선을 사용하여 포인트들을 연결합니다. 네 번째 줄에서, 우리는 뷰의 좌측 하단 모서리에서 우측 하단 모서리까지 연결하는 선을 경로에 추가합니다. 다섯 번째 줄에서, 새로운 선을 우측 하단에서 우측 상단까지 그립니다.
  • path.close()

    • 여기까지, 우리는 직사각형의 네 개의 선 중 세 개를 그렸습니다. 이성적으로 기대해 보았을 때, 우리는 형태를 만들기 위해 선을 하나 더 추가해야 합니다. 하지만, 그럴 필요가 없습니다. close() 메소드를 호출하여, 우리는 경로의 끝을 선언하고, 시스템은 자동으로 우리를 위해 없는 선을 그려 줄 것입니다.

쉽네요. 맞나요? 우리가 한 모든 것은 점들을 연결하고 포인트 사이에 선을 만들어 준 것입니다. 이제, 위의 메소드는 사용되어야 하고, 그것을 할 위치는 DemoView 클래스의 draw(_:) 메소드입니다. 그것은 경로에 컨텍스트를 제공할 것입니다.

draw(_:) 메소드를 재정의하여 createRectangle() 메소드 호출하는 코드 작성하기

지금까지 했던 것을 테스트하기 전에, 배경 및 선 색상(a fill and a stroke colour)을 설정하여 더 흥미롭게 해봅시다. 배경색은 우리가 선택한 색상으로 형태의 전체를 덮는 반면에, 테두리색은 형태의 경계선을 칠합니다. 몇 가지 코드를 더 작성해 봅시다.

배경색과 테두리색을 설정하는 코드를 draw(_:) 메소드 안에 작성하기

이것들은 정말로 간단합니다. 그래서 실행하여 베지어 경로 경로의 결과를 확인할 수 있습니다.

축하합니다. 이것이 당신의 첫 번째 베지어 경로입니다! 하지만, 뷰의 배경 색상을 설정하고 레이어에 경계를 추가하여 동일한 결과를 얻을 수 있으므로, 뷰의 프레임을 정확하게 따라 형태를 그리는 것은 다소 무의미합니다. 하지만 문제될 것은 없습니다. 우리가 여기서 한 것은 가장 좋은 시작을 위한 것입니다! 이제 우리는 기초를 알고, 더 진보된 경우를 보기 위해 갈 수 있습니다.

 

삼각형

당신이 추측하는 것처럼, 경로에 선을 추가하여 만들어지는 어떠한 형태를 만들기 위해 적절한 포인트를 명시하는 것이 중요합니다. 경로에 주어지는 점이 적절한 것이라면, 단일 형태에서 폴리곤까지도 만들 수 있습니다. 이러한 정신에서, 이번에는 삼각형을 만들어 봅시다.

삼각형 만드는 함수 정의하기

여기에서 시작점은 뷰의 상단 중앙 지점입니다. 위의 코드는 createRectangle 메소드 안에 있는 이전의 것과 비슷해 보입니다. 우리는 단지 포인트와 만들 선의 수를 바꾸었을 뿐입니다.

메소드를 draw(_:) 에서 호출하는 것을 잊지 마십시오.

시작점을 바꾸고 선을 추가해 보십시오. 다른 매개변수는 다른 결과를 이끌어 냅니다.

 

타원과 원

타원을 만드는 것은 쉽고, 우리가 정말로 필요로 하는 것은 간단한 UIBezierPath() 이 아닌 경로를 위한 다른 생성자를 사용하는 것입니다. 다음의 간단한 예제를 보십시오. 우리는 다른 생성자를 사용하고 있으며 형태를 위한 프레임으로 우리의 뷰의 바운드를 제공하고 있습니다.

타원형 경로 만드는 코드 작성하기. UIBezierPath(ovalIn:)

프레임을 변화시키는 것으로, 우리는 경로의 결과에 영향을 미치고, 어떠한 크기의 타원형을 그릴 수 있습니다. 명백하게, 당신은 항상 뷰의 바운드를 매개변수 값으로 사용할 필요는 없습니다. 뷰의 일부분의 프레임을 제공할 수 있습니다.

흥미로운 경우는 타원형을 원형으로 바꾸는 방법에 대한 것입니다.

원 만드는 코드 작성하기. UIBezierPath(ovalIn:)

새로운 타원형의 너비와 높이가 같아졌고 (이 경우에 뷰의 높이와 같음), 이것은 형태를 원의 형태로 만들어 줍니다. 또한, 원의 중점이 뷰의 중점과 갖다는 것을 주목할 알아두십시오.

 

둥근 모서리가 있는 직사각형

모든 iOS 개발자가 알고 있는 가장 기본적인 것 중 하나는 다음과 같은 명령을 사용하여 뷰에 둥근 모서리를 적용하는 방법입니다.

view.layer.cornerRadius = 15.0

그것은 모서리를 둥글게 하는 가장 쉬운 방법이지만, 불운하게도 뷰의 최상단에 베지어 경로를 만들었다면 아무런 효과도 일어나지 않을 것입니다. 그러나 그 해결책이 있습니다. 필요한 모든 것은 아래와 같이 둥근 직사각형처럼 경로를 만드는 것입니다.

둥근 모서리를 갖는 경로 만드는 코드 작성하기. UIBezierPath(roundedRect:cornerRadius:)

위의 생성자는 새로운 직사각형 경로를 만들고, 그것은 두 번째 매개변수에 특정한 값에 따라 모서리를 둥글게 합니다.

우리가 지금 본 것은 유용하지만, 단지 직사각형의 모서리 중 몇 개만 둥글게 하고 싶어 할 때는 흥미롭지 않습니다. 종종 하나 또는 그 이상, 하지만 전체는 아닌 모서리가 둥글게 되어 있는 사용자 정의 뷰를 만드는 것을 요구받을 때가 있고, 베지어 경로는 이 일을 쉽게 하기 위해 여기에 있습니다. 오직 뷰의 좌측 상단과 우측 하단 모서리만 둥글게 하는 방법을 보기 위해 예제를 사용합시다.

몇 개의 모서리만 둥글게 하는 경로를 만드는 코드 작성하기. UIBezierPath(roundedRect:byRoundingCorners:cornerRadii) (radii는 radius의 복수명사)

이 새로운 생성자는 세 개의 매개변수를 받습니다.

  1. 만들어질 직사각형의 프레임
  2. 둥글게 될 모서리. 하나의 모서리만 원한다면, 배열을 정의하는 문법을 사용할 필요는 없습니다. 하지만 더 많은 것을 원한다면, 위와 같이 하는 것을 필요로 합니다. 이 매개변수에서 주어진 값은 UIRectCorner 구조체의 프로퍼티들이며, 여기에서 더 많은 정보를 찾을 수 있습니다.
  3. 모서리 반지름 값. 이 매개변수는 CGSize 값을 기대하지만 사실은 너비만 고려됩니다. 높이는 무시됩니다.

나쁘지 않습니다. 우리는 단지 객체를 생성하였고 일부 모서리만을 둥글게 했습니다!

 

호(Arc) 만들기

많은 개발자들에게, 호 형태의 베지어 경로를 만드는 것은 지금까지 만나왔던 이전의 다른 경우들보다 훨씬 복잡해 보일 것입니다. 그리고 맞습니다. 그것은 훨씬 복잡합니다. 당신이 이것이 어떻게 작동하는지 이해하지 않는다면 말이죠! 나를 믿으십시오. 쉽습니다.

먼저 예제를 봅시다. 그러면 단계적으로 모든 것들을 해나갈 수 있습니다.

호를 그리는 코드 작성하기. UIBezierPath(arcCenter:radius:startAngle:endAngle:clockwise)

하나씩 매개변수 목록을 살펴봅시다.

  1. arcCenter

    • 호는 항상 원의 일부분입니다. 그러므로 원의 없어진 부분을 시각화하고 그것을 당신의 마음 속에서 완전히 그린다면, 그것의 중점을 볼 수 있을 것입니다. 상상 속 원의 중점은 또한 호의 중점입니다. 위의 예제에서 우리는 상상 속 원이 뷰의 프레임 안에 포함되는 것을 원하고, 그것의 중점은 또한 뷰의 중점이 되어야 합니다. 매개변수 값은 항상 CGPoint 값이어야 합니다.
  2. radius

    • 원의 반지름입니다. 상상 속 원의 지름이 뷰의 높이와 같기 대문에, 그것의 절반이 반지름입니다.
  3. startAngle

    • 이것은 당신이 그리고 있는 호의 '시작점'을 의미합니다. 우리는 이것과 다음에 나올 endAngle 매개변수에 대해 더 자세하게 알아볼 것이지만, 지금은 단순히 호가 시작하는 상상 속 원의 둘레 위의 포인트라고 생각하십시오. 기대되는 값은 라디안(호도법)으로 표현되는 각도이며, 육십분법으로 표현되는 각도가 아닙니다. 나중에 더 자세하게 설명할 것이고, 특히 위의 코드에서 보는 것에 대해 더 설명할 것입니다.
  4. endAngle

    • 그려지는 호의 '끝점'을 의미합니다. startAngle 에서 말했던 것과 비슷하게, 호가 끝나는 상상 속 원의 둘레 위에 있는 포인트라고 생각하십시오.
  5. clockWise

    • 호가 시계 방향으로 그려지는지, 아니면 그 반대 방향인지 알리는 불리언 값입니다. 이것과 이전 두 개의 매개변수의 적절한 조합이 당신의 그리기 원하는 적절한 원으로 이끌 것입니다.

이제 startAngleendAngle 값에 대해 조금 더 자세하게 이야기해 봅시다. 호의 시작점과 끝점을 각각 어떻게 표현하는지 적절하게 이해하기 위해, 당신이 학교 다닐 적의 수학과 책에서의 원의 각도를 기억해 봅시다.

당신의 젊을 적을 회상하면서, 위의 이미지를 보고 있을 것입니다. 원은 0도에서 시작하고, 360도를 만들고 완벽한 형태를 만들 때까지 반시계방향을 따라갑니다. 우리가 위에서 보는 것에 기초하여, 호를 그리는 것은 어렵지 않습니다. 어떤 A도에서 시작하여, B도로 간 후, 끝납니다. 동의하나요? 나도 그렇습니다. 하지만 당신이 쉽게 호를 그릴 수 있는 비밀이 있습니다. 모든 것을 잊어버리세요!

iOS에서 많은 것들은 일반적인 수학에서 우리가 아는 것과 비교했을 때 정반대로 되어 있습니다. 좌표계를 예로 들어봅시다. iOS에서 수직 아래로 향할 때 Y값이 증가하는 반면에, 데카르트 좌표계에서는 감소합니다. 같은 일이 위에서 보는 원의 각도에서도 일어납니다. iOS에서 원은 0도에서 시작하지만, 360도를 그릴 때까지 시계 방향으로 움직인다는 것을 고려해야 합니다.

이미지를 보면서, 우리의 예제에서 호가 180도에서 시작하여 0도에서 끝난 후 시계 방향으로 따라갔음을 고려한다면, 마지막 스크린샷에 만들어진 호가 어떻게 만들어졌는지는 명백해집니다.

더욱 명백하게 하기 위해, 다음의 호를 만들어 보세요. (왼쪽 반원)

이것을 성취하기 위한 두 가지 방법이 있습니다.

  • 위에 보여지는 원의 각도에 의하여 90도에서 시작하여 270도로, 시계 방향으로 가는 것
  • 또는 270도에서 시작하여 90도로 가고, 반시계 방향으로 따라가는 것

두 번째 경우를 선택하여, 반시계 방향으로 움직여 봅시다.

마지막 예는 이를 완전히 이해했는지 확인하는 것입니다. (반원에서 조금 더 차 있는 모양)

그리고 시작 각도와 끝 각도에 대해 몇 마디 더 하겠습니다. 우리 모드는 각도가 육십분법(각도) 또는 호도법(라디안)으로 표현되는 것을 알고 있습니다. 위의 생성자에 있는 두 개의 매개변수는 각각 라디안 값을 기대하지만, 사람은 육십분법을 생각해내는 것이 더 쉽습니다. 이러한 이유로, 각도를 라디안으로 바꾸는 것을 행하는 CGFloat 익스텐션을 제공하고, 마지막 세 개의 예제에서 사용할 것입니다.

이 익스텐션으로, 육십분법으로 나타내어진 각도를 표현하는 어떠한 CGFloat 숫자를 각각 라디안 값으로 쉽게 바꿀 수 있는데, 이전 예제의 시작 각도와 끝 각도 매개변수 값에서 보여지는 것과 비슷합니다.

 

베지어 경로와 CAShapeLayer

이전의 예제에서 봤듯이, UIView 의 서브클래스에 있는 draw(_:) 메소드는 그려질 베지어 경로를 위한 컨텍스트를 제공합니다. 그러나, 사용자 정의 그림을 그리는 것을 수행할 때 그 메소드를 항상 재정의해야하는 것은 아니며, 사실 가능하면 그것을 피해야 하는데, 성능에 영향을 줄 수 있기 때문입니다. 좋은 대안은 CAShapeLayer 객체를 사용하는 것인데, 그것들을 사용함으로써 훨씬 더 빠르게 렌더링되고 부가적인 유연함을 얻을 수 있습니다.

이 포스트의 시작에서 말했듯이, CAShapeLayerCALayer 의 서브클래스이고, 이러한 객체를 만들고 사용함으로써 우리는 실제로 뷰에 여분의 레이어를 추가합니다. 그것은 뷰의 최종 결과를 사용자 정의하는 데 사용 가능한 몇 가지 프로퍼티들을 가지고 있으며, 대부분은 애니메이션이 가능한데, 이것은 그 값들이 CABasicAnimation 과 같은 애니메이션을 줄 수 있는 방법을 사용하여 변화될 수 있다는 것을 의미합니다. 애니메이션은 레이어에서 큰 챕터로 구성되어 있고, 이 포스트에서는 확인하지 않을 것입니다. 아마 어떠한 시점에서 다른 튜토리얼로 구성될 것입니다.

CAShapeLayer 객체를 만들 때, 그것의 형태를 명시하거나, 다른 말로 경로를 명시하는 것은 필수적입니다. 경로를 설정하는 가장 쉬운 방법은 베지어 경로를 먼저 만들고, 그 다음에 그것을 형태 레이어 객체에 할당하는 것입니다. 이것이 가장 일반적인 접근법이기 때문에, 다음의 예제에서 이것을 사용해 볼 것입니다.

경로를 명시하는 것 이외에, 설정 가능한 다른 프로퍼티들이 있고, 종종 이것들 또한 설정되어야 합니다. 예를 들어, 배경색, 테두리색, 선 굵기, 그리고 위치가 그러한 것들 중에 있으며, 우리는 그 모든 것들이 활동중임을 알게 될 것입니다. 또한, 형태 레이어 객체가 뷰의 레이어에서 사용될 수 있는 두 가지 방법이 있습니다. 서브레이어로서, 또는 마스크로서 그렇습니다. 잠시 후 그것들의 차이를 알아볼 것입니다. 마지막으로, 다중 형태 레이어들은 레이어에 서브레이어로 만들어지고 추가될 수 있습니다. 그러나, 최상단의 대부분의 레이어들이 그들 아래에 있는 것들을 덮기 때문에, 조심스럽게 크기와 위치를 설정해야 합니다.

 

간단한 CAShapeLayer 객체

우리의 첫 번째 CAShapeLayer 객체를 만들어 봅시다. 하지만 그 전에, draw(_:) 메소드에서 사용했던 모든 코드를 주석처리 해야 합니다. 우리가 할 수 있는 가장 간단한 것은 이것입니다.

CAShapeLayer 를 만들어 뷰 레이어의 서브레이어로 추가하는 코드 작성하기

제일 먼저, 직사각형 베지어 경로를 그리기 위해 이전 파트에서 구현한 createRectangle() 메소드를 호출합니다. 이 메소드에 명시된 경로는 우리 클래스의 path 프로퍼티로부터 오는 것을 기억하세요.

다음으로, 형태 레이어 객체를 만드는데, 위에 표시된 것처럼 항상 쉽습니다. 먼저 객체를 생성하고, 베지어 경로를 그것의 path 프로퍼티에 할당합니다. 하지만 이 프로퍼티가 CGPath 값을 기대하는 반면에 우리의 pathUIBezierPath 객체임을 알아두세요. 그러므로 단지 cgPath 프로퍼티에 접근하는 것으로 그것의 Core Graphics 표현을 사용합니다.

마지막으로, 뷰의 레이어에 서브레이어로 추가합니다. 참고로 이것이 서브레이어를 추가하는 가장 일반적인 방법임에도 불구하고, 그러한 많은 것들을 가지고 있는 경우에 CALayer 클래스가 제공하는 다음의 메소드들을 사용할 수 있습니다.

insertSublayer(_:at:)
insertSublayer(_:above:)
insertSublayer(_:below:)

여기, 여기, 여기에서 각각 더 많은 것을 읽어보세요.

이제 이 메소드를 호출하고 결과를 확인하기 위해 애플리케이션을 실행해 보세요.

init(frame:) 에서 simpleShapeLayer() 호출하기

우리가 형태 레이어를 위한 배경색을 명시하지 않았다면, 검정색이 기본값으로 사용된 것을 확인 할 수 있을 것입니다. 이제 값을 설정하고, 더 많은 프로퍼티와 함께 놀아봅시다.

fillColor 프로퍼티와 strokeColor 프로퍼티는 CGColor 값을 기대하고 있고, 이것이 우리가 UIColor 객체를 cgColor 프로퍼티에 접근하여 CGColor 로 변환해야 하는 이유입니다. lineWidth 프로퍼티를 사용하여 경계선의 두께를 특정할 수 있습니다.

 

마스크 또는 서브레이어?

형태 레이어는 두 가지의 다른 방법으로 사용될 수 있습니다. 뷰의 기본 레이어에 마스크로서, 또는 위에서 했던 것처럼 그것의 서브레이어로서 추가되는 방법이 있습니다. 하지만 이 두 개의 방법 사이의 진짜 차이는 무엇일까요? 그리고 무엇을 사용할 지 어떻게 결정할 수 있을까요?

다시 한번 예제로 갑시다. 새로운 메소드가 있음을 알아챘을 것입니다.

다시 한번 이전에 만든 createTriangle() 메소드를 사용하고 있으며, 그 지점에 머무를 수 있습니다. 그 메소드는 삼각형의 경로를 만드는 경로를 그리고, 그것을 사용하기 위해 새로운 형태 레이어 객체를 생성합니다. 배경색을 명시해 주었고, 뷰의 기본 레이어에 서브레이어로 추가했습니다. 어려운 것도 없고, 새로운 것도 없습니다.

이 새로운 메소드를 호출합시다. 그리고 예상했던 것보다 훨씬 더 아름다운 결과를 확인하세요.

명백하게, 우리의 새로운 형태 레이어는 뷰의 최상단에 자리하고 있는 멋있는 노란색 삼각형이고, 배경색으로 커버되지 않는 부분은 어두운 회색으로 남아 있습니다.

메소드에 다음과 같은 변화를 줘 봅시다.

self.layer.addSublayer(shapeLayer) -> self.layer.mask = shapeLayer

addSubLayer(_:) 메소드를 주석처리하고, 대신 mask 프로퍼티를 사용하세요.

이번에는 삼각형 모양의 형태만 있을 뿐이고, 배경색을 지정해 주었으나 더이상 노란색은 보이지 않습니다. 심지어 형태 레이어의 경로가 커버하지 못하는 뷰의 부분은 사라졌습니다!

음, 이것이 마스크가 동작하는 방법입니다. 경로의 부분이 아닌 뷰의 부분은 잘려나가고, 실제로 뷰는 마스크로서 적용된 형태 레이어의 형태를 취합니다. 게다가, 배경색과 같은 프로퍼티는 새로운 레이어가 마스크일 때 아무런 효과도 내지 못합니다. 그러므로 삼각형의 색을 바꾸고 싶다면, 뷰의 배경색을 갱신할 필요가 있습니다.

말하자면, 뷰에 사용자 정의 형태를 주고 싶다면 mask 프로퍼티를 사용하고, 그렇지 않다면 당신이 만든 새로운 형태 레이어를 서브레이어로 추가하고 최종 목적을 달성하기 위해 뷰와 레이어의 프로퍼티들을 조작하세요.

 

단 하나의 서브레이어가 아니다

나는 이미 당신이 분명히 뷰의 레이어에 서브레이어로 하나 이상의 형태 레이어를 추가할 수 있다고 말했습니다. 그러므로 그것에 대해 더 많은 것을 알아볼 것입니다. 이 파트에서, 두 개의 서브레이어를 만들고 그것들의 몇몇 프로퍼티를 바꾼 후, 뷰에 적절하게 표시할 것입니다. 시작하는 사람을 위해, 두 개의 경로와 두 개의 형태를 만듭시다.

두 개의 UIBezierPath 인스턴스와 두 개의 CAShapeLayer 인스턴스 만들기

위의 코드는 삼각형 형태에 해당하는 두 개의 경로를 만드는데, 첫 번째는 코를 든 모양이고, 두 번째는 코를 내린 모양입니다. 이번에 우리는 경로와 형태에 뷰의 전체 너비와 높이를 사용하지 않습니다. 그것들의 절반만을 사용합니다. 일단 두 가지 경로가 특정되면, 두 개의 형태 레이어 객체에 할당되고, 뷰의 레이어에 서브레이어로 차례로 추가될 것입니다.

이 새로운 메소드를 호출합시다.

보다시피 두 번째 형태 레이어가 첫 번째 것을 포개고 있습니다. 그리고 올바르게 보이지 않습니다. 그것을 움직여보는 건 어떨까요?

두 번째 레이어의 위치를 아래로 내리기

좀 더 낫네요! 방금 사용한 position 프로퍼티는 정말로 중요한데, 형태 레이어를 움직이게 허용해주기 때문입니다. 그리고 두 개의 레이어를 중앙에 두고 싶다면 어떻게 할까요? 다음의 두 줄로 코드를 교체하면 됩니다.

대부분의 경우 position 프로퍼티는 형태 레이어를 다른 위치에 두는 데 적합한 프로퍼티입니다. 그러나 레이어의 프레임을 바꾸기를 원하는 때도 있을 것입니다. 그러한 경우에는, 가슴 속에 새겨두세요. frame 프로퍼티에 접근해서는 안됩니다. 대신에 서브레이어의 bounds 프로퍼티에 접근하세요.

문서에 따르면, bounds 프로퍼티는 그 자신의 좌표계에서 서브레이어의 기원origin 과 크기size 를 표현합니다. 이것이 의미하는 것을 이해하고 직사각형의 바운드의 기원과 크기를 어떻게 바꾸는지 보기 위해 예제에서 약간의 변화를 줘봅시다.

위의 추가로 서브레이어의 기본 크기를 변화시켰고, 명백하게 뷰의 볼 수 있는 공간을 초과하고 있습니다. 그려진 경로의 크기는 그것에 의해 영향을 받지 않지만, 기원을 변경한다면 경로는 움직일 것입니다.

중요한 것을 알아채세요. 일반적인 뷰의 좌표계에서 오리진 포인트의 X값을 증가시켜 뷰를 오른쪽으로 이동시키고, Y값을 증가시켜 아래로 이동시킨 것과는 다르게, 여기서는 정반대로 일어납니다. 우리는 음수값을 주어서 경로를 오른쪽 그리고 아래쪽으로 이동시켰습니다. 양수값은 경로의 오리진을 각각 왼쪽과 위쪽으로 옮길 것입니다. 그러나, 대부분의 경우 당신은 단지 레이어의 위치를 다시 설정할 필요가 있을 것이고, 아마 bounds 를 사용하는 일은 드물 것입니다.

 

경로 혼합하기

이 포스트에서 경로에 대해 논의하기 시작했을 때, UIBezierPath 클래스가 제공하는 일련의 생성자들을 훑었고, 몇 가지 종류의 형태는 단지 이러한 생성자를 사용해서 만들 수 있음을 알았습니다. 그러나, 다양한 경로 타입이 함께 섞인 것은 확인하지 못했고, 이제는 도형 레이어에 이야기하기 가장 좋은 때입니다.

addLine(to:) 메소드가 두 포인트 사이에 선을 만드는 것을 이미 알고 있는 것과 비슷하게, 호와 곡선 같은 다른 종류의 형태를 만드는 메소드들이 있습니다.

다음의 예제로 살펴보세요.

복잡한 뷰를 그리는 코드 작성하기

첫번째로, 코드에서 우리는 형태 레이어를 서브레이어가 아닌 마스크로 사용하였음을 알아두세요. 일부러 그렇게 했는데, 단지 최종 형태를 강조하고 싶었기 때문입니다. 그러나, 중요한 것은 경로를 만드는 것입니다. 보다시피 몇 가지 선, 호, 곡선의 조합으로 이루어져 있습니다. 좌측 상단(0.0, 0.0)에서 그리기를 시작하고, 시계 방향으로 그리기를 이어나갑니다. 경로가 단계적으로 어떻게 그려지는지 알기 위해 조심스럽게 주어진 포인트들을 살피세요.

addCurve 메소드는 새로 나왔는데, 이것은 형태의 오른쪽에 보이는 곡선을 만듭니다. 곡선은 다음과 같은 것입니다.

베지어 곡선과 컨트롤 포인트. 참고 링크들 참고하기

위처럼 선을 그리는 메소드는 세 개의 매개변수: 곡선의 최종 포인트(끝점), 실제로 선의 곡률을 정의하는 두 개의 컨트롤 포인트를 기대합니다. 당신이 그것을 수정할 때 곡선이 어떻게 행동하는지 보기 위해 이 컨트롤 포인트의 값을 변경해 보세요. 그럼에도 불구하고, 불편한 진실이 있는데, 곡선 뒤편에 많은 수학적 거짓이 들어 있습니다.

경로의 초반에 만든 호에 관해서는, 새롭거나 어려운 것이 없어 언급할 거리가 없습니다. 호를 그리는 것에 대해 말한 것만 기억하세요. 그러면 당신은 우리가 책에 의해서 레시피를 따라갔다는 것을 알게 될 것입니다. 나머지도 우리가 아는 것들입니다.

 

보너스 : CATextLayer 사용하기

CATextLayer 클래스는 이러한 역할을 하는 클래스 중에서도 덜 알려지거나 사용 빈도가 낮은 것 중 하나입니다. 그것의 목적은 어떠한 텍스트를 표시하는 CAShapeLayer 와 같은 레이어를 만드는 것입니다. 그리고 이 클래스가 베지어 경로나 형태와 함께 하는 일이 없다고 할지라도, 나는 그것을 이 포스트에서 보여주기를 정말로 원합니다. 그래서 여기에서 읽을만한 추가적인 것으로 제공할 것입니다. 이 부분은 이 포스트에서의 작은 삽입 어구로 생각하세요.

모든 개발자들은 인터페이스에서 99%의 경우에 UILabel 객체를 사용하여 텍스트를 표시합니다. 수정될 수 없는 텍스트에 대해서 말이죠. 하지만 때때로 레이블을 사용하는 것은 잘 작동하지 않거나, 레이블을 포함하는 뷰의 레이어에 많은 서브레이어가 추가되는 경우 난잡해보일 수도 있습니다. 이러한 경우에, CATextLayer 가 당신이 정말로 필요로 하는 것입니다 이것의 추가적인 이점은 어떠한 뷰라도 최상단에 텍스트 레이어를 추가하는 것이므로, 단지 레이블에 의존하지 않아도 됩니다.

CATextLayer 를 만들고 사용하는 것은 텍스트가 표시되기 원하는 방법대로 프로퍼티들을 설정하는 문제만 있을 뿐입니다. 다음의 코드 스니펫은 우리 뷰의 기본 레이어의 서브레이어로 추가되는 텍스트 레이어 같은 것을 만듭니다.

CATextLayer 만드는 코드 작성하기

몇 가지 알아둘 것이 있습니다.

  • textLayer 객체의 string 프로퍼티를 사용하여 우리가 표시하기 원하는 텍스트를 설정합니다.
  • 이름과 크기를 제공하여 폰트를 특정할지라도, 폰트 크기를 특정하기 위해 fontSize 프로퍼티를 사용하는 것 또한 필수적입니다. 폰트 생성에서 주어진 크기는 중요하지 않아 보입니다.
  • 텍스트 정렬은 textAlignmentMode 프로퍼티를 사용하여 설정 가능합니다. 당신이 설정할 수 있는 특정한 값들이 있고, kCAAlignmnetCenter 를 사용하여 텍스트를 중앙 정렬시킬 수 있습니다. 여기에서 더 많은 값들을 확인하세요.
  • UILabel 객체에서 일어나는 것처럼, 라인 수를 설정할 수 있는 선택지는 여기에는 없습니다. 대신에, \n 문자를 사용하여 텍스트를 여러 줄로 분리할 수 있습니다. 또한, 텍스트가 적절하게 레이어의 안에 들어갈 수 있도록 레이어의 프레임을 적절하게 설정하는 것을 염두해 두세요.
  • 항상 contentScale 프로퍼티를 사용하세요. 그래야 스케일 값을 고려하여 화면에 텍스트가 올바르게 그려질 수 있습니다. 사용하지 않으면, 결과는 픽셀화됩니다.

더 많은 정보는 공식 문서에서 확인하세요.

 

요약

이 포스트의 끝에서, 나는 이번 파트들에 있는 컨텐츠가 모두에게 유용하고 교육적이었으면 좋겠습니다. 여기서 만난 기초들을 바탕으로, 사용자 정의 뷰나 사용자 정의 형태 레이어와 함께 복잡한 경로를 조합하는 진보된 경우에 대해서 더 나아갈 수 있을 것입니다. 사실, 여기서 배운 것을 사용하는 것은 당신에게 달려 있습니다. 하지만 이제 지식의 이점을 살리고 마법을 부릴 때가 되었습니다!

 

 

 

참고 링크

A Beginner's Guide to Bezier Paths and Shape Layers

중학생도 알 수 있는 베지어 곡선

'iOS' 카테고리의 다른 글

Swift API Design Guidelines  (0) 2018.09.22
Memory Profiling  (0) 2018.09.17
KVO (Key-Value Observing)  (0) 2018.08.29
UITableViewCell / UICollectionReusableView 의 재사용  (0) 2018.08.24
커스텀 키보드 익스텐션 시작하기  (0) 2018.08.17
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함