iOS 13でダークモードをオプトアウトすることはできますか?


295

私のアプリの大部分は、ネイティブ実装ではまだ利用できない機能を提供するWebビューで構成されています。Webチームは、Webサイトにダークテーマを実装する予定はありません。そのため、私のアプリは、iOS 13のダークモードをサポートすることで少し半分に見えます。

ダークモードのサポートをオプトアウトして、アプリが常にライトモードを表示してWebサイトのテーマに一致するようにすることはできますか?


70
Info.Plistでに設定UIUserInterfaceStyleLightます。developer.apple.com/library/archive/documentation/General/…を
Tieme

1
お問い合わせいただきありがとうございます。通過する多くのアプリ。これは、トグルの準備ができるまでアプリを動作させ続けるために必要です。
user3741598

import FoundationインポートUIKit拡張UIViewController {open func awakeFromNib()をオーバーライドします{super.awakeFromNib()if #available(iOS 13.0、*){//常にライトインターフェイススタイルを採用します。overrideUserInterfaceStyle = .light}}}
Mohammad Razipour

1
UIUserInterfaceStyleをplistに追加するだけです。
とても

アプリをappstoreに送信している間、ライトモードのUIUserInterfaceStyleが原因でアップルが受け入れます。
キラン

回答:


683

まず、ダークモードのオプトアウトに関するAppleのエントリです。 このリンクのコンテンツは、Xcode 11およびiOS 13向けに記述されています

このセクションはXcode 11の使用に適用されます


アプリケーション全体をオプトアウトしたい場合

アプローチ#1

info.plistファイルで次のキーを使用します。

UIUserInterfaceStyle

そして、それに値を割り当てますLight

XMLのためのUIUserInterfaceStyle割り当て:

<key>UIUserInterfaceStyle</key>
<string>Light</string>

アプローチ#2

overrideUserInterfaceStyleアプリのwindow変数に対して設定できます。

プロジェクトの作成方法に応じて、これはAppDelegateファイルまたはにありSceneDelegateます。

if #available(iOS 13.0, *) {
    window?.overrideUserInterfaceStyle = .light
}


UIViewControllerを個別にオプトアウトしたい場合

override func viewDidLoad() {
    super.viewDidLoad()
    // overrideUserInterfaceStyle is available with iOS 13
    if #available(iOS 13.0, *) {
        // Always adopt a light interface style.
        overrideUserInterfaceStyle = .light
    }
}

overrideUserInterfaceStyleに関するAppleのドキュメント

上記のコードがXcode 11でどのように見えるか:

ここに画像の説明を入力してください

このセクションはXcode 10.xの使用に適用されます


提出にXcode 11を使用している場合は、この行の下のすべてを安全に無視できます。

関連するAPIはiOS 12には存在しないため、上記の値を使用しようとするとエラーが発生します。

設定のためのoverrideUserInterfaceStyleあなたにUIViewController

ここに画像の説明を入力してください

UIViewControllerを個別にオプトアウトしたい場合

これは、コンパイラバージョンとiOSバージョンをテストすることにより、Xcode 10で処理できます。

#if compiler(>=5.1)
if #available(iOS 13.0, *) {
    // Always adopt a light interface style.
    overrideUserInterfaceStyle = .light
}
#endif

アプリケーション全体をオプトアウトしたい場合

上記のスニペットを変更して、Xcode 10のアプリケーション全体に対して機能するようにするには、AppDelegateファイルに次のコードを追加します。

#if compiler(>=5.1)
if #available(iOS 13.0, *) {
    // Always adopt a light interface style.
    window?.overrideUserInterfaceStyle = .light
}
#endif

ただし、Xcodeバージョン10.xを使用すると、plist設定は失敗します。

ここに画像の説明を入力してください

功績@Aronネルソン@Raimundas Sakalauskas@NSLeaderrmaddy彼らのフィードバックと、この答えを改善するため。


2
アプリを今すぐ更新/アップロードすると、UIUserInterfaceStyleライトがブロックされます。無効なplistエントリとしてフラグが付けられます。(無効なplistキー)
Aron Nelson

2
これはiOS SDK 12(現在最新の安定したSDK)に対してコンパイルされません。iOS 12 SDKでも動作するソリューションについては、stackoverflow.com / a / 57521901/2249485を参照してください。
Raimundas Sakalauskas

これは不公平なので、「元の質問」よりもはるかに多くの見解を持つ質問は、回答を提供するためにロックされています。:(
Raimundas Sakalauskas

7
すべてのビューコントローラーで設定overrideUserInterfaceStyleする代わりにviewDidLoad、アプリのメインウィンドウで一度設定できます。アプリ全体を一方向に動作させたい場合は、はるかに簡単です。
rmaddy

2
使用する#if compiler(>=5.1)代わりに、responds(to:)setValue
NSLeader

162

「iOSの実装ダークモード」でのAppleのセッションによると(https://developer.apple.com/videos/play/wwdc2019/214/は 31:13から始まる)には、設定することが可能であるoverrideUserInterfaceStyleUIUserInterfaceStyleLightまたはUIUserInterfaceStyleDark任意のビューコントローラやビューに、traitCollectionサブビューまたはビューコントローラので使用されます。

SeanRですでに述べたように、アプリのplistファイルに、またはアプリのplistファイルで設定UIUserInterfaceStyleして、アプリ全体でこれを変更できます。LightDark


17
UIUserInterfaceStyleキーを設定すると、アプリはApp Storeで拒否されます
Sonius

2
AppleはITMS-90190エラーコードforums.developer.apple.com/thread/121028で
PRASAD1240

11
iOS 13 SDKはまだベータ版ではないため、拒否が発生する可能性が最も高くなります。Xcode 11 GMが利用可能になり次第、これは機能するはずです。
dorbeetle

2
@dorbeetleそれは本当ではありません、私はこのキーでアプリを1か月前のようにXcode 10で正常にアップロードしました。拒否は最近起こりました。ある種の新しいアップル戦略のようです。
スティーブン

1
それはまだ起こっています。Xcode GM2がアプリ署名エラーを返しました。Xcode 10.3が返しました:「無効なInfo.plistキー。Payload/ Galileo.appInfo.plistファイルのキー 'UIUserInterfaceStyle'は無効です。」
Evgen Bodunov

64

Xcode 11以降(つまり、iOS 13以降のSDK)を使用していない場合、アプリはダークモードのサポートを自動的に選択していません。したがって、ダークモードをオプトアウトする必要はありません。

Xcode 11以降を使用している場合、システムはアプリのダークモードを自動的に有効にします。好みに応じて、ダークモードを無効にする2つの方法があります。完全に無効にすることも、特定のウィンドウ、ビュー、またはビューコントローラーに対して無効にすることもできます。

アプリのダークモードを完全に無効にする

アプリのInfo.plistファイルのUIUserInterfaceStyleようLightに、値を含むキーを含めることで、ダークモードを無効にできます。 これはユーザーの設定を無視し、常にアプリに明るい外観を適用します。
ライトとしてのUIUserInterfaceStyle

ウィンドウ、ビュー、またはビューコントローラーのダークモードを無効にする

overrideUserInterfaceStyle適切なウィンドウ、ビュー、またはビューコントローラーのプロパティを設定することで、インターフェイスを常に明るいスタイルまたは暗いスタイルで表示させることができます。

ビューコントローラ:

override func viewDidLoad() {
    super.viewDidLoad()
    /* view controller’s views and child view controllers 
     always adopt a light interface style. */
    overrideUserInterfaceStyle = .light
}

ビュー:

// The view and all of its subviews always adopt light style.
youView.overrideUserInterfaceStyle = .light

窓:

/* Everything in the window adopts the style, 
 including the root view controller and all presentation controllers that 
 display content in that window.*/
window.overrideUserInterfaceStyle = .light

注:Appleでは、アプリでダークモードをサポートすることを強くお勧めしています。そのため、ダークモードは一時的にのみ無効にできます。

詳細はこちら:iOSアプリの特定のインターフェイススタイルの選択


34

********** Xcode 11以降の最も簡単な方法***********

これを前にinfo.plistに追加します </dict></plist>

<key>UIUserInterfaceStyle</key>
<string>Light</string>

Xcode 10.xでアプリを送信すると、このソリューションは失敗します
Tawfik Bouabid

27

私は解決策を見つけたと思います。最初はUIUserInterfaceStyle-Information Property ListUIUserInterfaceStyle-UIKitから組み合わせましたが、iOSアプリの特定のインターフェイススタイルの選択で実際に文書化されていることがわかりました。

info.plistUIUserInterfaceStyleUser Interface Style)を1UIUserInterfaceStyle.light)に設定します。

編集:ドービートルの答えによると、のより適切な設定はUIUserInterfaceStyleかもしれませんLight


2に値を設定することで、暗いモードを強制することはかかわらず、動作しません:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen

3
このキーをplistに含めると、App Storeが拒否されます。
ホセ・

1
AppStoreは、plist.infoでこのプロパティを拒否しなくなりました。私たちのアプリはすでに暗いので、「暗い」(大文字)を入れました。問題はありません。これにより、システムコントロールを適切に使用できます。
nickdnk

@nickdnkアップルが推奨するXcode 11でアプリを作成したと思います。
DawnSong

1
はい、そうしました。Appleがこのパラメーターをplistで受け入れているという事実は変わりません。これは、私が明確にしようとしていたことです。
nickdnk

23

上記の答えは、アプリ全体をオプトアウトしたい場合に有効です。UIを備えたlibで作業していて、.plistを編集する余裕がない場合は、コードを使用して行うこともできます。

iOS 13 SDKに対してコンパイルする場合は、次のコードを使用するだけです。

迅速:

if #available(iOS 13.0, *) {
    self.overrideUserInterfaceStyle = .light
}

Obj-C:

if (@available(iOS 13.0, *)) {
    self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}

しかしながら、あなたのコードが反対コンパイルしたい場合は、iOSの12 SDKあまりにも(今はまだ最新の安定SDKである)、あなたは、セレクタを使用してに頼る必要があります。セレクター付きのコード:

Swift(XCodeはこのコードの警告を表示しますが、SDK 12にプロパティが存在しないためコンパイルできないため、これが現時点での唯一の方法です):

if #available(iOS 13.0, *) {
    if self.responds(to: Selector("overrideUserInterfaceStyle")) {
        self.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
    }
}

Obj-C:

if (@available(iOS 13.0, *)) {
    if ([self respondsToSelector:NSSelectorFromString(@"overrideUserInterfaceStyle")]) {
        [self setValue:@(UIUserInterfaceStyleLight) forKey:@"overrideUserInterfaceStyle"];
    }
}

プロパティが何にoverrideUserInterfaceStyle属しているかを指定した方が良いでしょう。
DawnSong

12

最新アップデート-

Xcode 10.xを使用している場合、デフォルトUIUserInterfaceStylelight iOS 13.xです。iOS 13デバイスで実行すると、ライトモードでのみ動作します。

UIUserInterfaceStyleInfo.plistファイルにキーを明示的に追加する必要はありません。キーを追加すると、アプリを検証するときにエラーが発生します。

無効なInfo.plistキーです。Payload / AppName.appInfo.plistファイルのキー「UIUserInterfaceStyle」は無効です。

UIUserInterfaceStyleXcode 11.xを使用する場合は、Info.plistファイルにのみキーを追加してください。


1
これはXcode 10または11とは関係ありません。ユーザーがXcode 10のアプリフォームを展開し、ダークモードを処理しない場合、アプリをiPhone 11、Pro、またはPro Maxにインストールすると、ダークモードの問題が発生します。Xcode 11にアップデートして、この問題に対処する必要があります。
Niranjan Molkeri

3
@NiranjanMolkeriこれは新しいiPhoneとは関係ありません。これはiOS 13のダークモードに関するものです。以前のiOS 13ベータ版のアプリUIでは、明示的に処理しないとダークモードの問題がありました。しかし、最新バージョンでは修正されています。XCode 10を使用している場合、iOS13のデフォルトのUIUserInterfaceStyleはライトです。Xode11を使用している場合は、それを処理する必要があります。
kumarsiddharth123

Xcode 10.3を使用してアプリをTestFligthにアップロードし、plistにキーUIUserInterfaceStyleが含まれている場合、問題が発生します。無効なplistファイルであると表示されます。Xcode 10でビルドする場合は削除するか、Xcode 11を使用してアップロードする必要があります
eharo2

9

UIUserInterfaceStyleキーをplistファイルに追加すると、Appleはリリースビルドを拒否する可能性があります。https//stackoverflow.com/a/56546554/7524146 とにかく、各ViewControllerを明示的に伝えるのは面倒ですself.overrideUserInterfaceStyle = .light。ただし、この平和なコードをルートwindowオブジェクトに一度使用できます。

if #available(iOS 13.0, *) {
    if window.responds(to: Selector(("overrideUserInterfaceStyle"))) {
        window.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
    }
}

application(application: didFinishLaunchingWithOptions:)このセレクターはtrueその初期段階では応答しないため、内部ではこれを実行できないことに注意してください。しかし、後でそれを行うことができます。AppDelegateでUIを自動的に開始する代わりに、アプリでカスタムAppPresenterまたはAppRouterクラスを使用している場合は、非常に簡単です。


9

あなたは変えることができますダークモードを Xcodeの11でアプリケーション全体でオフ:

  1. Info.plistに行く
  2. 次のように追加します

    <key>UIUserInterfaceStyle</key>
    <string>Light</string>

Info.plistは以下のようになります...

ここに画像の説明を入力してください


1
Xcodeバージョン11.3.1(11C504)では、何らかの理由で動作しません
Andrew

7

-アプリ全体(ウィンドウ):

window!.overrideUserInterfaceStyle = .light

あなたはから窓を得ることができます SceneDelegate

-単一のViewControllerの場合:

viewController.overrideUserInterfaceStyle = .light

あなたはviewController、viewControllerの中でさえ、任意のものを設定することができます自己

-単一のビューの場合:

view.overrideUserInterfaceStyle = .light

viewビュー内でも任意に設定できます自体を

if #available(iOS 13.0, *) { ,,, }以前のiOSバージョンをサポートしている場合は、使用する必要がある場合があります。


6

他の応答とは別に、以下についての私の理解から、(XCode 11を使用して)iOS 13 SDKに対してコンパイルする場合は、ダークモードの準備だけが必要です。

システムは、iOS 13以降のSDKにリンクされたアプリが明るい外観と暗い外観の両方をサポートしていることを前提としています。iOSでは、ウィンドウ、ビュー、またはビューコントローラーに特定のインターフェイススタイルを割り当てることで、必要な特定の外観を指定します。Info.plistキーを使用して、ダークモードのサポートを完全に無効にすることもできます。

リンク


2

はい、viewDidLoadに次のコードを追加することでスキップできます。

if #available(iOS 13.0, *) {
        // Always adopt a light interface style.
        overrideUserInterfaceStyle = .light
    }

2

私のアプリは現在のところダークモードをサポートしておらず、明るいアプリバーの色を使用しています。次のキーをmyに追加することで、ステータスバーのコンテンツを暗いテキストとアイコンに強制できましたInfo.plist

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDarkContent</string>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>

ここで他の可能な値を見つけてくださいhttps : //developer.apple.com/documentation/uikit/uistatusbarstyle


2

Objective-Cバージョン

 if (@available(iOS 13.0, *)) {
        _window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
    }

1

ダークモードをサポートまたはバイパスするためにアプリで使用できるヒントとコツをいくつか紹介します。

最初のヒント:ViewControllerスタイルをオーバーライドするには

あなたはUIViewControllerのインターフェーススタイルをオーバーライドすることができます

1:overrideUserInterfaceStyle = .dark //ダークモードの場合

2:overrideUserInterfaceStyle = .light //ライトモードの場合

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        overrideUserInterfaceStyle = .light    
    }
}

2番目のヒント:info.plistにキーを追加する

単にあなたは新しいキーを追加することができます

UIUserInterfaceStyle

アプリのinfo.plistで、その値をLightまたはDarkに設定します。これにより、アプリのデフォルトスタイルが指定した値に上書きされます。すべてのviewControllerにoverrideUserInterfaceStyle = .lightこの行を追加する必要はありません。info.plistの1行だけです。



1
 if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light
        } else {
            // Fallback on earlier versions
        }

コードのみの回答を投稿するのではなく、この回答が問題をどのように解決するかを少し説明できますか。
アルンヴィノス

確かに@ArunVinoth IOS 13ではダークモードが導入されているため、展開ターゲットが13未満の場合は上記のコードを使用します。それ以外の場合は、ifブロックで記述された簡単なステートメントを使用できます。
Talha Rasool

1

スウィフト5

ダークモードからライトモードに切り替える2つの方法:

1- info.plist

    <key>UIUserInterfaceStyle</key>
    <string>Light</string>

2-プログラム的に

 UIApplication.shared.windows.forEach { window in
     window.overrideUserInterfaceStyle = .light
  } 

0

アプリのライフサイクル中にウィンドウプロパティが変更される可能性があるため、このソリューションを使用します。したがって、「overrideUserInterfaceStyle = .light」の割り当てを繰り返す必要があります。UIWindow.appearance()を使用すると、新しく作成されたUIWindowオブジェクトに使用されるデフォルト値を設定できます。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

      if #available(iOS 13.0, *) {
          UIWindow.appearance().overrideUserInterfaceStyle = .light
      }

      return true
    }
}

0

次の行をinfo.plistファイルに追加するだけです。

<key>UIUserInterfaceStyle</key>
<string>light</string>

これにより、アプリは強制的にライトモードでのみ実行されます。


これはすでにコメントされ、何度も答えられました。受け入れられた答えでさえこれを示唆しています。したがって、このコメントは新しい情報を追加しません。
JeroenJK

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
他の人がそれから学ぶことができるように、それを編集してあなたの回答にいくつかの説明を追加してください
ニコ・ハーゼ

0

この新しいキーUIUserInterfaceStyleをInfo.plistに追加し、その値をLightに設定します。アラートコントローラがライトモードで表示されることを確認します。

UIUserInterfaceStyle Light Info.plistファイルにキーUIUserInterfaceStyleを追加し、その値をLightまたはDarkに設定することにより、ユーザーの設定に関係なく、アプリケーション全体で強制的にライト/ダークモードになっている場合。


0

この質問には多くの答えがあります。 info.plist次のAppDelegateように設定できます。

#if compiler(>=5.1)
        if #available(iOS 13.0, *) {
            self.window?.overrideUserInterfaceStyle = .light
        }
        #endif

Xcode 11.3、iOS 13.3でテストする


-8

実際、私はアプリケーションのすべてのviwコントローラーをputzすることなく、コードのダークモードをグローバルにオプトアウトできるようにするコードをいくつか書いたところです。これはおそらく、クラスのリストを管理することにより、クラスごとにオプトアウトするように改良できます。私にとって、私が欲しいのは、ユーザーが私のアプリのダークモードインターフェイスを気に入っているかどうかを確認し、ユーザーが気に入らない場合はオフにできることです。これにより、残りのアプリケーションでダークモードを引き続き使用できます。

ユーザーの選択は適切です(Ahem、Appleを見ると、これが実装方法でした)。

これがどのように機能するかは、UIViewControllerの単なるカテゴリであるということです。ロードすると、ネイティブのviewDidLoadメソッドがグローバルフラグをチェックしてダークモードがすべて無効になっているかどうかを確認するメソッドに置き換えられます。

UIViewControllerのロード時にトリガーされるため、デフォルトでダークモードが自動的に起動して無効になります。これが必要なものでない場合は、早期にどこかにアクセスしてフラグを設定するか、デフォルトのフラグを設定する必要があります。

フラグをオンまたはオフにするユーザーに応答するために、まだ何も書いていません。したがって、これは基本的にサンプルコードです。ユーザーがこれを操作できるようにするには、すべてのView Controllerを再ロードする必要があります。私はそれをオフハンドで行う方法を知りませんが、おそらく何らかの通知を送信することでうまくいくでしょう。したがって、現在、このダークモードのグローバルオン/オフは、アプリの起動時または再起動時にのみ機能します。

巨大なアプリのすべてのMFING viewControllerでダークモードをオフにしようとするだけでは十分ではありません。カラーアセットを使用している場合は、完全に骨が折れています。私たちは10年以上にわたり、不変オブジェクトは不変であると理解しています。カラーアセットカタログから取得した色は、UIColorであると言いますが、動的(可変)色であり、システムが暗いモードから明るいモードに変わると、その下で変化します。それが特徴のはずです。ただし、もちろん、これらの変更を停止するように要求するマスター切り替えはありません(私が今知っている限りでは、誰かがこれを改善できるかもしれません)。

したがって、ソリューションは2つの部分に分かれています。

  1. いくつかのユーティリティと便利なメソッドを提供するUIViewControllerのパブリックカテゴリ...たとえば、私たちの一部がアプリにWebコードを混在させるという事実についてAppleが考えていなかったと思います。そのため、ダークモードまたはライトモードに基づいて切り替える必要のあるスタイルシートがあります。したがって、何らかの動的スタイルシートオブジェクトを作成するか(これは良いことです)、または単に現在の状態が何であるかを尋ねる必要があります(悪いが簡単です)。

  2. このカテゴリがロードされると、UIViewControllerクラスのviewDidLoadメソッドが置き換えられ、呼び出しがインターセプトされます。それがアプリストアのルールに違反しているかどうかはわかりません。もしそうなら、おそらく他の方法がありますが、それは概念実証と考えることができます。たとえば、すべてのメインビューコントローラータイプの1つのサブクラスを作成し、すべての独自のビューコントローラーをそれらから継承させることができます。次に、DarkModeカテゴリのアイデアを使用してそれを呼び出し、すべてのビューコントローラーを強制的にオプトアウトできます。それは醜いですが、それはいかなる規則にも違反しません。私はランタイムを使用することを好みます。それは、ランタイムがそうするために作られたものだからです。したがって、私のバージョンでは、カテゴリを追加するだけで、ダークモードをブロックするかどうかに応じて、カテゴリにグローバル変数を設定します。

  3. 既に説明したように、あなたはまだ森の外にいません。もう1つの問題は、UIColorが基本的に何をしたいのかということです。したがって、ビューコントローラーがダークモードをブロックしている場合でも、UIColorはそれをどこでどのように使用しているかを認識できないため、適応できません。その結果、あなたはそれを正しくフェッチすることができますが、それは将来のある時点で元に戻るでしょう。多分すぐに多分後で。したがって、その回避策は、CGColorを使用して2回割り当て、静的な色に変換することです。これは、ユーザーが戻って設定ページでダークモードを再度有効にした場合(ここでのアイデアは、ユーザーがシステムの他の部分に加えてアプリを制御できるようにこの機能を動作させることです)、それらすべての静的な色交換が必要です。これまでのところ、これは誰かが解決するために残されています。それを行う簡単なお尻の方法は、あなたがデフォルトを作ることです ダークモードをオプトアウトするには、ゼロで除算してアプリをクラッシュさせ、アプリを終了してユーザーに再起動するように指示することはできないためです。これはおそらくアプリストアのガイドラインにも違反していますが、それはアイデアです。

UIColorカテゴリを公開する必要はありません。colorNamedを呼び出して機能します。... DarkMode ViewControllerクラスにダークモードをブロックするように指示しなかった場合、期待どおりに完全に機能します。標準のリンゴのスファゲッティコードの代わりにエレガントなものを作ろうとすると、プログラムでダークモードをオプトアウトまたはトグルしたい場合、ほとんどのアプリを変更する必要があります。現在、Info.plistをプログラムで変更して、必要に応じてダークモードをオフにするより良い方法があるかどうかはわかりません。私が理解している限り、これはコンパイル時の機能であり、その後は骨の折れる作業です。

したがって、ここに必要なコードがあります。ドロップインし、1つのメソッドを使用してUIスタイルを設定するか、コードでデフォルトを設定する必要があります。あなたは自由に使用、変更、これをどんな目的のためにでも好きなように行うことができ、保証はありません。それがアプリストアを通過するかどうかはわかりません。改善は大歓迎です。

公正な警告ARCやその他の手持ちの方法は使用しません。

////// H file

#import <UIKit/UIKit.h>

@interface UIViewController(DarkMode)

// if you want to globally opt out of dark mode you call these before any view controllers load
// at the moment they will only take effect for future loaded view controllers, rather than currently
// loaded view controllers

// we are doing it like this so you don't have to fill your code with @availables() when you include this
typedef enum {
    QOverrideUserInterfaceStyleUnspecified,
    QOverrideUserInterfaceStyleLight,
    QOverrideUserInterfaceStyleDark,
} QOverrideUserInterfaceStyle;

// the opposite condition is light interface mode
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)override;
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;

// utility methods
// this will tell you if any particular view controller is operating in dark mode
- (BOOL)isUsingDarkInterfaceStyle;
// this will tell you if any particular view controller is operating in light mode mode
- (BOOL)isUsingLightInterfaceStyle;

// this is called automatically during all view controller loads to enforce a single style
- (void)tryToOverrideUserInterfaceStyle;

@end


////// M file


//
//  QDarkMode.m

#import "UIViewController+DarkMode.h"
#import "q-runtime.h"


@implementation UIViewController(DarkMode)

typedef void (*void_method_imp_t) (id self, SEL cmd);
static void_method_imp_t _nativeViewDidLoad = NULL;
// we can't @available here because we're not in a method context
static long _override = -1;

+ (void)load;
{
#define DEFAULT_UI_STYLE UIUserInterfaceStyleLight
    // we won't mess around with anything that is not iOS 13 dark mode capable
    if (@available(iOS 13,*)) {
        // default setting is to override into light style
        _override = DEFAULT_UI_STYLE;
        /*
         This doesn't work...
        NSUserDefaults *d = NSUserDefaults.standardUserDefaults;
        [d setObject:@"Light" forKey:@"UIUserInterfaceStyle"];
        id uiStyle = [d objectForKey:@"UIUserInterfaceStyle"];
        NSLog(@"%@",uiStyle);
         */
        if (!_nativeViewDidLoad) {
            Class targetClass = UIViewController.class;
            SEL targetSelector = @selector(viewDidLoad);
            SEL replacementSelector = @selector(_overrideModeViewDidLoad);
            _nativeViewDidLoad = (void_method_imp_t)QMethodImplementationForSEL(targetClass,targetSelector);
            QInstanceMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
        }
    }
}

// we do it like this because it's not going to be set often, and it will be tested often
// so we can cache the value that we want to hand to the OS
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)style;
{
    if (@available(iOS 13,*)){
        switch(style) {
            case QOverrideUserInterfaceStyleLight: {
                _override = UIUserInterfaceStyleLight;
            } break;
            case QOverrideUserInterfaceStyleDark: {
                _override = UIUserInterfaceStyleDark;
            } break;
            default:
                /* FALLTHROUGH - more modes can go here*/
            case QOverrideUserInterfaceStyleUnspecified: {
                _override = UIUserInterfaceStyleUnspecified;
            } break;
        }
    }
}
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;
{
    if (@available(iOS 13,*)){
        switch(_override) {
            case UIUserInterfaceStyleLight: {
                return QOverrideUserInterfaceStyleLight;
            } break;
            case UIUserInterfaceStyleDark: {
                return QOverrideUserInterfaceStyleDark;
            } break;
            default:
                /* FALLTHROUGH */
            case UIUserInterfaceStyleUnspecified: {
                return QOverrideUserInterfaceStyleUnspecified;
            } break;
        }
    } else {
        // we can't override anything below iOS 12
        return QOverrideUserInterfaceStyleUnspecified;
    }
}

- (BOOL)isUsingDarkInterfaceStyle;
{
    if (@available(iOS 13,*)) {
        if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark){
            return YES;
        }
    }
    return NO;
}

- (BOOL)isUsingLightInterfaceStyle;
{
    if (@available(iOS 13,*)) {
        if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight){
            return YES;
        }
        // if it's unspecified we should probably assume light mode, esp. iOS 12
    }
    return YES;
}

- (void)tryToOverrideUserInterfaceStyle;
{
    // we have to check again or the compile will bitch
    if (@available(iOS 13,*)) {
        [self setOverrideUserInterfaceStyle:(UIUserInterfaceStyle)_override];
    }
}

// this method will be called via the viewDidLoad chain as we will patch it into the
// UIViewController class
- (void)_overrideModeViewDidLoad;
{
    if (_nativeViewDidLoad) {
        _nativeViewDidLoad(self,@selector(viewDidLoad));
    }
    [self tryToOverrideUserInterfaceStyle];
}


@end

// keep this in the same file, hidden away as it needs to switch on the global ... yeah global variables, I know, but viewDidLoad and colorNamed: are going to get called a ton and already it's adding some inefficiency to an already inefficient system ... you can change if you want to make it a class variable. 

// this is necessary because UIColor will also check the current trait collection when using asset catalogs
// so we need to repair colorNamed: and possibly other methods
@interface UIColor(DarkMode)
@end

@implementation UIColor (DarkMode)

typedef UIColor *(*color_method_imp_t) (id self, SEL cmd, NSString *name);
static color_method_imp_t _nativeColorNamed = NULL;
+ (void)load;
{
    // we won't mess around with anything that is not iOS 13 dark mode capable
    if (@available(iOS 13,*)) {
        // default setting is to override into light style
        if (!_nativeColorNamed) {
            // we need to call it once to force the color assets to load
            Class targetClass = UIColor.class;
            SEL targetSelector = @selector(colorNamed:);
            SEL replacementSelector = @selector(_overrideColorNamed:);
            _nativeColorNamed = (color_method_imp_t)QClassMethodImplementationForSEL(targetClass,targetSelector);
            QClassMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
        }
    }
}


// basically the colors you get
// out of colorNamed: are dynamic colors... as the system traits change underneath you, the UIColor object you
// have will also change since we can't force override the system traits all we can do is force the UIColor
// that's requested to be allocated out of the trait collection, and then stripped of the dynamic info
// unfortunately that means that all colors throughout the app will be static and that is either a bug or
// a good thing since they won't respond to the system going in and out of dark mode
+ (UIColor *)_overrideColorNamed:(NSString *)string;
{
    UIColor *value = nil;
    if (@available(iOS 13,*)) {
        value = _nativeColorNamed(self,@selector(colorNamed:),string);
        if (_override != UIUserInterfaceStyleUnspecified) {
            // the value we have is a dynamic color... we need to resolve against a chosen trait collection
            UITraitCollection *tc = [UITraitCollection traitCollectionWithUserInterfaceStyle:_override];
            value = [value resolvedColorWithTraitCollection:tc];
        }
    } else {
        // this is unreachable code since the method won't get patched in below iOS 13, so this
        // is left blank on purpose
    }
    return value;
}
@end

メソッドスワッピングを実行するために使用するユーティリティ関数のセットがあります。別のファイル。これは標準的なものですが、どこでも同じようなコードを見つけることができます。

// q-runtime.h

#import <Foundation/Foundation.h>
#import <objc/message.h>
#import <stdatomic.h>

// returns the method implementation for the selector
extern IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector);

// as above but gets class method
extern IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector);


extern BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                              Class replacementClass, SEL replacementSelector);

extern BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                                 Class replacementClass, SEL replacementSelector);


// q-runtime.m

static BOOL
_QMethodOverride(Class targetClass, SEL targetSelector, Method original, Method replacement)
{
    BOOL flag = NO;
    IMP imp = method_getImplementation(replacement);
    // we need something to work with
    if (replacement) {
        // if something was sitting on the SEL already
        if (original) {
            flag = method_setImplementation(original, imp) ? YES : NO;
            // if we're swapping, use this
            //method_exchangeImplementations(om, rm);
        } else {
            // not sure this works with class methods...
            // if it's not there we want to add it
            flag = YES;
            const char *types = method_getTypeEncoding(replacement);
            class_addMethod(targetClass,targetSelector,imp,types);
            XLog_FB(red,black,@"Not sure this works...");
        }
    }
    return flag;
}

BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                                 Class replacementClass, SEL replacementSelector)
{
    BOOL flag = NO;
    if (targetClass && replacementClass) {
        Method om = class_getInstanceMethod(targetClass,targetSelector);
        Method rm = class_getInstanceMethod(replacementClass,replacementSelector);
        flag = _QMethodOverride(targetClass,targetSelector,om,rm);
    }
    return flag;
}


BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
                              Class replacementClass, SEL replacementSelector)
{
    BOOL flag = NO;
    if (targetClass && replacementClass) {
        Method om = class_getClassMethod(targetClass,targetSelector);
        Method rm = class_getClassMethod(replacementClass,replacementSelector);
        flag = _QMethodOverride(targetClass,targetSelector,om,rm);
    }
    return flag;
}

IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector)
{
    Method method = class_getInstanceMethod(aClass,aSelector);
    if (method) {
        return method_getImplementation(method);
    } else {
        return NULL;
    }
}

IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector)
{
    Method method = class_getClassMethod(aClass,aSelector);
    if (method) {
        return method_getImplementation(method);
    } else {
        return NULL;
    }
}

q-runtime.hは私の再利用可能なライブラリであり、これはその一部にすぎないため、これをいくつかのファイルからコピーして貼り付けています。コンパイルできないものがある場合はお知らせください。


この質問で述べたように、それは、UIColorの挙動をcontrolingに来るときあなたは運が悪いではありません。stackoverflow.com/questions/56487679/...
raven_raven
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.