Ist es möglich, den Dunkelmodus unter iOS 13 zu deaktivieren?

295

Ein großer Teil meiner App besteht aus Webansichten, um Funktionen bereitzustellen, die durch native Implementierungen noch nicht verfügbar sind. Das Webteam hat keine Pläne, ein dunkles Thema für die Website zu implementieren. Daher sieht meine App mit Dark Mode-Unterstützung unter iOS 13 ein bisschen halb aus.

Ist es möglich, die Unterstützung für den Dunkelmodus so zu deaktivieren, dass unsere App immer den Lichtmodus anzeigt, der zum Thema der Website passt?

SeanR
quelle
70
Setzen Sie UIUserInterfaceStyleauf LightIhrem info.plist. Siehe developer.apple.com/library/archive/documentation/General/…
Tieme
1
Danke, dass Sie gefragt haben - für uns alle. Viele Apps zum Durchlaufen. Dies ist erforderlich, damit Apps so lange funktionieren, bis der Umschalter bereit ist.
user3741598
import Foundation import UIKit-Erweiterung UIViewController {open func awakeFromNib () überschreiben {super.awakeFromNib () wenn #available (iOS 13.0, *) {// Immer einen Light-Interface-Stil annehmen. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour
1
Fügen Sie einfach UIUserInterfaceStyle in plist hinzu. es ist so einfach
Fattie
Beim Senden der App an den Appstore akzeptiert Apple dies aufgrund von UIUserInterfaceStyle im Light-Modus.
Kiran

Antworten:

682

Hier ist zunächst Apples Eintrag zum Deaktivieren des Dunkelmodus. Der Inhalt dieses Links ist für Xcode 11 und iOS 13 geschrieben :

Dieser Abschnitt gilt für die Verwendung von Xcode 11


Wenn Sie Ihre GESAMTE Bewerbung abbestellen möchten

Ansatz Nr. 1

Verwenden Sie den folgenden Schlüssel in Ihrer Datei info.plist :

UIUserInterfaceStyle

Und weisen Sie ihm einen Wert von zu Light.

Das XML für die UIUserInterfaceStyleZuordnung:

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

Ansatz Nr. 2

Sie können overrideUserInterfaceStylegegen die windowVariable der App setzen .

Je nachdem, wie Ihr Projekt erstellt wurde, befindet sich dies möglicherweise in der AppDelegateDatei oder in der SceneDelegate.

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


Wenn Sie Ihren UIViewController individuell deaktivieren möchten

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

Apple-Dokumentation für overrideUserInterfaceStyle

Wie der obige Code in Xcode 11 aussehen wird:

Geben Sie hier die Bildbeschreibung ein

Dieser Abschnitt gilt für die Verwendung von Xcode 10.x.


Wenn Sie Xcode 11 für Ihre Übermittlung verwenden, können Sie alles unter dieser Zeile ignorieren.

Da die entsprechende API in iOS 12 nicht vorhanden ist, werden beim Versuch, die oben angegebenen Werte zu verwenden, Fehler angezeigt:

Zum Einstellen overrideUserInterfaceStylein IhremUIViewController

Geben Sie hier die Bildbeschreibung ein

Wenn Sie Ihren UIViewController individuell deaktivieren möchten

Dies kann in Xcode 10 durch Testen der Compiler-Version und der iOS-Version behoben werden:

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

Wenn Sie Ihre GESAMTE Bewerbung abbestellen möchten

Sie können das obige Snippet so ändern, dass es für die gesamte Anwendung für Xcode 10 funktioniert, indem Sie Ihrer AppDelegateDatei den folgenden Code hinzufügen .

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

Bei Verwendung von Xcode Version 10.x schlägt die Plist-Einstellung jedoch fehl:

Geben Sie hier die Bildbeschreibung ein

Dank an @Aron Nelson , @Raimundas Sakalauskas , @NSLeader und rmaddy für die Verbesserung dieser Antwort mit ihrem Feedback.

CodeBender
quelle
2
Das UIUserInterfaceStyle-Licht wird blockiert, wenn Sie Ihre App jetzt aktualisieren / hochladen. Es wird als ungültiger Plist-Eintrag gekennzeichnet. (Ungültiger Plist-Schlüssel)
Aron Nelson
2
Dies wird nicht mit iOS SDK 12 (derzeit aktuelles stabiles SDK) kompiliert. Unter stackoverflow.com/a/57521901/2249485 finden Sie eine Lösung, die auch mit iOS 12 SDK funktioniert.
Raimundas Sakalauskas
Dies ist so unfair, dass die Frage, die viel mehr Ansichten als die "ursprüngliche Frage" hat, für die Bereitstellung von Antworten gesperrt ist. :(
Raimundas Sakalauskas
7
Statt der Einstellung overrideUserInterfaceStylein viewDidLoadjeder View - Controller, können Sie es einmal auf das Hauptfenster der Anwendung festgelegt. So viel einfacher, wenn Sie möchten, dass sich die gesamte App in eine Richtung verhält.
rmaddy
2
Verwenden Sie #if compiler(>=5.1)stattdessen responds(to:)undsetValue
NSLeader
162

Laut Apple - Sitzung zum Thema „Implementierung von dunklen Modus auf iOS“ ( https://developer.apple.com/videos/play/wwdc2019/214/ bei 31:13 Start) ist es möglich, Satz overrideUserInterfaceStylezu UIUserInterfaceStyleLightoder UIUserInterfaceStyleDarkauf jedem View - Controller oder Ansicht , die in der verwendet werdentraitCollection für jede Unteransicht oder Ansichtssteuerung verwendet wird.

Wie bereits von SeanR erwähnt, können Sie dies UIUserInterfaceStylein Lightoder Darkin der Plist-Datei Ihrer App festlegen, um dies für Ihre gesamte App zu ändern.

Dorbeetle
quelle
17
Wenn Sie den UIUserInterfaceStyle-Schlüssel festlegen, wird Ihre App im App Store abgelehnt
Sonius
2
Apple mit ITMS-90190 Fehlercode forums.developer.apple.com/thread/121028
PRASAD1240
11
Die Ablehnung ist am wahrscheinlichsten, da das iOS 13 SDK noch nicht aus der Beta ist. Ich denke, das sollte funktionieren, sobald der Xcode 11 GM verfügbar ist.
Dorbeetle
2
@dorbeetle es ist nicht wahr, ich habe meine App mit diesem Schlüssel erfolgreich hochgeladen, wie vor 1 Monat mit Xcode 10. Die Ablehnungen geschehen vor kurzem. Es scheint einige Arten von neuen Apple-Strategie.
Steven
1
Es passiert immer noch. Xcode GM2 hat einen App-Signaturfehler zurückgegeben. Xcode 10.3 gab Folgendes zurück: "Ungültiger Info.plist-Schlüssel. Der Schlüssel 'UIUserInterfaceStyle' in der Datei Payload / Galileo.appInfo.plist ist ungültig."
Evgen Bodunov
64

Wenn Sie Xcode 11 oder höher (z. B. iOS 13 oder höher SDK) nicht verwenden, hat sich Ihre App nicht automatisch für die Unterstützung des Dunkelmodus entschieden. Sie müssen den Dunkelmodus also nicht deaktivieren.

Wenn Sie Xcode 11 oder höher verwenden, hat das System den Dunkelmodus für Ihre App automatisch aktiviert. Es gibt zwei Möglichkeiten, den Dunkelmodus je nach Ihren Vorlieben zu deaktivieren. Sie können es vollständig deaktivieren oder für ein bestimmtes Fenster, eine bestimmte Ansicht oder einen bestimmten Ansichtscontroller deaktivieren.

Deaktivieren Sie den dunklen Modus vollständig für Ihre App

Sie können den Dunkelmodus deaktivieren, indem Sie den UIUserInterfaceStyleSchlüssel mit einem Wert wie Lightin der Info.plist-Datei Ihrer App einfügen. Dies ignoriert die Präferenzen des Benutzers und verleiht Ihrer App immer ein helles Aussehen.
UIUserInterfaceStyle als Licht

Deaktivieren Sie den Dunkelmodus für Fenster, Ansicht oder View Controller

Sie können erzwingen, dass Ihre Benutzeroberfläche immer hell oder dunkel angezeigt wird, indem Sie die Einstellung festlegen overrideUserInterfaceStyle Eigenschaft des entsprechenden Fensters, der Ansicht oder des Ansichtscontrollers festlegen.

Controller anzeigen:

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

Ansichten:

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

Fenster:

/* 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

Hinweis: Apple empfiehlt dringend, den Dunkelmodus in Ihrer App zu unterstützen. Sie können den Dunkelmodus also nur vorübergehend deaktivieren.

Lesen Sie hier mehr: Auswählen eines bestimmten Schnittstellenstils für Ihre iOS-App

Ajith R Nayak
quelle
34

********** Einfachster Weg für Xcode 11 und höher ***********

Fügen Sie dies vorher zu info.plist hinzu </dict></plist>

<key>UIUserInterfaceStyle</key>
<string>Light</string>
Kingsley Mitchell
quelle
Diese Lösung schlägt fehl, wenn die App auf Xcode 10.x
Tawfik Bouabid
27

Ich glaube, ich habe die Lösung gefunden. Ich habe es zunächst aus UIUserInterfaceStyle - Information Property List und UIUserInterfaceStyle - UIKit zusammengesetzt , aber jetzt gefunden, dass es tatsächlich unter Auswählen eines bestimmten Schnittstellenstils für Ihre iOS-App dokumentiert ist .

In Ihrem info.plistsetzen UIUserInterfaceStyle( User Interface Design ) bis 1 ( UIUserInterfaceStyle.light).

BEARBEITEN: Gemäß der Antwort von dorbeetle kann eine geeignetere Einstellung für UIUserInterfaceStylesein Light.

SeanR
quelle
Das Erzwingen des Dunkelmodus durch Setzen des Werts auf 2 funktioniert jedoch nicht:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen
3
Wenn Sie diesen Schlüssel in der Liste haben, wird der App Store abgelehnt.
José
1
AppStore lehnt diese Eigenschaft in plist.info nicht mehr ab. Ich habe "Dark" (groß geschrieben) gesetzt, da unsere App bereits dunkel ist. Keine Probleme. Auf diese Weise können wir die Systemsteuerungen ordnungsgemäß verwenden.
Nickdnk
@nickdnk Ich denke, Sie haben Ihre App mit Xcode 11 erstellt, was von Apple empfohlen wird.
DawnSong
1
Ja, habe ich. Es ändert nichts an der Tatsache, dass Apple diesen Parameter in der Liste akzeptiert, was ich klarstellen wollte.
Nickdnk
23

Die obige Antwort funktioniert, wenn Sie die gesamte App deaktivieren möchten. Wenn Sie an der Bibliothek mit Benutzeroberfläche arbeiten und nicht den Luxus haben, .plist zu bearbeiten, können Sie dies auch über Code tun.

Wenn Sie mit iOS 13 SDK kompilieren, können Sie einfach den folgenden Code verwenden:

Schnell:

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

Obj-C:

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

JEDOCH , wenn Sie möchten, dass Ihr Code mit iOS 12 SDK kompiliert wird zu (die jetzt noch die letzte stabile SDK ist), sollten Sie auf die Verwendung Selektoren greifen. Code mit Selektoren:

Swift (XCode zeigt Warnungen für diesen Code an, aber dies ist die einzige Möglichkeit, dies zu tun, da die Eigenschaft in SDK 12 nicht vorhanden ist und daher nicht kompiliert werden kann):

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"];
    }
}
Raimundas Sakalauskas
quelle
Es ist besser, wenn Sie angeben, zu was die Eigenschaft overrideUserInterfaceStylegehört.
DawnSong
12

Neuestes Update-

Wenn Sie Xcode 10.x verwenden, dann der Standard UIUserInterfaceStyleist lightfür iOS 13.x. Wenn es auf einem iOS 13-Gerät ausgeführt wird, funktioniert es nur im Lichtmodus.

Sie müssen den UIUserInterfaceStyleSchlüssel nicht explizit in die Datei Info.plist einfügen. Wenn Sie ihn hinzufügen, wird beim Überprüfen Ihrer App ein Fehler angezeigt.

Ungültiger Info.plist-Schlüssel. Der Schlüssel 'UIUserInterfaceStyle' in der Datei Payload / AppName.appInfo.plist ist ungültig.

UIUserInterfaceStyleFügen Sie den Schlüssel nur in die Datei Info.plist ein, wenn Sie Xcode 11.x verwenden.

kumarsiddharth123
quelle
1
Dies hat nichts mit Xcode 10 oder 11 zu tun. Wenn der Benutzer die App von Xcode 10 bereitstellt und sich nicht um den Dunkelmodus kümmert, treten bei der Installation der App auf dem iPhone 11, Pro oder Pro Max Probleme mit dem Dunkelmodus auf. Sie müssen auf Xcode 11 aktualisieren und dieses Problem beheben.
Niranjan Molkeri
3
@NiranjanMolkeri Dies hat nichts mit neueren iPhones zu tun. Es geht um den Dunkelmodus unter iOS 13. In früheren Apps der Beta-Version von iOS 13 gab es Probleme mit dem Dunkelmodus, wenn dies nicht explizit behandelt wurde. Aber in der neuesten Version ist das behoben. Wenn Sie XCode 10 verwenden, ist der Standard-UIUserInterfaceStyle für iOS13 hell. Wenn Sie Xode11 verwenden, müssen Sie damit umgehen.
Kumarsiddharth123
Sie werden Probleme haben, wenn Sie eine App mit Xcode 10.3 auf TestFligth hochladen und die Liste den Schlüssel UIUserInterfaceStyle enthält. Es wird angezeigt, dass es sich um eine ungültige Plist-Datei handelt. Sie müssen es entweder entfernen, wenn Sie Xcode 10
einbauen
9

Wenn Sie UIUserInterfaceStyleder plist-Datei einen Schlüssel hinzufügen , lehnt Apple möglicherweise den hier erwähnten Release-Build ab: https://stackoverflow.com/a/56546554/7524146 Auf jeden Fall ist es ärgerlich, jedem ViewController dies explizit mitzuteilen self.overrideUserInterfaceStyle = .light. Sie können diesen Code-Code jedoch einmal für Ihr Stammobjekt verwenden window:

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

Beachten Sie nur, dass Sie dies im Inneren nicht tun können, application(application: didFinishLaunchingWithOptions:)da dieser Selektor truein diesem frühen Stadium nicht reagiert . Aber Sie können es später tun. Es ist sehr einfach, wenn Sie in Ihrer App benutzerdefinierte AppPresenteroder AppRouterKlassen verwenden, anstatt die Benutzeroberfläche in AppDelegate automatisch zu starten.

SerhiiK
quelle
9

Sie können den Dunkelmodus in der gesamten Anwendung in Xcode 11 deaktivieren:

  1. Gehen Sie zu Info.plist
  2. Fügen Sie unten wie hinzu

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

Info.plist wird wie folgt aussehen ...

Geben Sie hier die Bildbeschreibung ein

Enamul Haque
quelle
1
funktioniert aus irgendeinem Grund nicht für Xcode Version 11.3.1 (11C504)
Andrew
7

- Für die gesamte App (Fenster):

window!.overrideUserInterfaceStyle = .light

Sie können das Fenster von bekommen SceneDelegate

- Für einen einzelnen ViewController:

viewController.overrideUserInterfaceStyle = .light

Sie können jeden viewControllerselbst im viewController selbst einstellen

- Für eine einzelne Ansicht:

view.overrideUserInterfaceStyle = .light

Sie können jede festlegen view, auch innerhalb der Ansicht selbst

Möglicherweise müssen Sie verwenden, if #available(iOS 13.0, *) { ,,, }wenn Sie frühere iOS-Versionen unterstützen.

Mojtaba Hosseini
quelle
6

Abgesehen von anderen Antworten müssen Sie sich nach meinem Verständnis der folgenden Punkte nur auf den Dunkelmodus vorbereiten, wenn Sie mit iOS 13 SDK kompilieren (mit XCode 11).

Das System geht davon aus, dass Apps, die mit dem SDK für iOS 13 oder höher verknüpft sind, sowohl helle als auch dunkle Erscheinungen unterstützen. In iOS geben Sie das gewünschte Erscheinungsbild an, indem Sie Ihrem Fenster, Ihrer Ansicht oder Ihrem Ansichtscontroller einen bestimmten Schnittstellenstil zuweisen. Sie können die Unterstützung für den Dunklen Modus auch mithilfe einer Info.plist-Taste vollständig deaktivieren.

Verknüpfung

Claudio
quelle
2

Ja, Sie können überspringen, indem Sie den folgenden Code in viewDidLoad hinzufügen:

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

Meine App unterstützt derzeit keinen dunklen Modus und verwendet eine helle App-Balkenfarbe. Ich konnte den Inhalt der Statusleiste in dunklen Text und Symbole zwingen, indem ich meinem Schlüssel den folgenden Schlüssel hinzufügte Info.plist:

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

Die anderen möglichen Werte finden Sie hier: https://developer.apple.com/documentation/uikit/uistatusbarstyle

ToniTornado
quelle
2

Objective-c-Version

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

Hier sind einige Tipps und Tricks, die Sie in Ihrer App verwenden können, um den Dunkelmodus zu unterstützen oder zu umgehen.

Erster Tipp: So überschreiben Sie den ViewController-Stil

Sie können den Schnittstellenstil von UIViewController durch überschreiben

1: overrideUserInterfaceStyle = .dark // Für den dunklen Modus

2: overrideUserInterfaceStyle = .light // Für den Lichtmodus

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

Zweiter Tipp: Hinzufügen eines Schlüssels in info.plist

Sie können einfach einen neuen Schlüssel hinzufügen

UIUserInterfaceStyle

in Ihrer App info.plist und setzen Sie den Wert auf Hell oder Dunkel. Dadurch wird der Standardstil der App auf den von Ihnen angegebenen Wert überschrieben. Sie müssen nicht overrideUserInterfaceStyle = .light diese Zeile in jedem viewController hinzufügen, nur eine Zeile in info.plist ist es.

Mohammed Ebrahim
quelle
1

Fügen Sie einfach folgenden Schlüssel in Ihre info.plistDatei ein:

<key>UIUserInterfaceStyle</key>
    <string>Light</string>
Moeen Ahmad
quelle
1
 if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light
        } else {
            // Fallback on earlier versions
        }
Talha Rasool
quelle
Können Sie ein wenig erklären, wie diese Antwort das Problem löst, anstatt nur einen Code zu veröffentlichen?
Arun Vinoth
Ja sicher @ArunVinoth In IOS 13 wird der Dunkelmodus eingeführt. Wenn Ihr Bereitstellungsziel niedriger als 13 ist, verwenden Sie den obigen Code. Andernfalls können Sie eine einfache Anweisung verwenden, die in if block geschrieben ist.
Talha Rasool
1

Swift 5

Zwei Möglichkeiten, um den Dunkel- in den Hellmodus zu schalten:

1- info.plist

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

2- Programmatisch

 UIApplication.shared.windows.forEach { window in
     window.overrideUserInterfaceStyle = .light
  } 
Rashid Latif
quelle
0

Ich würde diese Lösung verwenden, da die Fenstereigenschaft während des App-Lebenszyklus geändert werden kann. Die Zuweisung von "overrideUserInterfaceStyle = .light" muss daher wiederholt werden. Mit UIWindow.appearance () können wir den Standardwert festlegen, der für neu erstellte UIWindow-Objekte verwendet wird.

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
    }
}
Dmitry
quelle
0

Fügen Sie einfach diese Zeile in die Datei info.plist ein:

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

Dadurch wird die App nur im Lichtmodus ausgeführt.

Rahul Gusain
quelle
Dies wurde bereits mehrfach kommentiert und beantwortet. Sogar die akzeptierte Antwort legt dies nahe. Daher fügt dieser Kommentar keine neuen Informationen hinzu.
JeroenJK
0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}
Mohammad Razipour
quelle
2
Bitte fügen Sie Ihrer Antwort eine Erklärung hinzu, indem Sie sie bearbeiten, damit andere daraus lernen können
Nico Haase
0

Sie können Folgendes tun: Fügen Sie diesen neuen Schlüssel UIUserInterfaceStyle zu Info.plist hinzu und setzen Sie seinen Wert auf Light. und überprüfen Sie, ob der Alarmregler im Lichtmodus angezeigt wird.

UIUserInterfaceStyle Light Wenn Sie in Ihrer gesamten Anwendung den Hell / Dunkel-Modus erzwingen, unabhängig von den Einstellungen des Benutzers, fügen Sie den Schlüssel UIUserInterfaceStyle zu Ihrer Info.plist-Datei hinzu und setzen Sie den Wert entweder auf Hell oder Dunkel.

Hominda
quelle
0

Diese Frage hat so viele Antworten. Wenn Sie sie in verwenden info.plist, können Sie sie AppDelegatewie folgt einstellen :

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

Test auf Xcode 11.3, iOS 13.3

Niraj
quelle
-8

Eigentlich habe ich gerade einen Code geschrieben, mit dem Sie den Dunkelmodus im Code global deaktivieren können, ohne mit jedem einzelnen viw-Controller in Ihrer Anwendung putzeln zu müssen. Dies kann wahrscheinlich verfeinert werden, um Klasse für Klasse durch Deaktivieren einer Klassenliste zu deaktivieren. Ich möchte, dass meine Benutzer sehen, ob ihnen die Dunkelmodus-Oberfläche für meine App gefällt, und wenn sie sie nicht mögen, können sie sie deaktivieren. Dadurch können sie den Dunkelmodus für den Rest ihrer Anwendungen weiterhin verwenden.

Die Auswahl der Benutzer ist gut (Ahem, wenn Sie sich Apple ansehen, sollten Sie es so implementiert haben).

Das funktioniert also so, dass es nur eine Kategorie von UIViewController ist. Beim Laden wird die native viewDidLoad-Methode durch eine ersetzt, die ein globales Flag überprüft, um festzustellen, ob der Dunkelmodus für alles deaktiviert ist oder nicht.

Da es beim Laden von UIViewController ausgelöst wird, sollte es automatisch gestartet und der Dunkelmodus standardmäßig deaktiviert werden. Wenn dies nicht das ist, was Sie wollen, müssen Sie irgendwo früh einsteigen und das Flag setzen, oder setzen Sie einfach das Standard-Flag.

Ich habe noch nichts geschrieben, um dem Benutzer zu antworten, der die Flagge ein- oder ausschaltet. Das ist also im Grunde ein Beispielcode. Wenn der Benutzer damit interagieren soll, müssen alle Ansichts-Controller neu geladen werden. Ich weiß nicht, wie ich das ohne weiteres machen soll, aber wahrscheinlich reicht es aus, eine Benachrichtigung zu senden. Im Moment funktioniert dieses globale Ein / Aus für den Dunkelmodus nur beim Start oder Neustart der App.

Jetzt reicht es nicht mehr aus, den Dunkelmodus in jedem einzelnen MFING viewController in Ihrer riesigen App auszuschalten. Wenn Sie Farbressourcen verwenden, sind Sie vollständig entbeint. Wir haben seit mehr als 10 Jahren unveränderliche Objekte als unveränderlich verstanden. Farben, die Sie aus dem Farb-Asset-Katalog erhalten, sind UIColor-Farben, aber dynamische (veränderbare) Farben, die sich unter Ihnen ändern, wenn das System vom Dunkel- in den Hell-Modus wechselt. Das soll ein Feature sein. Aber natürlich gibt es keinen Master-Schalter, der diese Dinge auffordert, diese Änderung zu beenden (soweit ich jetzt weiß, kann vielleicht jemand dies verbessern).

Die Lösung besteht also aus zwei Teilen:

  1. Eine öffentliche Kategorie auf UIViewController, die einige nützliche und bequeme Methoden bietet. Ich glaube zum Beispiel nicht, dass Apple darüber nachgedacht hat, dass einige von uns Webcode in ihre Apps einmischen. Als solche haben wir Stylesheets, die basierend auf dem Dunkel- oder Hellmodus umgeschaltet werden müssen. Daher müssen Sie entweder eine Art dynamisches Stylesheet-Objekt erstellen (was gut wäre) oder einfach nach dem aktuellen Status fragen (schlecht, aber einfach).

  2. Diese Kategorie ersetzt beim Laden die viewDidLoad-Methode der UIViewController-Klasse und fängt Aufrufe ab. Ich weiß nicht, ob das gegen die App Store-Regeln verstößt. Wenn dies der Fall ist, gibt es wahrscheinlich andere Möglichkeiten, aber Sie können es als Proof of Concept betrachten. Sie können beispielsweise eine Unterklasse aller Haupttypen von Ansichtscontrollern erstellen und alle Ihre eigenen Ansichtscontroller von diesen erben lassen. Anschließend können Sie die DarkMode-Kategorieidee verwenden und sie aufrufen, um das Deaktivieren aller Ihrer Ansichtscontroller zu erzwingen. Es ist hässlicher, aber es wird keine Regeln brechen. Ich bevorzuge die Verwendung der Laufzeit, da dies die Aufgabe der Laufzeit ist. In meiner Version fügen Sie einfach die Kategorie hinzu. Sie legen eine globale Variable für die Kategorie fest, ob sie den Dunkelmodus blockieren soll oder nicht.

  3. Sie sind noch nicht aus dem Wald, wie bereits erwähnt, das andere Problem ist, dass UIColor im Grunde alles tut, was zum Teufel es will. Selbst wenn Ihre View Controller den Dunkelmodus blockieren, weiß UIColor nicht, wo oder wie Sie ihn verwenden, und kann sich daher nicht anpassen. Infolgedessen können Sie es korrekt abrufen, aber dann wird es irgendwann in der Zukunft auf Sie zurückgreifen. Vielleicht bald, vielleicht später. Um dies zu umgehen, müssen Sie es zweimal mit einer CGColor zuweisen und in eine statische Farbe umwandeln. Dies bedeutet, wenn Ihr Benutzer zurückkehrt und den Dunkelmodus auf Ihrer Einstellungsseite wieder aktiviert (die Idee hier ist, dass dies funktioniert, damit der Benutzer über den Rest des Systems hinaus die Kontrolle über Ihre App hat), all diese statischen Farben müssen ersetzt werden. Bisher muss dies jemand anderes lösen. Der einfachste Weg, dies zu tun, besteht darin, einen Standardwert festzulegen, den Sie ' Wenn Sie den Dunkelmodus wieder deaktivieren, teilen Sie ihn durch Null, um die App zum Absturz zu bringen, da Sie sie nicht beenden können, und weisen Sie den Benutzer an, sie einfach neu zu starten. Das verstößt wahrscheinlich auch gegen die Richtlinien des App Stores, aber es ist eine Idee.

Die UIColor-Kategorie muss nicht verfügbar gemacht werden. Sie ruft lediglich colorNamed auf: ... Wenn Sie der DarkMode ViewController-Klasse nicht mitgeteilt haben, dass sie den Dunkelmodus blockieren soll, funktioniert sie wie erwartet einwandfrei. Der Versuch, etwas Elegantes anstelle des Standard-Apple-Sphaghetti-Codes zu erstellen, bedeutet, dass Sie den größten Teil Ihrer App ändern müssen, wenn Sie den Dunkelmodus programmgesteuert deaktivieren oder umschalten möchten. Jetzt weiß ich nicht, ob es eine bessere Möglichkeit gibt, die Info.plist programmgesteuert zu ändern, um den Dunkelmodus nach Bedarf auszuschalten. Soweit ich weiß, ist dies eine Funktion zur Kompilierungszeit, und danach sind Sie entbeint.

Hier ist der Code, den Sie benötigen. Sollte vorbeischauen und nur die eine Methode verwenden, um den UI-Stil festzulegen oder den Standard im Code festzulegen. Es steht Ihnen frei, dies für jeden Zweck zu verwenden, zu ändern und zu tun, was immer Sie wollen, und es wird keine Garantie gegeben, und ich weiß nicht, ob es den App Store passieren wird. Verbesserungen sind sehr willkommen.

Faire Warnung Ich verwende kein ARC oder andere Handholding-Methoden.

////// 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

Es gibt eine Reihe von Dienstprogrammfunktionen, mit denen Methoden ausgetauscht werden. Separate Datei. Dies ist jedoch Standard und Sie können ähnlichen Code überall finden.

// 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;
    }
}

Ich kopiere und füge dies aus ein paar Dateien ein, da q-runtime.h meine wiederverwendbare Bibliothek ist und dies nur ein Teil davon ist. Wenn etwas nicht kompiliert werden kann, lass es mich wissen.

dbquarrel
quelle
Sie haben kein Pech, wenn es darum geht, das Verhalten von UIColor zu steuern, wie in dieser Frage erläutert
raven_raven