hcn1519 / TILMemo

블로그 초안 저장소
10 stars 1 forks source link

다크모드 #57

Closed hcn1519 closed 4 years ago

hcn1519 commented 4 years ago

다크모드 도입에 있어서 필요한 정보를 조사하면서 실제 구현과 관련된 내용을 자세하게 정리해보았습니다. 공식 문서 및 영상을 보면서 관련 내용을 같이 참고하시면 좋을 것 같습니다. 해당 내용의 대부분은 아래의 자료를 기반으로 작성되었습니다.

자료 목록

샘플 코드

내용 목차

1. UITraitCollection

2. 다크모드 주요 구현 대상

1. UITraitCollection

iOS 12에서는 다크모드가 지원되지 않는데, macOS의 다크모드가 지원되면서 API만 미리 추가된 것으로 보입니다.

dark1

dark2

UITraitCollection.current

class BackgroundView: UIView {
    override func draw(_ rect: CGRect) {
        // UIKit sets UITraitCollection.current to self.traitCollection
        UIColor.systemBackground.setFill()
        UIRectFill(rect) 
    }
}

dark3

class ViewController: UIViewController {
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        updateTitle()
    }
    private func updateTitle() {
        if #available(iOS 13.0, *) {
            guard traitCollection.userInterfaceStyle == .dark else {
                self.title = "라이트 모드"
                return
            }
            self.title = "다크 모드"
        } else {
            self.title = "라이트 모드"
        }
    }
}

dark4

let layer = CALayer()
let traitCollection = view.traitCollection

// Option 1 - resolvedColor를 통해 traitCollection 반영
let resolvedColor = UIColor.label.resolvedColor(with: traitCollection)
layer.borderColor = resolvedColor.cgColor

// Option 2 - performAsCurrent 클로저 활용
traitCollection.performAsCurrent {
    layer.borderColor = UIColor.label.cgColor
}

// Option 3 - 직접 current TraitCollection 업데이트
// 이 경우 UITraitCollection은 동작하는 Thread에서만 적용되어 다른 Thread에 영향을 주지 않습니다.
// 이 방식은 performAsCurrent의 내부 동작과 동일합니다.
let savedTraitCollection = UITraitCollection.current
UITraitCollection.current = traitCollection
layer.borderColor = UIColor.label.cgColor
UITraitCollection.current = savedTraitCollection

traitCollectionDidChange(_:)

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { 
    super.traitCollectionDidChange(previousTraitCollection)
    if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { 
        // Resolve dynamic colors again
    }
}

dark5

TraitCollection을 활용하여 라이트/다크 모드 강제 설정하기

dark6

2. 다크모드 주요 구현 대상

  1. 색상
  2. 이미지
  3. 기타 Components

색상

namedColor를 통한 지원

Use the Any Appearance variant to specify the color value to use on older systems that do not support Dark Mode.

dark7

dark8

let purColor = UIColor(named: "Pure")

System Color

let color: UIColor = UIColor.systemBlue
let labelColor: UIColor = UIColor.label

Resolved Color

// ViewController와 subView의 UITraitCollection.userInterfaceStyle에 따라서 값이 다름
let vcBGColor = UIColor.systemBackground.resolvedColor(with: viewController.traitCollection)
let subViewBGColor = UIColor.systemBackground.resolvedColor(with: subView.traitCollection)

Dynamic Provider

let myColor: UIColor = {
    if #available(iOS 13, *) {
        let color = UIColor(dynamicProvider: { traitCollection in
            if traitCollection.userInterfaceStyle == .dark {
                return UIColor.white
            } else {
                return UIColor.black
            }
        })
        return color
    } else {
        // 하위버전
        return UIColor.black
    }
}()

Note: dynamicProvider 생성자를 활용해서 생성된 UIColor는 Interface Builder에서 사용할 수 없습니다.

이미지

dark9

Template Image

dark92

let image = UIImage(named: "dessert")?.withRenderingMode(.alwaysTemplate)

Resolved Image

open class UIImageAsset : NSObject, NSSecureCoding {
    open func image(with traitCollection: UITraitCollection) -> UIImage
}

Symbol Image

기타 Components

1. StatusBar

public enum UIStatusBarStyle : Int {
    case `default` // Automatically chooses light or dark content based on the user interface style
    @available(iOS 7.0, *)
    case lightContent // Light content, for use on dark backgrounds
    @available(iOS 13.0, *)
    case darkContent // Dark content, for use on light backgrounds
}

2. UIActivityIndicatorView

UIActivityIndicatorView(style: .medium)
UIActivityIndicatorView(style: .large)

스크린샷 2020-03-05 오후 5 50 56

3. AttributedString

let attributes: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 36.0), 
    .foregroundColor: UIColor.label
]