🌱 난 오늘 무엇을 공부했을까?
📌 면접스터디
📍 UIWindow의 역할
🔗 UIWindow 객체란?
- 앱의 사용자 인터페이스에 대한 배경과 뷰에 이벤트를 전달하는 개체
@MainActor class UIWindow : UIView
🔗 특징
- 뷰컨트롤러와 함께 작동해서 이벤트를 처리하고 앱 작동의 기본이 되는 다른 많은 작업을 수행
- 스토리보드는 앱 대리자 객체에 window 프로퍼티가 있어야함.
- 앱에서 스토리보드를 사용하지 않는 경우 이 Window을 직접 만들어야 함.
- 기본적인 window가 아닌 추가 window는 일반적으로 외부 화면에 콘텐츠를 표시하는 데 사용
- 고유한 모양이 따로 없지만, root view controller에서 관리하는 하나이상의 뷰를 호스팅한다.
- 인터페이스에 적합한 뷰를 추가해서 스토리보드 root view controller를 구성.
- UIWindow를 하위 클래스로 만들 필요는 거의 없다. 왜냐하면 Window에서 구현할 수 있는 동작의 유형은 일반적으로 더 높은 수준의 뷰 컨트롤에서 쉽게 구현이 가능하기 때문에.
🔗 주요 역할
- 일반적으로 아래의 작업을 수행해야 할 때 window를 사용
앱의 콘텐츠를 표시하는 기본 Window을 제공
추가 콘텐츠를 표시하려면 추가 Window(필요에 따라)을 만듬.
- 또 다른 사용 상황
- 다른 Window와 비교하여 Window의 가시성에 영향을 주는 z-axis level 설정
- Window를 표시하고 키보드 이벤트의 대상으로 만듬.
- Window의 좌표계에서 좌표 값을 반환
- root view controller의 Window 변경
- Window가 표시되는 화면 변경
🔗 키보드 동작 이해
- 터치 이벤트는 발생한 Window로 전달되지만, 해당 좌표 값이 없는 이벤트는 key window로 전달된다.
- 한 번에 하나의 Window만 key window이 될 수 있으며 Window의 isKeyWindow 속성을 사용하여 상태를 결정
- 어떤 Window이 핵심인지 알아야 하는 경우
didBecomeKeyNotification
및 didResignKeyNotification
알림을 관찰
- key window는 키보드 및 기타 비터치 관련 이벤트를 수신
- 시스템은 앱의 주요 Window 변경에 대한 응답으로 이러한 알림을 보낸다.
- Window가 key window가 되도록 강제로 정하거나, Window가 키 상태를 사임하도록 설정하려면 적절한 메서드를 호출해야함.
https://developer.apple.com/documentation/uikit/uiwindow
https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW1
📍 UINavigationController 역할
🔗 UINavigationController란?
- 계층으로 콘텐츠를 탐색하기 위해 스택 기반의 체계를 정의하는 컨테이너 뷰 컨트롤러
- 컨테이너 뷰 컨트롤러 : 자체 내부에 다른 뷰 컨트롤러의 콘텐츠를 포함하는 컨트롤러
@MainActor class UINavigationController : UIViewController
- 계층 구조의 각 수준에서 해당 수준의 콘텐츠를 표시하는 적절한 화면(사용자 정의 뷰 컨트롤러에서 관리)을 제공
- 내비게이션 인터페이스에서 하나 이상의 하위 뷰 컨트롤러를 관리하는 컨테이너 뷰 컨트롤러
- 한 번에 하나의 하위 뷰 컨트롤러만 표시
뷰 컨트롤러에서 항목을 선택하면 새로운 뷰를 보여주고 이전 뷰를 숨긴다.
인터페이스 상단에 있는 내비게이션 바에서 뒤로 버튼을 탭하면 상단 뷰 컨트롤러가 제거되어 아래에 있는 뷰 컨트롤러가 표시
🔗 특징
-
내비게이션 스택이라고 하는 정렬된 배열을 사용하여 자식 뷰 컨트롤러를 관리
-
첫번째 뷰컨이 root view controller이고 스택의 가장 아래부분임.
-
마지막 뷰컨이 현재 표시되는 뷰 컨트롤러이고 스택의 가장 윗부분임.
-
내비게이션 바는 항상 존재하며 내비게이션 컨트롤러 자체에서 관리하며 하위 뷰 컨트롤러에서 제공하는 콘텐츠를 사용하여 내비게이션 바를 업데이트
- isToolbarHidden 속성이 false이면 내비게이션 컨트롤러는 최상위 뷰 컨트롤러에서 제공하는 콘텐츠로 도구 모음을 유사하게 업데이트
-
내비게이션 컨트롤러는 대리자 개체로 동작을 조정
- 델리게이트 객체는 뷰 컨트롤러의 푸시 또는 팝핑을 재정의하고, 사용자 정의 애니메이션 전환을 제공하고, 내비게이션 인터페이스의 기본 방향을 지정
-
탐색 컨트롤러와 관리하는 개체 간의 관계
-
네비게이션도 View 객체를 가지고 있다.
- 탐색 모음, 선택적 도구 모음 및 최상위 뷰 컨트롤러에 해당하는 콘텐츠 View를 통합한 View
-
아래 이미지는 전체 탐색 인터페이스를 표시하기 위해 이러한 View가 구성되는 방법을 보여줌
- 내비게이션 바 및 도구 모음 뷰의 내용이 변경되더라도 View 자체는 변경되지 않음.
- 실제로 변경되는 유일한 View는 탐색 스택의 최상위 뷰 컨트롤러에서 제공하는 사용자 지정 콘텐츠 뷰.
-
내비게이션 바의 모양 관련 속성을 사용자 지정할 수 있지만 frame, bounds, or alpha를 직접 변경해서는 안 된다.
-
탐색 모음의 전체 모양을 사용자 지정하려면 UIAppearance API를 사용
https://developer.apple.com/documentation/uikit/uinavigationcontroller
📍 setNeedsLayout, setNeedsDisplay
🔗 setNeedsLayout
- layoutSubviews()를 직접 호출하면 안되기 때문에 setNeedsLayout을 호출한다.
- 수신기의 현재 레이아웃을 무효화하고 다음 업데이트 주기 동안 레이아웃 업데이트를 트리거
- 요청을 기록하고 즉시 반환
뷰의 하위 뷰 레이아웃을 조정하려면
애플리케이션의 메인 스레드에서 이 메서드를 호출
- 즉시 업데이트를 강제하지 않고 대신 다음 업데이트 주기를 기다리기 때문에 해당 뷰가 업데이트되기 전에 여러 뷰의 레이아웃을 무효화하는 데 사용할 수 있다.
- 이 동작을 통해
모든 레이아웃 업데이트를 하나의 업데이트 주기로 통합할 수 있으며 이는 일반적으로 성능에 더 좋다
.
🔗 setNeedsDisplay
- draw(_ rect: CGRect)를 직접 호출하면 안되기 때문에 setNeedsDisplay를 호출해서 뷰를 다시 그려야 함을 알린다.
- 수신자의 전체 범위 사각형을 다시 그려야 하는 것으로 표시
- 요청을 기록하고 즉시 반환
뷰의 내용을 다시 그려야 함을 시스템에 알릴 수 있다
.
- 뷰는 무효화된 모든 뷰가 업데이트되는 시점인 다음 드로잉 주기까지 실제로 다시 그려지지 않는다.
- 뷰의 내용이나 모양이 변경될 때만 뷰를 다시 그리도록 요청하려면 이 방법을 사용해야 함.
- 뷰의 geometry(x, y값?)만 변경하면 일반적으로 뷰가 다시 그려지지 않습니다.
- 기존 콘텐츠를 다시 표시하면 변경되지 않은 콘텐츠를 다시 그릴 필요가 없으므로 성능이 향상
🔗 공통점
- 즉시 업데이트가 아닌 View의 메인 런 주기에 적용된다.
🔗 차이점
📍 UIStackView 장점과 단점
🔗 UIStackView란?
- 스택뷰를 사용하면 자동 레이아웃의 기능을 활용하여 장치의 방향, 화면 크기 및 사용 가능한 공간의 모든 변경 사항에 동적으로 적응할 수 있는 사용자 인터페이스를 생성할 수 잇다.
🔗 장점
- 스택 뷰를 사용하면 오토 레이아웃을 직접 사용하지 않고도 내용을 배치할 수 있지만, 스택 뷰 자체의 위치를 지정하려면 여전히 자동 레이아웃을 사용해야 함.
- 스택 뷰를 사용하면 새로운 뷰가 추가될 때 자동으로 조절해주기 때문에 모든 레이아웃의 변경이 필요하지 않다.
🔗 단점
- 내부에 있는 서브뷰들의 레이아웃이 스택 뷰의 환경설정에 따라 변화되기 때문에 각각의 서브뷰들의 세밀한 레이아웃 설정 부분이 어려울 수 있다.
- 예) 공통 space 는 8인데 나는 특정 부분만 5로 띄어주고 싶은 상황
- vertical 방식으로 사용중인 스택뷰의 특정 서브뷰만 Horizontal 방식으로 바꾸고 싶을 때 새로운 스택 뷰를 만들어서 넣어줘야 하는 번거로움
📍 Closure
🔗 클로저란?
- 코드 안에서 전달되어 사용할 수 있는 로직을 가진 코드의 블럭
- 일급 객체
- 일급 객체는 전달 인자로 보낼 수 있고, 변수와 상수 등으로 저장하거나 전달할 수 있다. 함수의 반환 값이 될 수 있다.
- 참조 타입
🔗 특징
- 전역 함수는 이름이 있고 값을 캡처하지 않는 클로저
- 중첩 함수는 이름이 있고 둘러싸는 함수에서 값을 캡처할 수 있는 클로저
- 클로저 표현식은 주변 문맥에서 값을 캡처할 수 있는 가벼운 구문으로 작성된 명명되지 않은 클로저
🔗 클로저 축약
- 문맥에서 매개변수 및 반환 값 유형 유추
- 문맥을 보고 매개변수나 반환 타입을 생략할 수 있다.
- 단일 표현식 클로저의 암시적 반환
- 단축 인수 이름
- 인수의 이름을 따로 정하지 않고 $0, $1과 같은 형태로 사용가능.
- 후행 클로저
- 클로저의 표현식이 마지막인 경우 생략하고 { } 형태로 사용가능.
🔗 클로저의 값 캡쳐
- 클로저는 정의된 주변 컨텍스트에서 상수와 변수를 캡처할 수 있다.
- 캡쳐 된 상수와 변수를 정의한 원래 범위가 더 이상 존재하지 않더라도 본문 내에서 해당 상수와 변수의 값을 참조하고 수정할 수 있다.
- 클로저는 참조 타입이기 때문에 클로저가 참조타입을 캡쳐할 때 그 참조타입이 클로저를 참조한다면 강한 순환참조가 생겨서 메모리 릭이 발생한다.
🔗 클로저의 탈출
- 클로저를 매개 변수 중 하나로 사용하는 함수를 선언할 때 매개 변수의 유형 앞에 @escaping을 작성하여 클로저가 탈출할 수 있음을 나타낼 수 있다
- 클로저가 탈출할 수 있는 한 가지 방법은 함수 외부에 정의된 변수에 저장되는 것
- 비동기 처리에서 많이 사용된다.
- 그 이유는 함수가 종료 된 뒤에 실행되는 비동기 처리의 실행을 보장해야 하기 때문에
- self가 클래스의 인스턴스를 참조하는 경우 이스케이프 클로저에서 self를 캡처하면 강력한 참조 순환을 쉽게 만들 수 있다.
- 이스케이프 클로저는 self가 구조체나 열거형의 인스턴스일 때 self에 대한 변경 가능한 참조를 캡처할 수 없다.
🔗 자동클로저
-
함수에 인수로 전달되는 표현식을 래핑하기 위해 자동으로 생성되는 클로저
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
print("Now serving \(customerProvider())!")
print(customersInLine.count)
-
자동 클로저는 클로저를 호출할 때까지 내부 코드가 실행되지 않기 때문에 지연이 가능하다.
-
지연 평가는 코드 평가 시기를 제어할 수 있기 때문에 부작용이 있거나 계산 비용이 많이 드는 코드에 유용하다.