연습/swift개발

4.5 얼굴앱-얼굴형

TimeSave 2022. 1. 20. 21:55

 

여기서 계산기 실습을 하지 않을거야

 

완전히 새로운 앱을 만들거야

 

여기로 내려와서 새로운 Xcode 프로젝트를 만들거야

 

여기있지, 여기 iOS application에서 single view application을 만들거야

 

FaceIt이라고 부를거고 얼굴그리기앱이니까

 

당연히 Objective-C말고 Swift지

 

Universal 앱으로 할거야

 

드디어 여러개의 MVC도 같이 만들거야

 

테스팅이나 코어데이터도 안할꺼고

 

계산기를 넣던 같은 곳에 넣을거야

 

아직 소스코드 컨트롤은 안할거야

 

자, 여기 우리의 새로운 앱이 있네

 

항상 하던 같은 일을 할거야

 

images.Assets라고 부르긴 했는데

 

Assets.xcassts라고 불려

 

여기는 원하는 이미지를 드래그해서 여기에 넣는 곳이야

 

foo같은 이름을 가진 UIImage같은 것들말이야

 

여기에 넣을거야, 하지만 분명히 우린 이러한 것들이 필요없지

 

Supporting Files 그룹안으로 넣을거야

 

그러면 왼쪽을 볼 때, 주의를 산만하게 하지 않을거야

 

우리 정말로 봐야할 건 스토리보드와 viewController이지

 

다음주에 ViewController Lifecycle에 대해 이야기할거야

 

일단 이것들을 지우고

 

첫번째로 ViewController 이름을 다시 지을거야

 

지난주에 말했던 ViewController같은 일반화된 이름이니까

 

별로 좋지않아

 

ViewController가 아닌 FaceViewController로 불리면 좋겠어

 

얼굴을 보여줄거니까

 

그냥 바로 FaceViewController라고 말할 수 있고

 

FaceViewController로 가기 좋다고 생각할 수 있는데

 

당연히 아직 뭔가 부족하지

 

첫번째로 아마 파일이름을 다시 짓는게 좋겠어

 

왜냐하면 스위프트에서 일반적으로 파일의 이름은

 

파일안에 있는 중요한 클래스 이름이 되어야 해

 

사람들이 파일의 이름을 보았을때 그 클래스를 기대할테니까

 

하지만 이걸로도 역시 충분하지 않지

 

왜냐하면 스토리보드로 돌아가면

 

여기에 있는 ViewController를 볼거야

 

사실 여기 위에 있는 inspector 를 볼수 있지

 

viewController 위를 클릭하고 여기에 있는 오른쪽 것을 보면

 

identity inspector라고 불리는 것을 볼 수 있을거야

 

여기선 스토리보드가 뷰 컨트롤러의 클래스를

 

뭐라고 생각하는지 볼 수 있어

 

현재는 ViewController라고 되어 있는 걸 볼 수 있지

 

일반적인 뷰 컨트롤러야

 

그리고 당연히 원하는 것이 아니니까 이름을 다시 적을거야

 

그냥 여기를 클릭하고 FaceViewCotnorller로 바꿀수 있지

 

과제2에서도 꼭 해야하는 일은 아니지만

 

viewController의 이름을 다시 짓는 일을 강력하게 추천해

 

과제2는 계산기에 관한 과제였지

 

ViewController에서 CalculatorViewController로 바꿔

 

과제3에서는 반드시 이렇게 하고 싶을거야

 

어쨌든 과제2에서 이름을 다시 짓는 것을 추천해

 

지금 내가 했던 것이 너가 해야 할 전부야

 

어렵지 않잖아. 좋은 연습이야

 

스토리보드를 코드에 연결하기위해 identity inspector에서

 

어떤 클래스를 가져야하는지 이해하는데 도움이 될거야

 

이름을 바꿨으니까 이제 얼굴을 그리고 싶어

 

UIView의 서브클래스인 face view가 필요해

 

UIView의 서브클래스를 만들어보자

 

프로젝트에 어떤 것을 추가할 때는 언제나

 

file에 new로 가서 오른쪽에 있는 file에서 만들거야

 

iOS source이고, Cocoa Touch Class가 될거야

 

UIView의 서브클래스가 될거니까

 

UIView나 UIViewController 같이 Cocoa Touch 안에 있는

 

어떤 서브클래스를 추가할 때는 언제나 Cocoa Touch로 해야 해

 

Cocoa Touch class를 더블 클릭 할거야

 

여기 Subclass of에 이미 UIView라고 적혀있지

 

특히 Objective-C를 한다면

 

NSObject 서브클래스들을 만들 수 있어

 

UIViewController 등 많은 클래스의 서브클래스를 만들 수 있어

 

하지만 우리는 여기에 있는 UIView로 할거야

 

FaceView라고 부를거야

 

뷰에 얼굴을 그릴꺼니까

 

여기에 있는 같은 FaceIt 그룹안에 넣을 거야

 

FaceIt그룹은 모든 것들을 가지고 있어

 

여기 안에 넣으면 이렇게 생겼지

 

FaceView에 drawRect가 있는 것에 주의해

 

여기 drawRect이 있지

 

하지만 주석처리 되어있지

 

왜 drawRect을 주석처리해서 줬을까?

 

사실 drawRect가 어떤 행동을 하지 않는다면

 

drawRect가 필요없지

 

만약 시스템이 drawRect를 갖는 UIView가 있다고 본다면

 

항상 스스로 그려내야 한다고 생각할거야

 

하지만 아무것도 그리지 않는다면 아마 하위뷰를 가지겠지

 

시스템이 너희가 자신을 그리도록 만드는데에

 

시간을 낭비하게 만들고 싶지 않을거야

 

하지만 우리는 당연히 그릴거니까

 

drawRect 주석처리를 지우고 얼굴 그리기를 시작하자

 

얼굴은 전에 그렸던 것처럼 그릴거야

 

직사각형이 될거고 이게 내가 그려야 할 영역인 bound가 되겠지

 

중앙에 나의 얼굴을 그릴거야

 

직사각형의 너비나 높이 보다는 작아야겠지

 

얼굴이 이 안에 꽉 차게 들어오도록 말이지

 

그리고나서 동그란 눈 두개와 웃는 얼굴을 그릴거야

 

웃거나 찌뿌리는 것처럼 움직이고 변할 수 있도록 만들거야

 

이게 우리가 만들 것이지

 

이 얼굴이 가운데에 오는 것을 원하고

 

너비나 높이보다 작지만 꽉 맞기를 원하기 때문에

 

얼굴의 가운데와 반경으로 시작하기위해 두개의 변수를 만들거야

 

그럼 해볼까

 

첫번째로 여기에 반경(radius)을 만들어보자

 

var를 만들거고 drawRect의 안쪽에서 실행할거야

 

skullRadius라고 부를거야

 

나의 얼굴의 두개골(skull)의 반경이니까

 

뷰의 너비 또는 높이의 최소값과 같도록 만들거야

 

뭐가 뷰의 너비와 높이일까

 

여기에 살펴볼 수 있는 다른 변수들이 있어

 

이 rect을 살펴볼 수도 있지

 

이렇게 선언할 수 있겠지 let width = rect.size.width

 

이렇게 하는게 맞을까?

 

아니, 틀렸지

 

이 rect은 단지 뷰의 어떤 부분을 그릴 것인지를 말하는 최적화야

 

내 얼굴은 전체 뷰 안에서 그려질 필요가 있지만

 

그렇지 않게 되면 적절한 사이즈가 되지 않을거야

 

전부 작아지게 될거야

 

rect은 적절하지 않지

 

frame.size.width라고 말할 수 있어

 

이것은 맞을까?

 

맞을까 아닐까?

 

몇명이 아니라고 고개를 젓는게 보이네

 

대답은 아니야

 

슈퍼뷰 좌표계 안에서 나를 포함하는 직사각형이 frame이야

 

나 스스로 그릴거야

 

슈퍼뷰의 좌표안에서 그릴 수 없어

 

내 좌표 시스템안에서 그려야해

 

우리가 여기서 원하는 것은 bounds야

 

bounds는 나의 좌표시스템에서 그릴 직사각형이야

 

높이도 같아 bounds.size.height

 

또 skull의 반경(반지름)을 원해

 

지름으로 하지 않을거야

 

반으로 나눈 반지름을 원하지

 

이렇게 지역변수를 별도로 만드는게 바보같이 보일지도 몰라

 

더 좋은 방법은 이렇게 여기로 복사 붙여넣기를 하는 거지

 

공간을 더 넓히자

 

나의 skull의 반경을 얻어냈어

 

좋은데 또 뭐가 필요할까?

 

skull의 중심이 필요하겠지

 

skullCenter라고 부를거야

 

그 center라는게 뭘까?

 

그냥 center라고 하면 될까? 좋은걸까?

 

아니지! center는 상위뷰 좌표의 center야

 

저건 내가 어디있는지 알려주는 것이지 center가 아니야

 

bounds의 실제 center를 얻을 흥미로운 방법이 두가지 있어

 

믿겨질지 모르겠지만 하나는 convertPoint라고 말할 수 있어

 

UIView의 메소드인 convertPoint야

 

center의 포인트를 변환할건데

 

내 superview로부터 바꿀거야

 

그러니까 superview 좌표계의 center의 포인트를

 

나의 좌표계의 포인트로 바꿔줄거란 이야기야

 

이렇게 하는게 맞고 이렇게 해야 작동되지

 

이렇게 해야 나의 좌표 시스템안에서 작동되는 거니까

 

같은 포인트이지만 나의 좌표 시스템안에 있는 포인트인거지

 

이게 내가 할 수 있는 방법 중 하나야

 

아마 우린 이런 방식으로 하진 않을건데

 

대신에 CGRect의 어떤 변수를 사용할 수 있을거야

 

예를 들어 아마 skullCenter를

 

x와 y값을 갖는 CGPoint라고 말할 수 있어

 

x는 bounds.midX가 되고 y는 bounds.midY 가 될거야

 

midX는 단지 CGRect안에 있는 프로퍼티야

 

직사각형을 걸쳐 중간이 되는 지점인 x를 말할거야

 

midY도 같은 거지

 

이제 센터와 반경을 얻었어

 

var로 뒀었지만 아마 let이었으면 할거야

 

계산을 완료해서 이후론 변하지 않을테니까

 

이제 이렇게 만들었으니까 여기 있는 동그라미로 된

 

두개골의 Bezier path를 생성해야 해

 

skull이라고 부르는 지역 변수를 만들거야

 

타입은 UIBezierPath일거고

 

UIBezierPath에 이니셜라이저가 이렇게 많은게 보이지

 

ovaIinRect 인자를 갖는 이니셜라이저를 사용할 수도 있겠지

 

왜냐하면 그 원은 타원형이니까

 

하지만 arcCenter가 있는 이니셜라이저를 사용할거야

 

센터와 반경 값을 가지고 있으니까

 

전체 bound에 타원형을 별도로 생성하고 싶진 않아

 

skull을 가져오기위해 그냥 사각형을 만들어야 해

 

여기있는 radius(반경)와 center(중심) 값을 받는

 

arcCenter(호 중심)라는 이니셜라이저를 사용할거야

 

여기에 이미 갖고 있는 값들이니까

 

호를 따라 돌아갈 startAngle과 endAngle도 있어

 

시계방향이든 반시계 방향이든 간에 말야

 

arc(호)의 중심은 무엇일까?

 

skullCenter이지

 

skullCenter가 내가 그릴 이 호의 중심이고

 

내 두개골이 될 녀석이지

 

radius에 들어갈 건 skullRadius겠지

 

그게 내 두개골을 그릴 반지름인거지

 

startAngle에서 endAngle은 radians으로 되어 있어

 

radians(라디안, 호도법)를 알고 있는 사람 손 들어봐

 

거의 다 알고 있네, 좋아

 

라디안은 원 하나를 그릴 때 0에서 2π 사이 값으로 표시해

 

그래서 0에서 시작해서 π의 두 배로 끝낼거야

 

마지막으로 시계방향이나 반시계 방향으로 그릴건가인데

 

우리 원은 완전히 한바퀴를 돌기때문에 어느 쪽이든 상관없어

 

난 그냥 반시계 방향으로 해 놓을게

 

그럼 이제 어떻게 호를 그리는지 알겠지

 

BezierPath를 생성했는데 여기서 path는 원 주위를 말하겠지

 

여기에 에러가 떴네

 

이 에러에 대해서 어떻게 생각하니?

 

여길 보면 Double 타입을 CGFloat 타입이어야 할

 

인자로 변환할 수 없다는 군

 

에러가 M_PI 부분을 가리키고 있어

 

전에 이야기 했던 건데 그리는 것 모두는 CGFloat로 되어 있어

 

이건 무슨 타입일까?

 

이 표현은 무슨 타입일까?

 

맞아, Double이지

 

그럼 우리는 여기에 Double을 사용할 수 없어

 

CGFloat이어야 하지

 

이렇게 해서 Double을 CGFloat으로 변환해야 돼

 

이건 왜 에러가 나지 않는걸까?

 

지난 시간에 말했던대로 말그대로 0.0이 있으면

 

double이라고 생각해

 

말했지 않았니? 내가 거짓말을 했나보군

 

왜냐하면 0.0을 봤을 때

 

리터럴이기 때문에 수많은 다른 타입으로 변환할 수 있어

 

스위프트는 리터럴에 대해 자동으로 타입변환을 할 수 있어

 

(*리터럴: 직접 값을 나타내는 형태)

 

0.0은 리터럴이니까 다른 타입의 숫자로 바꿀 수 있지

 

Double, Float, CGFloat같은 것들로 말이지

 

이것들로 어떻게 변환할지 다 알아

 

그럼 왜 여기에 CGFloat을 넣어야할까?

 

왜냐하면 이 메소드가 CGFloat 값을 취한다는 걸 알기 때문이야

 

그리고 리터럴이 있는데 인자 타입이 CGFloat이라면

 

할 수 있다면 CGFloat으로 변환하려 할거야

 

실제로 여기서도 변환이 되기도 하고

 

모두들 이해되니?

 

이제 skull을 가지고 있고 UIBezierPath 타입이지

 

이젠 skull의 속성을 설정할 수 있어

 

linewidth(선굵기)를 5.0 포인트로 줄 수 있겠지

 

컬러를 설정하기 원한다면

 

skull.setColor 이런식으로 하지 않을 거야

 

내가 원하는 색을 만들거야

 

두개골을 파랗게 만들어보자

 

blueColor는 UIColor안에 있는 타입 메소드지

 

그 다음엔 set이라고 할거야

 

setFill( )도 있고 setStroke( )도 있어

 

또 fill과 stroke 모두를 설정하는 그냥 set도 있어

 

이제 여기까지 한 걸 그릴려면 skull.stroke라고 할거야

 

이렇게 하면 내가 그렸던 호를 따라서 원을 그리겠지

 

선 굵기(lineWidth)와 색상이 함께 설정될거야

 

이 모든게 우리 얼굴의 두개골을 그리기위해 필요한 것들이야

 

이제 얼굴의 두개골을 그려낼 멋진 커스텀한 UIview가 생겼어

 

 

'연습 > swift개발' 카테고리의 다른 글

4.7 얼굴앱 - 눈 넣기  (0) 2022.01.20
4.6 얼굴앱 - 스토리보드 연결  (0) 2022.01.20
4.4 텍스트를 넣으려면  (0) 2022.01.20
4.3 그림을 그려보자  (0) 2022.01.20
4.2 뷰는 어떻게 만들까  (0) 2022.01.20