È possibile disattivare la modalità oscura su iOS 13?


296

Gran parte della mia app è costituita da visualizzazioni Web per fornire funzionalità non ancora disponibili tramite implementazioni native. Il team web non ha intenzione di implementare un tema oscuro per il sito Web. Pertanto, la mia app apparirà un po 'metà / metà con il supporto della modalità Dark su iOS 13.

È possibile disattivare il supporto della modalità oscura in modo tale che la nostra app mostri sempre la modalità luce per abbinare il tema del sito Web?


70
Impostato UIUserInterfaceStylesu Lightnel tuo Info.Plist. Vedi developer.apple.com/library/archive/documentation/General/…
Tieme

1
Grazie per avermelo chiesto - per tutti noi. Molte app da esaminare. Ciò è necessario per far funzionare le app fino a quando l'interruttore non è pronto.
user3741598

import Foundation import UIKit extension UIViewController {sovrascrive open func awakeFromNib () {super.awakeFromNib () se #available (iOS 13.0, *) {// Adotta sempre uno stile di interfaccia leggero. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour

1
aggiungi semplicemente UIUserInterfaceStyle in plist. è così facile
Fattie l'

Durante l'invio dell'app all'app store, Apple accetta a causa di UIUserInterfaceStyle in modalità Light.
Kiran,

Risposte:


684

Innanzitutto, ecco la voce di Apple relativa alla disattivazione della modalità oscura. Il contenuto di questo link è scritto per Xcode 11 e iOS 13 :

Questa sezione si applica all'utilizzo di Xcode 11


Se desideri annullare la tua INTERA applicazione

Approccio n. 1

Utilizzare la chiave seguente nel file info.plist :

UIUserInterfaceStyle

E assegnagli un valore di Light.

Il XML per l' UIUserInterfaceStyleassegnazione:

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

Approccio n. 2

Puoi impostare overrideUserInterfaceStylela windowvariabile dell'app .

A seconda di come è stato creato il progetto, questo può essere nel AppDelegatefile o nel file SceneDelegate.

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


Se desideri disattivare UIViewController su base individuale

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

Documentazione Apple per overrideUserInterfaceStyle

Come apparirà il codice sopra in Xcode 11:

inserisci qui la descrizione dell'immagine

Questa sezione si applica all'utilizzo di Xcode 10.x.


Se stai usando Xcode 11 per l'invio, puoi tranquillamente ignorare tutto ciò che si trova al di sotto di questa linea.

Poiché l'API pertinente non esiste in iOS 12, si verificheranno errori quando si tenta di utilizzare i valori forniti sopra:

Per l'impostazione overrideUserInterfaceStylenel tuoUIViewController

inserisci qui la descrizione dell'immagine

Se desideri disattivare UIViewController su base individuale

Questo può essere gestito in Xcode 10 testando la versione del compilatore e la versione iOS:

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

Se desideri annullare la tua INTERA applicazione

Puoi modificare lo snippet di cui sopra in modo che funzioni contro l'intera applicazione per Xcode 10, aggiungendo il seguente codice al tuo AppDelegatefile.

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

Tuttavia, l'impostazione del plist fallirà quando si utilizza Xcode versione 10.x:

inserisci qui la descrizione dell'immagine

Ringraziamo @Aron Nelson , @Raimundas Sakalauskas , @NSLeader e rmaddy per aver migliorato questa risposta con il loro feedback.


2
La luce UIUserInterfaceStyle è bloccata quando aggiorni / carichi la tua app ora. Viene contrassegnato come una voce plist non valida. (Chiave plist non valida)
Aron Nelson,

2
Questo non verrà compilato con iOS SDK 12 (attualmente l'ultimo SDK stabile). Vedi stackoverflow.com/a/57521901/2249485 per la soluzione che funzionerà anche con iOS 12 SDK.
Raimundas Sakalauskas,

Questo è così ingiusto che la domanda che ha molte più opinioni della "domanda originale" è bloccata per fornire risposte. :(
Raimundas Sakalauskas,

7
Invece di impostare overrideUserInterfaceStylein viewDidLoadogni controller di visualizzazione, è possibile impostarlo una volta nella finestra principale dell'app. Molto più facile se vuoi che l'intera app si comporti in un modo.
rmaddy,

2
Utilizzare #if compiler(>=5.1)invece responds(to:)esetValue
NSLeader il

162

Secondo la sessione di Apple su "Implementazione della modalità oscura su iOS" ( https://developer.apple.com/videos/play/wwdc2019/214/ che inizia alle 31:13) è possibile impostare overrideUserInterfaceStylesu UIUserInterfaceStyleLighto UIUserInterfaceStyleDarksu qualsiasi controller di visualizzazione o vista , che verrà utilizzato in traitCollectionper qualsiasi subview o controller di visualizzazione.

Come già detto da SeanR, è possibile impostare UIUserInterfaceStylea Lighto Darknel file di plist della tua app per cambiare questo per tutta la vostra applicazione.


17
Se imposti la chiave UIUserInterfaceStyle la tua app verrà rifiutata nell'App store
Sonius

2
Apple ha rifiutato con il codice di errore ITMS-90190 forum.developer.apple.com/thread/121028
PRASAD1240

11
È molto probabile che il rifiuto accada perché l'SDK di iOS 13 non è ancora uscito dalla beta. Penso che dovrebbe funzionare non appena Xcode 11 GM è disponibile.
Dorbeetle,

2
@dorbeetle non è vero, ho caricato la mia app con questa chiave con successo come 1 mese fa con Xcode 10. I rifiuti si verificano di recente. Sembra una sorta di nuova strategia Apple.
Steven

1
Sta ancora succedendo. Xcode GM2 ha restituito un errore di firma dell'app. Xcode 10.3 restituito: "Chiave Info.plist non valida. La chiave 'UIUserInterfaceStyle' nel file Payload / Galileo.appInfo.plist non è valida."
Evgen Bodunov,

64

Se non stai utilizzando Xcode 11 o versioni successive (i, e iOS 13 o versioni successive SDK), l'app non ha automaticamente scelto di supportare la modalità oscura. Quindi, non è necessario disattivare la modalità oscura.

Se si utilizza Xcode 11 o versioni successive, il sistema ha abilitato automaticamente la modalità scura per l'app. Esistono due approcci per disabilitare la modalità oscura in base alle proprie preferenze. È possibile disabilitarlo completamente o disabilitarlo per qualsiasi controller specifico per finestra, vista o vista.

Disabilita la modalità oscura per la tua app

Puoi disabilitare la modalità oscura includendo la UIUserInterfaceStylechiave con un valore come Lightnel file Info.plist della tua app. Ciò ignora le preferenze dell'utente e applica sempre un aspetto leggero alla tua app.
UIUserInterfaceStyle as Light

Disabilita la modalità dark per Window, View o View Controller

È possibile forzare la visualizzazione dell'interfaccia in uno stile chiaro o scuro impostando la overrideUserInterfaceStyleproprietà della finestra, della vista o del controller appropriati.

Visualizza controller:

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

Visualizzazioni:

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

Finestra:

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

Nota: Apple consiglia vivamente di supportare la modalità oscura nella tua app. Quindi, puoi disabilitare temporaneamente solo la modalità oscura.

Maggiori informazioni qui: Scelta di uno stile di interfaccia specifico per la tua app iOS


34

********** Il modo più semplice per Xcode 11 e versioni successive ***********

Aggiungi questo a info.plist prima </dict></plist>

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

questa soluzione fallirà quando si invierà l'app su Xcode 10.x
Tawfik Bouabid

27

Penso di aver trovato la soluzione. Inizialmente l'ho messo insieme da UIUserInterfaceStyle - Elenco proprietà informazioni e UIUserInterfaceStyle - UIKit , ma ora l'ho trovato effettivamente documentato in Scelta di uno stile di interfaccia specifico per la tua app iOS .

In info.plist, impostare UIUserInterfaceStyle( Stile interfaccia utente ) su 1 ( UIUserInterfaceStyle.light).

EDIT: secondo la risposta di dorbeetle, UIUserInterfaceStylepotrebbe essere un'impostazione più appropriata per Light.


L'applicazione della modalità oscura impostando il valore su 2 non funziona però:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen

3
Avere questa chiave nella lista comporterà un rifiuto dell'App Store.
José,

1
AppStore non rifiuta più questa proprietà in plist.info. Ho messo "Dark" (in maiuscolo) poiché la nostra app è già scura. Nessun problema. Questo ci consente di utilizzare correttamente i controlli di sistema.
Nickdnk,

@nickdnk Penso che tu abbia creato la tua app con Xcode 11, che è raccomandato da Apple.
DawnSong,

1
Si l'ho fatto. Non cambia il fatto che Apple accetti questo parametro nel plist, che era quello che stavo cercando di chiarire.
Nickdnk,

23

La risposta sopra funziona se si desidera annullare l'intera app. Se stai lavorando sulla libreria con interfaccia utente e non hai il lusso di modificare .plist, puoi farlo anche con il codice.

Se stai compilando con iOS 13 SDK, puoi semplicemente usare il seguente codice:

Swift:

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

Obj-C:

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

TUTTAVIA , se vuoi che il tuo codice venga compilato anche su iOS 12 SDK (che al momento è ancora l'ultimo SDK stabile), dovresti ricorrere all'uso dei selettori. Codice con selettori:

Swift (XCode mostrerà avvisi per questo codice, ma per ora è l'unico modo di farlo poiché la proprietà non esiste in SDK 12 quindi non verrà compilata):

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"];
    }
}

Sarà meglio se si specifica a quale proprietà overrideUserInterfaceStyleappartiene.
DawnSong,

12

Ultimo aggiornamento-

Se stai usando Xcode 10.x, l'impostazione predefinita UIUserInterfaceStyleè lightper iOS 13.x. Quando eseguito su un dispositivo iOS 13, funzionerà solo in modalità luce.

Non è necessario aggiungere esplicitamente la UIUserInterfaceStylechiave nel file Info.plist, aggiungendola si verificherà un errore quando si convalida l'app, dicendo:

Chiave Info.plist non valida. La chiave "UIUserInterfaceStyle" nel file Payload / AppName.appInfo.plist non è valida.

Aggiungi la UIUserInterfaceStylechiave nel file Info.plist solo quando usi Xcode 11.x.


1
Questo non ha nulla a che fare con Xcode 10 o 11. Se l'utente distribuisce l'app dal modulo Xcode 10 e non si occupa della modalità oscura, l'app installata in iPhone 11, Pro o Pro Max avrà problemi di modalità oscura. devi aggiornare a Xcode 11 e risolvere questo problema.
Niranjan Molkeri,

3
@NiranjanMolkeri Questo non ha nulla a che fare con gli iPhone più recenti. Riguarda la modalità Dark su iOS 13. Nelle precedenti versioni della versione beta di iOS 13 l'interfaccia utente presentava problemi di modalità dark se non gestita in modo esplicito. Ma nell'ultima versione, questo è stato risolto. Se stai usando XCode 10, UIUserInterfaceStyle predefinito è leggero per iOS13. Se stai usando Xode11 devi gestirlo.
kumarsiddharth123,

Avrai problemi se carichi un'app su TestFligth usando Xcode 10.3 e il plist include la chiave UIUserInterfaceStyle. Dirà che si tratta di un file plist non valido. Devi rimuoverlo se
stai compilando

9

Se aggiungerai la UIUserInterfaceStylechiave al file plist, probabilmente Apple rifiuterà la build di rilascio come menzionato qui: https://stackoverflow.com/a/56546554/7524146 Comunque è fastidioso dire esplicitamente a ciascun ViewController self.overrideUserInterfaceStyle = .light . Ma puoi usare questo codice di pace una volta per il tuo windowoggetto root :

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

Nota solo che non puoi farlo all'interno application(application: didFinishLaunchingWithOptions:)perché per questo selettore non risponderà truein quella fase iniziale. Ma puoi farlo più tardi. È semplicissimo se stai utilizzando un'app personalizzata AppPresentero di AppRouterclasse nella tua app invece di avviare automaticamente l'interfaccia utente in AppDelegate.


9

Puoi disattivare la modalità oscura nell'intera applicazione in Xcode 11:

  1. Vai a Info.plist
  2. Aggiungi muggito come

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

Info.plist apparirà come sotto ...

inserisci qui la descrizione dell'immagine


1
non funziona per Xcode versione 11.3.1 (11C504) per qualche motivo
Andrew

7

- Per l'intera app (finestra):

window!.overrideUserInterfaceStyle = .light

È possibile ottenere la finestra da SceneDelegate

- Per un singolo ViewController:

viewController.overrideUserInterfaceStyle = .light

È possibile impostare qualsiasi viewController, anche all'interno della viewController esso auto

- Per una singola vista:

view.overrideUserInterfaceStyle = .light

È possibile impostare qualsiasi view, anche all'interno della vista si auto

Potrebbe essere necessario utilizzare if #available(iOS 13.0, *) { ,,, }se si supportano versioni precedenti di iOS.


6

A parte le altre risposte, dalla mia comprensione di quanto segue, devi solo prepararti per la modalità Dark durante la compilazione con iOS 13 SDK (usando XCode 11).

Il sistema presuppone che le app collegate all'SDK di iOS 13 o versioni successive supportino aspetti sia chiari che scuri. In iOS, si specifica l'aspetto specifico desiderato assegnando uno stile di interfaccia specifico alla finestra, alla vista o al controller di visualizzazione. Puoi anche disabilitare completamente il supporto per Dark Mode usando un tasto Info.plist.

collegamento


2

Sì, puoi saltare aggiungendo il seguente codice in viewDidLoad:

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

2

La mia app non supporta la modalità scura al momento e utilizza un colore della barra delle applicazioni chiaro. Sono stato in grado di forzare il contenuto della barra di stato su testo e icone scuri aggiungendo la seguente chiave al mio Info.plist:

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

Trova gli altri possibili valori qui: https://developer.apple.com/documentation/uikit/uistatusbarstyle


2

Versione Objective-c

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

1

Ecco alcuni suggerimenti e trucchi che puoi utilizzare nella tua app per supportare o bypassare la modalità oscura.

Primo consiglio: per sovrascrivere lo stile ViewController

puoi sovrascrivere lo stile dell'interfaccia di UIViewController con

1: overrideUserInterfaceStyle = .dark // Per la modalità oscura

2: overrideUserInterfaceStyle = .light // Per la modalità luce

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

Secondo suggerimento: aggiunta di una chiave in info.plist

Puoi semplicemente aggiungere una nuova chiave

UIUserInterfaceStyle

nella tua info.plist della tua app e imposta il suo valore su Chiaro o Scuro. questo sostituirà lo stile predefinito dell'app al valore fornito. Non è necessario aggiungere overrideUserInterfaceStyle = .light questa riga in ogni viewController, solo una riga in info.plist è tutto.


1

Basta semplicemente aggiungere la seguente chiave nel tuo info.plistfile:

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

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

Puoi spiegare un po 'come questa risposta risolverà il problema, invece di pubblicare una risposta solo in codice.
Arun Vinoth,

Sì certo @ArunVinoth In IOS 13 viene introdotta la modalità dark, quindi se la tua destinazione di distribuzione è inferiore a 13, usa il codice sopra, altrimenti puoi usare una semplice istruzione scritta in if block.
Talha Rasool,

1

Swift 5

Due modi per passare dalla modalità oscura a quella chiara:

1- info.plist

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

2- Programmaticamente

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

0

Vorrei utilizzare questa soluzione poiché la proprietà della finestra può essere modificata durante il ciclo di vita dell'app. Quindi è necessario ripetere l'assegnazione di "overrideUserInterfaceStyle = .light". UIWindow.appearance () ci consente di impostare il valore predefinito che verrà utilizzato per gli oggetti UIWindow appena creati.

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

Aggiungi queste righe nel file info.plist:

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

Ciò imporrà l'esecuzione dell'app solo in modalità luce.


Questo è già stato commentato e risposto molte volte. Anche la risposta accettata sta suggerendo questo. Pertanto questo commento non aggiunge alcuna nuova informazione.
JeroenJK,

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
Per favore, aggiungi qualche spiegazione alla tua risposta modificandola, in modo che altri possano imparare da essa
Nico Haase,

0

Puoi fare: aggiungi questa nuova chiave UIUserInterfaceStyle a Info.plist e imposta il suo valore su Light. e verifica che il controller di avviso venga visualizzato in modalità luce.

UIUserInterfaceStyle Light Se si attiva la modalità luce / buio nell'intera applicazione, indipendentemente dalle impostazioni dell'utente, aggiungendo la chiave UIUserInterfaceStyle al file Info.plist e impostando il valore su Chiaro o Scuro.


0

Questa domanda ha così tante risposte, piuttosto usandola info.plistpuoi impostarla in AppDelegatequesto modo:

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

Test su Xcode 11.3, iOS 13.3


-8

In realtà ho appena scritto del codice che ti permetterà di disattivare globalmente la modalità dark nel codice senza dover putz con ogni singolo controller viw nella tua applicazione. Questo può probabilmente essere perfezionato per rinunciare a una classe per classe gestendo un elenco di classi. Per me, ciò che voglio è che i miei utenti vedano se a loro piace l'interfaccia in modalità oscura per la mia app e, se non gli piace, possono disattivarla. Ciò consentirà loro di continuare a utilizzare la modalità oscura per il resto delle loro applicazioni.

La scelta dell'utente è buona (Ahem, guardandoti Apple, è così che dovresti averlo implementato).

Quindi, come funziona è che è solo una categoria di UIViewController. Quando viene caricato, sostituisce il metodo viewDidLoad nativo con uno che controllerà un flag globale per vedere se la modalità dark è disabilitata per tutto o no.

Poiché viene attivato al caricamento di UIViewController, dovrebbe avviarsi automaticamente e disabilitare la modalità scura per impostazione predefinita. Se questo non è quello che vuoi, allora devi entrare lì da qualche parte in anticipo e impostare il flag, oppure semplicemente impostare il flag predefinito.

Non ho ancora scritto nulla per rispondere all'utente attivando o disattivando la bandiera. Quindi questo è fondamentalmente un codice di esempio. Se vogliamo che l'utente interagisca con questo, tutti i controller di visualizzazione dovranno essere ricaricati. Non so come farlo a mano, ma probabilmente l'invio di una notifica farà il trucco. Quindi in questo momento, questo on / off globale per la modalità dark funzionerà solo all'avvio o al riavvio dell'app.

Ora, non è sufficiente provare a disattivare la modalità oscura in ogni singolo controller MFING view nella tua enorme app. Se stai utilizzando le risorse di colore, sei completamente disossato. Per oltre 10 anni abbiamo capito che gli oggetti immutabili sono immutabili. I colori che ottieni dal catalogo delle risorse colore dicono che sono UIColor ma sono colori dinamici (mutabili) e cambieranno sotto di te quando il sistema passa dalla modalità scura a quella chiara. Questa dovrebbe essere una caratteristica. Ma ovviamente non c'è nessun maestro a chiedere a queste cose di smettere di fare questo cambiamento (per quanto ne so adesso, forse qualcuno può migliorarlo).

Quindi la soluzione è in due parti:

  1. una categoria pubblica su UIViewController che offre alcuni metodi di utilità e convenienza ... ad esempio, non credo che apple abbia pensato al fatto che alcuni di noi si mescolano nel codice Web nelle nostre app. Pertanto, abbiamo fogli di stile che devono essere attivati ​​in base alla modalità scuro o chiaro. Quindi, o hai bisogno di costruire un qualche tipo di oggetto di foglio di stile dinamico (che sarebbe buono) o semplicemente chiedere quale sia lo stato attuale (cattivo ma facile).

  2. questa categoria durante il caricamento sostituirà il metodo viewDidLoad della classe UIViewController e intercetterà le chiamate. Non so se questo infrange le regole dell'app store. Se lo fa, ci sono altri modi per aggirare questo probabilmente, ma puoi considerarlo una prova di concetto. Ad esempio, puoi creare una sottoclasse di tutti i tipi di controller di visualizzazione principali e far ereditare tutti i controller di visualizzazione da quelli, quindi puoi utilizzare l'idea della categoria DarkMode e chiamarci per forzare la disattivazione di tutti i controller di visualizzazione. È più brutto ma non infrange alcuna regola. Preferisco usare il runtime perché è quello che ha fatto il runtime. Quindi nella mia versione aggiungi semplicemente la categoria, imposti una variabile globale sulla categoria per decidere se bloccare o meno la modalità oscura, e lo farà.

  3. Non sei ancora fuori dal bosco, come già detto, l'altro problema è che UIColor sta praticamente facendo quello che diavolo vuole. Quindi, anche se i controller della vista bloccano la modalità dark UIColor non sa dove o come lo stai usando, quindi non può adattarsi. Di conseguenza, puoi recuperarlo correttamente, ma poi ti verrà ripristinato in futuro. Forse presto, forse dopo. Quindi il modo per aggirare questo è allocandolo due volte usando un CGColor e trasformandolo in un colore statico. Ciò significa che se l'utente torna indietro e riattiva la modalità oscura nella pagina delle impostazioni (l'idea qui è di far funzionare questo in modo che l'utente abbia il controllo sulla tua app sopra e sopra il resto del sistema), tutti quei colori statici è necessario sostituire. Finora questo è lasciato a qualcun altro da risolvere. Il modo più semplice per farlo è quello di fare un default che tu ' disattivando la modalità oscura, dividi per zero per arrestare l'app in quanto non puoi uscire e dire all'utente di riavviarla. Ciò probabilmente viola anche le linee guida dell'App Store, ma è un'idea.

La categoria UIColor non ha bisogno di essere esposta, funziona solo chiamando colorNamed: ... se non hai detto alla classe DarkMode ViewController di bloccare la modalità oscura, funzionerà perfettamente come previsto. Cercare di creare qualcosa di elegante al posto del codice standard di Apple Sphaghetti, il che significa che dovrai modificare la maggior parte della tua app se vuoi disattivare programmativamente la modalità oscura o attivarla. Ora non so se esiste un modo migliore di modificare programmaticamente Info.plist per disattivare la modalità oscura, se necessario. Per quanto riguarda la mia comprensione, questa è una funzione di compilazione del tempo e, successivamente, vieni disossato.

Quindi ecco il codice che ti serve. Dovrebbe essere inserito e usare solo un metodo per impostare lo stile dell'interfaccia utente o impostare il valore predefinito nel codice. Sei libero di usare, modificare, fare ciò che vuoi con questo per qualsiasi scopo e non viene data alcuna garanzia e non so se passerà all'app store. Miglioramenti molto graditi.

Avviso equo Non utilizzo ARC o altri metodi di gestione.

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

Esiste una serie di funzioni di utilità che utilizza per eseguire lo scambio di metodi. File separato. Questa è roba standard e puoi trovare codice simile ovunque.

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

Sto copiando e incollando questo da un paio di file poiché q-runtime.h è la mia libreria riutilizzabile e questa è solo una parte di essa. Se qualcosa non si compila fammi sapere.


Non siete fuori di fortuna quando si tratta di un comportamento controling UIColor, come discusso in questa domanda: stackoverflow.com/questions/56487679/...
raven_raven
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.