
오늘은 이런 뷰를 만들면서, 라이트모드와 다크모드를 적용시키는 작업을 진행했다.
// 라이트모드/다크모드 적용
private func updateMode(_ mode: ThemeMode) {
switch mode {
case .light:
UIApplication.shared.windows.forEach { window in
window.overrideUserInterfaceStyle = .light
}
updateCheckMaker(lightModeButton)
case .dark:
UIApplication.shared.windows.forEach { window in
window.overrideUserInterfaceStyle = .dark
}
updateCheckMaker(darkModeButton)
}
}
라이트모드와 다크모드 적용을 하기 위해 ViewController에 이런 메서드를 작성했는데,
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead 이런 경고가 떴다.
무슨 말이냐 하면,
iOS 15 버전부터 멀티 윈도우 지원이 확장되면서 특정 UIWindowScene에 속한 윈도우를 명시적으로 다루는 것이 중요해졌기 때문에, UIApplication.shared.windows는 iOS 15부터 사용이 권장되지 않는다고 한다.
UIApplication.shared.windows는 앱의 UI 계층을 직접 탐색하고 제어하는 데 사용되었던 방식으로, 앱에서 현재 활성화된 모든 UIWindow 객체를 배열로 반환하는 속성이다.
과거 iOS에는 Scene 개념이 없었기 때문에, 앱 내의 모든 윈도우를 단순히 가져오는 방식으로 UI를 제어할 수 있었다고 한다.
// 라이트모드/다크모드 적용
private func updateMode(_ mode: ThemeMode) {
switch mode {
case .light:
// iOS 15 이상에서 권장되는 방식
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.forEach { windowScene in
windowScene.windows.forEach { window in
window.overrideUserInterfaceStyle = .light
}
}
updateCheckMaker(lightModeButton)
case .dark:
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.forEach { windowScene in
windowScene.windows.forEach { window in
window.overrideUserInterfaceStyle = .dark
}
}
updateCheckMaker(darkModeButton)
}
}
따라서 코드를 위와 같이 수정하였다.
UIApplication.shared.windows를 사용해 앱의 모든 윈도우를 가져왔지만, iOS 15 이후에는 활성화된 특정 Scene(UIWindowScene)을 명시적으로 다루는 방식이 요구된다UIWindowScene 객체만 추출한다.connectedScenes는 UIScene 타입의 집합(Set)이기 때문에 UIWindowScene(윈도우와 연결된 Scene)을 추출하는 것이다.compactMap으로 connectedScenes에서 UIWindowScene으로 변환 가능한 객체만 추출한다.CarPlay ExternalDisplay같은 다른 타입의 장면은 제외하고, 작업하려는 윈도우와 관련된 Scene만 필터링 하는 것이다..forEach로 각 UIWindowScene을 순회하면서, 각 Scene에 속한 윈도우들을 가져온다.windowScene.windows는 해당 Scene의 모든 UIWindow 객체를 배열로 반환하는 것이다..forEach로 개별 UIWindow 객체를 순회한다.UIWindow에는 overrideUserInterfaceStyle이라는 속성이 있다. 이 속성을 통해 윈도우의 사용자 인터페이스 스타일(라이트/다크 모드)을 강제로 지정할 수 있다.UIWindow의 overrideUserInterfaceStyle 속성을 통해 강제로 각각 .dark 또는 .light를 적용한 것이다..unspecified 를 사용할 수도 있다.위 메서드에서, 명시적으로 Scene을 구분하거나 특정 Scene에 대해 다른 처리를 수행하는 로직 없이 모든 활성화 된 Scene의 UIWindowScene을 추출 후 그 안에 포함된 모든 UIWindow에 대해 스타일을 적용하고 있다.
코드의 목적은 앱의 모든 활성 Scene과 그 Scene에 속한 UIWindow 객체에 동일한 스타일(다크 모드/라이트 모드)을 적용하는 것이기 때문이다.
만약 각 Scene의 구분이 필요하다면 특정 Scene을 다루기 위한 조건문이나 필터링 로직이 추가로 필요할 것이다.
// 예시
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.session.configuration.name == "MainScene" } // 특정 Scene 필터링
.forEach { windowScene in
windowScene.windows.forEach { window in
window.overrideUserInterfaceStyle = .light
}
}
멀티 Scene을 고려해야 하는 경우는 주로
의 경우들이다.
초급 단계의 앱 개발에서, iPhone 앱은 대부분 단일 Scene에 단일 UIWindow를 사용하기도 하고, iPhone에서는 기본적으로 멀티 Scene 기능을 사용하지 않으므로, 단일 Scene과 UIWindow만 처리하면 충분하다.
현재까지 다뤄본 각종 UI들은 루트 UIView에 속하고, 이 루트 UIView는 UIViewController가 관리하는 형태였기 때문에, 위의 updateMode 메서드의 forEach문은 한번만 실행 될 것이다.
다만 iOS 15 버전 이상에서 권장하는 방식이기도 하고, 언제까지고 초급 단계에만 머물러 있을 것은 아니기 때문에 이번 기회에 알고 넘어가면 좋다고 생각한다.
참고