안녕하세요, caution입니다. 오늘은 앱에 Dark Mode를 적용하는 방법에 대해 알아보려고 합니다 :)
Dark Mode
Dark Mode는 iOS 13.0
이상부터 앱에서 설정 가능한 화면 모드입니다. 이전 버전에서는 모두 Light Mode였다고 생각하시면 되요! 화면 모드는 설정 > 디스플레이 및 밝기
에서 변경할 수 있습니다.
사진에서 보이는 것처럼 간단하게 라이트 모드는 밝은 배경에 어두운 글씨, 다크 모드는 어두운 배경에 밝은 글씨로 설정되어 있다고 생각하시면 쉬워요.
HIG
본격적으로 개발에 다크모드를 적용하기 전 HIG를 읽어보고 오시는 걸 추천합니다 :). 헤헿
프로젝트에 다크모드 적용하기
Dark Mode를 적용하는 가장 쉬운 방법은 xib
와 Asset을 사용하는 거라고 생각합니다.
Xcode 11 버전부터는 storyboard
를 열었을 때 하단에 Interface Style
으로 라이트모드와 다크모드를 바꿔가면서 확인할 수 있기 때문에 Color
와 Image
모두 Asset을 잘 구성해두면 바로바로 확인할 수 있거든요.
하지만 코드로 화면을 구성하시는 분들도 많을 것 같아요. 그래서 먼저 Apple의 의도에 맞게 Asset Catalog를 사용해서 다크 모드에 대응하는 방법을 살펴보고 TraitCollection
을 사용해서 코드로 적용하는 방법을 살펴볼게요~!
Color
시스템 컬러
Apple에서는 기본적으로 Symantic Color
와 System Color
를 제공하고 있어요. 이 컬러들을 사용하면 크게 신경쓰지 않아도 라이트 모드, 다크 모드에 맞는 직역하자면 의미론적 색상인데요, 스토리보드에서 Color를 지정할 때 System Background Color
나, Table Background Color
등을 보신적이 있나요? 이렇게 미리 의도를 가지고 그 의미를 품은 이름을 가진 색상을 Symantic Color
라고 합니다.
Xcode 11에서 컬러를 지정해보셨다면 Symantic Color
들이 더 다양해진 걸 볼 수 있을 거에요.
System Background Color
, System Fill Color
, Placeholder Text Color
등등 다양한 Symantic Color
들을 살펴보고 싶으시다면 HIG의 Color-dynamic system colors를 살펴보면 좋을 것 같아요. 기회가 되면 다음에 정리해서 올릴게요! 오늘은 시간문제로 패스패스~!!
그래서 이 Symantic Color
들과 System Color
만을 사용해서 화면을 하나 구성해 볼게요.
온전하게 System에서 제공하는 컬러들만 사용된 화면이에요. Apple에서 제공되는 컬러들은 두 가지 모드에 모두 동일한 느낌을 줄 수 있는 컬러들이에요. 비슷해 보이지만 System Color
들은 두 모드에서 색상이 서로 다릅니다. 그리고 Symantic Color
들은 총 4가지 단계(primary, secondary, tertiary, quaternary)로 분류해서 컬러를 제공해주기 때문에 적절한 단계의 컬러들을 사용하면 앱의 가독성을 높일 수 있습니다.
사용자 컬러
하지만 이 시스템 컬러 외에도 디자이너 분들이 혹은 개발자분들이 원하는 컬러가 있을 수 있겠죠! 그 경우 Color Asset
에 Color를 저장해서 Named Color로 스토리보드에서 바로 사용할 수 있습니다.
Named Color? Xcode 9 이상부터 Asset에 컬러를 지정하고 이름을 지정하면 시스템 컬러처럼 스토리보드에서 가져다 쓸 수 있습니다. 이를 Named Color라고 합니다. iOS 11.0+ 을 지원합니다.
Asset을 열고 + 버튼을 눌러 New Color Set
을 선택합시다. 그리고 나서 attributes inspector를 열고
Appearance
항목에서 Any, Light, Dark
를 선택하면 다음 화면처럼 세 가지 컬러를 선택할 수 있습니다.
이미지는 Apple 아티클인 Supporting Dark Mode in your Interface에서 퍼왔습니다.
각각 라이트 모드와 다크 모드에 대한 컬러를 선택하면 이 컬러가 지정됐을 때 그 모드에 따라 컬러값이 변경됩니다.
그렇다면 Any
는 무엇일까요? 다크모드의 개념이 사용되기 이전 버전에 대한 컬러를 별도로 지정할 경우 Any
를 사용하면 됩니다. 만약 라이트 모드와 이전버전의 컬러를 똑같이 사용할 거라면 Any, Dark
만 선택해도 됩니다.
세 가지 컬러를 선택해 볼게요. Any
에는 빨간색, Light
에서는 초록색, Dark
에서는 파란색으로 선택해볼게요. ~매우 극단적인 개발자의 컬러감각~
화면의 배경색으로 이 컬러를 선택해주고 앱을 실행하면?
우리가 원하던 대로 라이트 모드와 다크 모드, 그리고 13 이전 버전에서는 Any Appearance에 선택된 빨간색으로 나오는 걸 볼 수 있어요.
자, 개발자라면 이쯤되서 궁금한 점이 하나 나타나야 합니다. 화면 중앙에 나타난 레이블에 텍스트를 어떻게 셋팅해준 걸까요?
컬러의 경우에는 Named Color
로 미리 설정해주었지만, 레이블에 들어가는 텍스트는 어떻게 지정된 걸까요?
이건 이미지까지 한 번 다시 살펴보고 코드로 DarkMode에 적용하는 법을 살펴보면서 알려드릴게요~!
이미지
이미지는 컬러와 마찬가지로 Asset Catalog를 사용하면 매우 간편합니다. 마찬가지로 Appearance
에 맞는 이미지들만 셋팅해주면 스토리보드에서도 런타임에서도 화면 모드에 맞는 이미지를 가져다 쓰게 됩니다.
위의 컬러 셋팅과 동일한 이야기여서 별도 예를 들지는 않을게요!
SF Symbols
하지만 라이트모드 / 다크모드 모두 이미지를 만드는 게 부담되는 디자이너/개발자 분들을 위해서 Apple에서 SF Symbols를 제공해주고 있어요.
SF-Symbols 앱을 설치하면 Xcode에서 SF-Symbols에 포함된 이미지들을 가져다 쓸 수 있습니다. 단, iOS 13버전 이상에서만 사용할 수 있습니다.
주의하세요!
모든 SF 기호는 Xcode 및 Apple SDK 라이센스 계약에 정의된 시스템 제공 이미지로 간주되며 여기에 명시된 조건에 따릅니다. 앱 아이콘, 로고 또는 상표 관련 사용에는 SF 기호를 사용할 수 없습니다.
스토리보드에서 SF-Symbol 사용하기
iOS 13 이전버전에서는 이미지가 nil로 셋팅되니까 조심하세요!
코드로 SF-Symbol 사용하기
if #available(iOS 13.0, *) {
let upImageView = UIImageView()
upImageView.image = UIImage(systemName: "square.and.arrow.up")
}
코드로 다크모드 대응하기
이제부터는 코드로 다크모드에 대응하는 방법을 알아볼 거에요. 코드로 화면을 구성하고 계셨다면 방식은 이전에 코드를 작성하는 방식 그대로 사용하시면 됩니다. 하지만 이미지나 색상들을 선택할 때 필요한 몇 가지 요소를 알려드릴게요.
UITraitCollection
이 클래스를 아시나요? iOS 8.0에 처음 나타난 개념이고, Apple 문서에서는 UITraitCollection
을 다음과 같이 정의합니다.
수평 및 수직 크기 클래스, 디스플레이 척도 및 사용자 인터페이스 관용어와 같은 특성으로 정의된 앱용 iOS 인터페이스 환경
간단히 말하자면 이 앱이 설치된 환경의 수평크기, 수직크기는 얼마이고, 아이패드인지 아이폰인지, 라이트모드인지 다크모드인지 등등 환경적 특성들을 가지고 있는 클래스입니다.
그리고 이 UITraitCollection
은 이미 UIViewController
와 UIView
내부에 traitCollection
이라는 property로 포함되어 있습니다.
// UIViewController의 traitCollection
self.traitCollection
// UIViewController의 view의 traitCollection
self.view.traitCollection
그리고 디스플레이모드에 대한 정보는 traitCollection
의 userInterfaceStyle
property에서 가져올 수 있습니다. 이 userInterfaceStyle
은 light
, dark
, unspecific
세 가지 케이스를 가지는 enum
class UIUserInterfaceStyle의 인스턴스입니다. 그래서 traitCollection
으로 다크모드인지 라이트모드인지 판별해서 알맞은 Asset을 설정해주면 됩니다.
traitCollectionDidChange
앱을 사용하는 도중에도 사용자는 시스템 디스플레이 타입을 변경할 수 있습니다. 이때 traitCollectionDidChange(:)
가 호출됩니다.
마찬가지로 UIView
와 UIViewController
모두 이 메서드를 가지고 있으며, previousTraitCollection
을 매개변수로 넘겨받기 때문에 다음과 같이 사용해볼 수 있습니다.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
}
Asset으로 리소스를 관리하고 있는 경우
간단합니다! 앞서 보셨던 방식 그대로 사용하시면 됩니다. Named Color
나 #imageliteral(:)
을 사용하고 계셨다면 Asset에서 다크모드용 리소스만 셋팅해주시고 사용하는 방식은 동일합니다.
let image = UIImage(named: "hello")
self.imageView.image = image