나만의 공부 노트

6강 정리 본문

(2017) 스탠포드 강의

6강 정리

va95 2021. 2. 19. 03:02

학습 내용

 

Draw

모든 뷰가 draw를 하지 않습니다.(UIImageView, UIStackView 등등은 subview가 드로잉을 합니다.)

그렇기 때문에 draw를 하지 않는 뷰는 함수를 구현하지 않는게 좋습니다.(그렇지 않으면 새로운 버퍼를 만드는 등 준비 동작을 합니다.)

 

UIGraphicsGetCurrentContext()

해당 전역함수는 nil을 반환할 수 있는 옵셔널 타입입니다.

그러나 draw가 호출될 때에는 명확한 context를 가지고 있기 때문에 nil이 반환될 일이 없습니다.

또한, CG를 이용하여 context에 그리고 있는 경우, strokePath() 또는 fillPath()를 호출하면 context에 있는 경로를 지우면서 그리기 때문에 경로가 남지 않습니다.

 

UIView

UIView에는 view 자체에 그릴 수 있는 함수인 draw()가 있습니다.

새로 그릴 필요가 있다면 setNeedsDisplay()를 호출합니다.

또한, UIview에는 bound가 바뀌면 layoutSubviews()가 호출됩니다.

bound가 바뀌지 않고 서브뷰의 위치들을 바꾸고 싶다면 setNeedsLayout()을 호출합니다.

layoutSubviews()를 정의할 때, 반드시 super.layoutSubviews()를 먼저 호출한 뒤 정렬합니다.

super.layoutSubviews()는 오토레이아웃에서 지정한 정렬을 수행합니다.

따라서 layoutSubviews()에서 정렬한 코드는 오토레이아웃보다 우선순위가 높습니다.(super.layoutSubviews를 마지막에 쓰면 반대입니다.)

 

UILabel

label에 adjustsFontSizeToFitWidth라는 변수는 default로 false값이 설정되어 있습니다.

true로 설정 시, 자동으로 text 길이에 맞게 너비를 줄이기만 합니다.(자동으로 늘릴 순 없습니다.)

만약 label에 너비가 설정되어 있다면, sizeToFit() 함수는 너비는 그대로 둔 채 높이만 변경합니다.

따라서 너비를 지우고 sizeToFit()을 사용해야 올바른 너비를 얻을 수 있습니다.

 

CGAffineTransform

모든 뷰는 var transform을 가지고 있습니다.

아핀 변환을 지원하며, translate, rotate, scale 세 가지를 지원합니다.(역행렬도 있습니다.)

비트 변환을 하기 때문에, scale의 경우 울퉁불퉁한 경계면이 생길 수도 있습니다.

CGAffineTransform.identity를 통해 기본 행렬을 만들고 .rotated 와 같은 함수로 행렬을 만듭니다.

아핀 변환은 bounds를 기준으로 움직입니다.

 

traitCollectionDidChange

여러 속성들(설정에서 텍스트 크기 조절, 핸드폰 화면 방향 변경 등등의 여러 시스템 설정들)이 변경되었을 때, 뷰에 정의되어 있는 이 함수가 호출됩니다.

func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) 형태로 사용하며 속성이 변경되었을 때, 주로 view를 바꾸는 데 사용됩니다.

 

IBDesignable

class 선언 앞에 @IBDesignable 키워드를 사용하면 인터페이스 빌더가 바로 드로잉을 해줍니다.

Image 같은 경우는 UIImage를 불러올 때, 추가 인자가 필요합니다.

UIImage(named: "example", in: Bundle(for:self.classForCoder), compatibleWith: traitCollection)으로 불러오면 인터페이스 빌더가 이미지를 읽고 표시해줍니다.

 

Bundle

A Bundle is a directory with a standardized hierarchical structure that holds executable code and the resources used by that code. (실행 가능한 코드와, 그 코드에 의해 사용되는 리소스를 가진 디렉토리)

즉, 번들은 파일 시스템의 디렉토리로서 실행 코드, 이미지, 소리같은 관련 자원(resource 이하 리소스) 등을 한 공간에 그룹짓습니다.

일반적으로 우리가 구성하는 앱 또는 프레임워크는 하나의 번들이고, 번들마다 한 개의 info.plist를 가지고 있습니다. (info.plist는 Bundle.main.bundleIdentifier로 접근할 수 있습니다.)

앱에서 Bundle.main(클래스 변수)을 접근하면 해당 앱의 번들을 가져올 수 있습니다.

또는 위처럼 Bundle(for:self.classForCoder) 해당 클래스가 속한 번들을 가져올 수도 있습니다.

 

TraitCollection

원래 아이폰은 단일 디바이스 크기를 지원했지만 아이패드, 애플 워치 등 새로운 기기의 등장과 아이폰 자체의 화면 크기 변화, 그리고 폰트나 로컬라이즈 이슈 등 다양한 것들의 변화에 대응하여 UI를 효과적으로 다룰 도구가 필요했고, 그래서 iOS 8 이후 소개된 개념이 UITraitCollection입니다.

에서 보듯 UITraitCollection은 iOS의 인터페이스 환경을 나타내는 trait의 모음입니다.

UI를 배치하는 Layout, 모습을 표현하는 Appearance, 그리고 3DTouch의 가능 여부 등 다양한 환경 값을 가지고 있습니다.

UIScreen -> UIWindow -> UIViewController -> UIPresentationController -> UIView 등 UITraitEnvironment 프로토콜을 따르는데 이 프로토콜은 Required인 traitCollection 프로퍼티를 가지고 있기 때문에, 보통 우리가 제어하는 뷰와 뷰 컨트롤러는 traitCollection을 default 값으로 가지고 있습니다.

traitCollection은 get 프로퍼티이며, 현재 환경에 대한 정보를 얻을 수 있습니다.

한 예로 traitCollection.layout을 통해 왼쪽->오른쪽 화면 출력인지, 오른쪽->왼쪽 화면 출력인지 알 수 있습니다.

 

+ Size Class

사이즈 클래스는 unspecified, compact, regular의 3종류가 있고, 각각의 기기들은 가로 세로 2종류씩 총 4가지 형태의 사이즈 클래스를 갖습니다.

아이패드는 가로 세로 regular인 반면, 아이폰은 가로 세로가 compact입니다.(특정 세대 이후 아이폰은 세로가 충분이 길기 때문에 regular로 되어 있습니다.)

그래서 세로가 regular인 아이폰을 가로 모드로 사용한다면 splitview를 사용할 수 있습니다!

이처럼 사이즈 클래스를 나뉘어 놓았기 때문에, 사이즈 클래스를 기준으로 UI를 만들어 나갈 수 있습니다.

 

UIView는 UITraitEnvironment 프로토콜을 채택했기 때문에 var traitCollection, func traitCollectionDidChange 가 구현되어 있습니다. (기본 클래스에 traitCollectionDidChange가 구현이 되어있어서, 오버라이드해서 사용하면 됩니다.) 

traitCollection이 변경되면 screen -> window -> viewController -> view -> subviews 순으로 traitCollectionDidChange가 호출됩니다.

 

또한 인터페이스 빌더에서 보이는 + 버튼을 누르면 trait마다 적용할 속성을 추가할 수 있습니다.

 

+ 위의 UIImage를 가져오는 코드에서 compatibleWith 인자로 traitCollection이 필요했던 이유는 이미지와 관련된 환경 값을 알아야하기 때문입니다.

 

IBInspectable

인터페이스 빌더는 타입을 자동으로 추론할 수 없습니다.

위의 키워드를 사용한다면 타입을 명시적으로 선언해야합니다.

 

Gestures

제스처는 기본적으로 추상적인 class이기 때문에 모든 제스처를 인식할 수 없습니다.

하지만 애플이 하위 클래스로 구현해놓은 gesture recognizer만으로도 충분히 제스처를 인식할 수 있습니다.

위와 같이 보통 view controller안에서 IBOutlet의 didset을 이용하여 제스처를 인식하고 핸들링하도록 구현합니다.

 

action 메소드에서는 알맞는 gesture recognizer를 인자로 받은 후, 해당 인자가 가지고 있는 함수들을 사용해 정보를 얻어오면 됩니다.

각각의 gesture recognizer가 가지고 있는 메소드는 모두 다르며 애플 문서를 참고하시면 됩니다.

한 예시로 pan gesture recognizer는 translation, velocity, setTranslation 함수를 가지고 있습니다.

 

모든 gesture recongnizer는 UIGestureRecognizer를 상속받고 UIGestureRecognizer에는 var state라는 프로퍼티를 공통적으로 가지고 있습니다.

매우 유용한 변수인데, gesture를 인식받고 있는 상태를 나타냅니다.

예를 들어, gesture recongnizer가 시작 전일 때에는 .possible로 설정이 되어 있으며, 위의 사진에 보이는 것처럼 다양한 상태 중 하나를 가지고 있습니다. (단, swipe는 연속적인 제스처가 아니기 때문에 .ended 또는 .recognized 밖에 인식이 되지 않습니다.)

.failed는 여러 개의 제스처로 해석될 때 탈락하는 제스처에서 발생합니다.(ex. tap 인식 -> pan 인식, 보통 제스처는 바로 인식이 되지 않고 어느정도의 시간이 경과한 후에 state에 적용이 됩니다.)

.cancelled는 보통 드래그 앤 드랍에서 발생하며, 앱이 백그라운드로 진입하는 등 취소되는 상황에서 발생합니다.

 

몇몇 gesture recognizer는 기본적으로 설정해야하는 값들이 있습니다. (ex. 몇번의 탭이 필요한지)

 

target action

target action 패턴은 기본적으로 objective-c 환경에서 제작되었습니다.

따라서 action에 필요한 메서드는 objc로도 호환이 되어야 하므로, action 함수에 @objc 키워드를 붙여야합니다.

#selector는 함수를 주소값으로 참조하는 키워드입니다.

'(2017) 스탠포드 강의' 카테고리의 다른 글

8강 정리  (0) 2021.02.20
7강 정리  (0) 2021.02.19
5강 정리  (0) 2021.02.17
4강 정리  (0) 2021.02.17
3강 정리  (0) 2021.02.17