UIKit

Joker.·2025년 6월 12일
0

UIKit 개요 및 앱 생명주기, 이벤트 전달 흐름 정리


1. 🧱 UIKit 개요 및 앱 실행 과정

📌 앱 실행 순서

  1. main() 실행
  2. UIApplicationMain() 호출 → iOS 앱의 런타임 진입점
  3. UIApplication 객체 생성 (앱의 본체)
  4. Storyboard, nib, Info.plist 등 UI 관련 리소스 로드
  5. AppDelegate 객체 생성 및 RunLoop 준비
  6. application(_:didFinishLaunchingWithOptions:) 호출
  7. SceneDelegate 통해 UI 표시

앱 시작 시 AppDelegate, SceneDelegate, RunLoop가 함께 구성된다.


2. 🧾 Info.plist 설정 & Scene 구성

✅ 필수 Scene 설정 항목 (Info.plist)

  • Application Scene Manifest
    • Enable Multiple Windows = NO
    • Delegate Class Name = $(PRODUCT_MODULE_NAME).SceneDelegate

❌ Storyboard 제거 시 주의사항

  • Main Interface 항목 제거 필요 (Build Settings → Info.plist Values)
  • 제거하지 않으면 다음 오류 발생: Could not find a storyboard named 'Main'

3. 🔄 Main Run Loop

  • 이벤트 처리 루프 역할 수행
  • 사용자 터치, 알림 등 외부 입력 발생 시 메인 RunLoop가 이를 처리
  • UI 업데이트도 RunLoop에서 수행됨

❗ AppDelegate에서 과도한 초기화 작업은 지양 (UI 표시 전 blocking 위험)


4. 🧭 SceneDelegate

📌 역할

  • iOS 13+에서 도입된 UI 생명주기 관리 객체
  • AppDelegate → 프로세스 전반 생명주기 관리
  • SceneDelegate → UI 단위 생명주기 관리

주요 메서드 예시

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)

📌 특징 (SceneDelegate)

  • 하나의 앱에서 여러 Scene을 가질 수 있음 (예: iPad 멀티 윈도우)
  • UIWindowScene: UIScene의 서브클래스로, 하나 이상의 UIWindow를 관리
  • 직접 인스턴스화할 수 없음
  • Info.plist와 AppDelegate 설정을 통해 연결됨

5. 🔁 App Life Cycle (앱 생명주기)

상태설명
Not Running앱이 실행되지 않거나 종료된 상태
Inactive실행 중이지만 이벤트는 받지 않음 (예: 재난문자)
Active앱이 포그라운드에서 정상 작동 중
Background백그라운드에서 작업 중 (예: 음악 재생)
Suspended백그라운드에서 멈춘 상태 (메모리 해제 대상)

🎯 상태 변화 대응 메서드

func sceneDidBecomeActive(_ scene: UIScene)
func sceneWillResignActive(_ scene: UIScene)

6. 🧩 UIViewController Life Cycle

✅ 주요 메서드와 호출 시점

메서드설명
loadView()뷰 객체를 생성할 때 호출 (직접 구현할 일 드묾)
viewDidLoad()뷰가 메모리에 올라온 직후 1회만 호출됨
viewWillAppear()뷰가 화면에 나타나기 직전, 매번 호출됨
viewDidAppear()뷰가 화면에 완전히 나타난 직후
viewWillDisappear()뷰가 화면에서 사라지기 직전
viewDidDisappear()뷰가 완전히 사라진 직후

일반적으로는 viewDidLoad, viewWillAppear, viewDidAppear를 주로 사용.

🔍 실습용 로그 출력 예시

override func viewDidLoad() {
    super.viewDidLoad()
    print("🔥 viewDidLoad 호출됨")
}

7. 📡 UIResponder & 이벤트 전달

  • 이벤트를 가장 먼저 처리할 수 있는 객체가 해당 이벤트에 "응답"한다.
  • 만약 처리가 불가능하다면, nextResponder를 따라 다음 responder로 전달된다.

📌 이벤트 전달 흐름 예시

UILabel → UIView → UIViewController → UIWindow → UIApplication → AppDelegate
UILabel이 터치 이벤트를 처리하지 않으면 → superview인 UIView로 전달

그 후 ViewController → UIWindow → UIApplication 순으로 올라간다


8. 👆 Hit Test

hitTest(_:with:) 메서드란?

특정 지점(Point)에 대한 터치 이벤트가 어떤 뷰(View)에 닿았는지를 판별하는 메서드이다.

터치 위치에 가장 가까운(깊은 계층의) 뷰가 이벤트에 응답하게 됨

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // 이벤트를 받을 View를 결정
    return super.hitTest(point, with: event)
}

📌 이벤트 전달 조건

조건설명
isHidden == false뷰가 화면에 보여야 함
alpha > 0.01뷰가 충분히 불투명해야 함
isUserInteractionEnabled == true사용자 인터랙션이 가능해야 함

🧪 실습 팁

  • 위 조건들을 변경해가며 어떤 뷰가 이벤트를 받는지 실험해보기

  • View 계층 구조를 따라 이벤트가 어떻게 전달되는지 로그로 추적해보기


🧪 One More Thing

다음과 같은 실습을 통해 개념을 실제로 확인해보자.


SceneDelegate 생명주기 확인

func sceneDidBecomeActive(_ scene: UIScene) {
    print("🌞 Scene이 Active 상태로 진입")
}

func sceneWillResignActive(_ scene: UIScene) {
    print("🌙 Scene이 Inactive 상태로 전환")
}

UIViewController 생명주기 로그 확인

override func viewDidLoad() {
    super.viewDidLoad()
    print("📌 viewDidLoad 호출됨")
}

override func viewWillAppear(_ animated: Bool) {
    print("📌 viewWillAppear 호출됨")
}

hitTest 커스터마이징 실습

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    print("📍 \(self)의 hitTest 실행")
    return super.hitTest(point, with: event)
}

오늘의 느낀 점

  • 세상에는 배울 것은 넘쳐나고, 시간이 부족하다. 많이 늦었지만, 지금부터 해도 늦지 않다고 생각하고
    굳게 마음 먹고 칼을 뽑았으니, 무라도 베어보자는 마음으로 끝까지 노력해서 누구에게도 뒤쳐지지 않고,
    더 앞서나가는 내가 되어야겠다고 생각을 한 오늘이다. 열심히 하니깐 끝나고나니 뿌듯하다 !

profile
🃏

0개의 댓글