LoginSignup
3
1

More than 5 years have passed since last update.

[macOS][Swift] カスタムカラーをDarkModeに対応する(暫定版)

Last updated at Posted at 2018-07-03

MojaveおよびXcode10を持ってないので動作確認できてません

ソースコード in Gist

ダークモードに対応するために作った。
ついでに「アクセシビリティ」環境設定の「コントラストを上げる」にも対応した。

システムが提供する名前付きのNSColorはシステムが勝手にやってくれるので何もやらなくてもよい。
まずは適切な名前付きNSColorを用いるようにすべきである。

これは10.13及びそれより前のmacOSもターゲットに入れた方法である。
10.14以上のみをターゲットとする場合は Asset Catalog の Colors を用いるべきである。

富豪プログラマなので独自に定義したカスタムカラーにいかに簡単にアクセスするかを最上位の目的とした。
この方法を用いると描画パフォーマンスが低下する恐れがある。

使い方

draw(_:)内などで使用する。
モードの変更の通知がよくわからない。
モードが変わると NSWindow#display() などが呼び出されるようだ。


func draw(_ dirtyRect: NSRect) {

  // モードに準じた色が得られる
  let borderColor = ColorSetManager.current[.borderColor]
  borderColor.set()

  ...
}

登場する人

  • protocol
    • ColorSet 色が集まってる
    • SpecialColorSet スペシャルな色が集まってる
  • struct
    • ColorSetManager 神様。全部知ってる。 名前を ColorSetGod にするか悩んだ。
    • ColorName 色の名前が定義されてる。
    • BaseColorSet 通常時の色のセット。
    • そのたColorSet SpecialColorSet の具象化されたもの
  • enum
    • AppearanceMode あぴあらんす!

主要な人

ColorSet protocol

ColorNameからNSColorがもらえるsubscriptが宣言されてる。

protocol ColorSet {

    subscript(named: ColorName) -> NSColor { get }
}

ColorSetManager struct

現在の使用するべき ColorSet を知っている。

struct ColorSetManager {

  static var current: ColorSet { ... }
}

脇役の人

AppearanceMode enum

現在のモードを表す内部的なenum。
現在のモードも教えてくれる。

enum AppearanceMode {

    case normal

    case highContrast

    case dark

    case highContrastDark
}

extension AppearanceMode {

    static var current: AppearanceMode { ... }
}

ColorName struct

色の名前。 initは privateの方がイイかも
static let で色を追加していく。

struct ColorName {

    let name: String

    init(_ name: String) {

        self.name = name
    }
}

extension ColorName {

  static let asokoNoAnoBubun = ColorName("asokoNoAnoBubun")
}

裏方の人 (private)

BaseColorSet struct

inherit from ColorSet
基本となるカラーセット。
外部からは隠されている。

private struct BaseColorSet: ColorSet { ... }

SpecialColorSet protocol

inherit from ColorSet
特殊なモードでのカラーセット。
色の定義がない場合は BaseColorSet の色を返すように protocol extension が定義されている。
外部からは隠されている。

private protocol SpecialColorSet: ColorSet {

  func color(named: ColorName) -> NSColor?
}

private extension SpecialColorSet {

  subscript(named: ColorName) -> NSColor {

        return color(named: named) ?? BaseColorSet.shared[named]
    }
}

HighContrastColorSet struct

inherit from SpecialColorSet

DarkModeColorSet struct

inherit from SpecialColorSet

HighContrastDarkModeColorSet struct

inherit from SpecialColorSet

それぞれのモードでの具象型
外部からは隠されている。

現在のモードの識別

現在のモードの取得方法は10.14とそれ以前で変わっている。
前述通りMojaveおよびXcode10を持ってないので動くかどうかは不明。

extension AppearanceMode {

    static var current -> AppearanceMode {

        if #available(macOS 10.14, *) {

            return currentMode1014()
        }

        /// macOS 10.13までは NSWorkspaceから。DarkModeはない。

        if NSWorkspace.shared.accessibilityDisplayShouldIncreaseContrast {

            return .highContrast
        }

        return .normal
    }

    @available(macOS 10.14, *)
    private static func currentMode1014() -> AppearanceMode {

        // 10.14ではNSAppearance.current.nameだけを調べればいい
        switch NSAppearance.current.name {

        case .aqua: return .normal

            /// not available in macOS 10.13 SDK
//  macOS 10.14 SDKがないのでコンパイル不能。コメントアウト
//        case .accessibilityHighContrastAqua: return .highContrast
//
//        case .darkAqua: return .dark
//
//        case .accessibilityHighContrastDarkAqua: return .highContrastDark

        default: return .normal
        }
    }
}
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1