İOS 13'te karanlık moddan çıkmak mümkün mü?


297

Uygulamamın büyük bir kısmı, yerel uygulamalar aracılığıyla henüz kullanılamayan işlevler sağlamak için web görünümlerinden oluşmaktadır. Web ekibinin web sitesi için karanlık bir tema uygulama planı yoktur. Bu nedenle, uygulamam iOS 13'teki Karanlık Mod desteğiyle yarım / yarım görünecek.

Uygulamamız her zaman web sitesi temasına uygun ışık modu gösterecek şekilde Karanlık Mod desteğinden vazgeçmek mümkün mü?


71
Set UIUserInterfaceStyleiçin Lightsizin info.plist içinde. Bkz. Developer.apple.com/library/archive/documentation/General/…
Tieme

1
Sorduğunuz için teşekkürler - hepimiz için. Geçmesi gereken birçok uygulama. Geçiş hazır olana kadar uygulamaların çalışmasını sağlamak için bu gereklidir.
user3741598

import Foundation import UIKit uzantısı UIViewController {açık fonu geçersiz kıl awakeFromNib () {super.awakeFromNib () #available (iOS 13.0, *) {// Her zaman bir ışık arabirimi stili benimseyin. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour

1
UIUserInterfaceStyle'ı plist'e eklemeniz yeterlidir . bu kadar kolay
Fattie

Uygulamayı uygulama mağazasına gönderirken Işık modunda UIUserInterfaceStyle nedeniyle elma kabul edin.
kiran

Yanıtlar:


684

İlk olarak, Apple'ın karanlık moddan çıkma ile ilgili girişi . Bu bağlantıdaki içerik Xcode 11 ve iOS 13 için yazılmıştır :

Bu bölüm Xcode 11 kullanımı için geçerlidir


ENTIRE başvurunuzu iptal etmek isterseniz

Yaklaşım # 1

İnfo.plist dosyanızda aşağıdaki anahtarı kullanın :

UIUserInterfaceStyle

Ve ona bir değer atayın Light.

XML için UIUserInterfaceStyleatama:

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

Yaklaşım # 2

overrideUserInterfaceStyleUygulamanın windowdeğişkenine göre ayarlayabilirsiniz .

Projenizin nasıl oluşturulduğuna bağlı olarak, bu AppDelegatedosyada veya SceneDelegate.

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


UIViewController cihazınızı bireysel olarak devre dışı bırakmak istiyorsanız

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 için Apple belgeleri

Yukarıdaki kod Xcode 11'de nasıl görünecek:

resim açıklamasını buraya girin

Bu bölüm Xcode 10.x kullanımı için geçerlidir


Sunumunuz için Xcode 11 kullanıyorsanız, bu satırın altındaki her şeyi güvenle yok sayabilirsiniz.

İlgili API, iOS 12'de bulunmadığından, yukarıda sağlanan değerleri kullanmaya çalışırken hatalarla karşılaşırsınız:

Ayarlamak overrideUserInterfaceStyleiçinUIViewController

resim açıklamasını buraya girin

UIViewController cihazınızı bireysel olarak devre dışı bırakmak istiyorsanız

Bu, derleyici sürümünü ve iOS sürümünü test ederek Xcode 10'da ele alınabilir:

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

ENTIRE başvurunuzu iptal etmek isterseniz

Aşağıdaki kod parçasını, AppDelegatedosyanıza aşağıdaki kodu ekleyerek Xcode 10 uygulamasının tamamında çalışacak şekilde değiştirebilirsiniz .

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

Ancak, Xcode sürüm 10.x kullanılırken plist ayarı başarısız olur:

resim açıklamasını buraya girin

Kredi @Aron Nelson , @Raimundas Sakalauskas , @NSLeader ve rmaddy onların geribildirim ile bu cevabı geliştirmek için.


2
Uygulamanızı şimdi güncellerken / yüklerken UIUserInterfaceStyle ışığı engellendi. Geçersiz bir plist girişi olarak işaretlenir. (Geçersiz plist anahtarı)
Aron Nelson

2
Bu, iOS SDK 12'ye (şu anda en son kararlı SDK) karşı derlenmeyecektir. İOS 12 SDK ile de çalışacak çözüm için stackoverflow.com/a/57521901/2249485 adresine bakın .
Raimundas Sakalauskas

Bu o kadar haksızdır ki, "orijinal soru" dan çok daha fazla görüşe sahip olan soru cevaplar için kilitlidir. :(
Raimundas Sakalauskas

7
Yerine ayar overrideUserInterfaceStyleiçinde viewDidLoadher görünümü denetleyicisi, uygulamanın ana penceresinde bir kez ayarlayabilirsiniz. Tüm uygulamanın bir şekilde davranmasını istiyorsanız çok daha kolay.
rmaddy

2
#if compiler(>=5.1)Bunun yerine kullanın responds(to:)vesetValue
NSLeader

162

"İOS'ta Uygulama Koyu Modu" (Apple'ın oturumuna göre https://developer.apple.com/videos/play/wwdc2019/214/ 31:13 başlayarak) o sette mümkündür overrideUserInterfaceStyleetmek UIUserInterfaceStyleLightveya UIUserInterfaceStyleDarkherhangi bir görünüm denetleyicisi veya görünüm , traitCollectionherhangi bir alt görünüm veya görünüm denetleyicisi için kullanılacak.

Zaten SeanR tarafından belirtildiği gibi, Belirleyebileceğiniz UIUserInterfaceStyleiçin Lightya Darkda tüm uygulamalar için bu değiştirmek için uygulamanızın plist dosyasında.


17
UIUserInterfaceStyle anahtarını ayarlarsanız, uygulamanız App Store'da reddedilir
Sonius

2
Apple, ITMS-90190 hata kodu forums.developer.apple.com/thread/121028
PRASAD1240

11
Reddetme büyük olasılıkla gerçekleşir, çünkü iOS 13 SDK henüz beta sürümünden çıkmamıştır. Ben Xcode 11 GM hazır olur olmaz çalışması gerektiğini düşünüyorum.
dorbeetle

2
@dorbeetle doğru değil, Xcode 10 ile 1 ay önce olduğu gibi bu anahtarla uygulamamı başarıyla yükledim. Reddedilenler son zamanlarda gerçekleşti. Bazı yeni Apple stratejileri gibi görünüyor.
steven

1
Hala oluyor. Xcode GM2, uygulama imzalama hatası döndürdü. Xcode 10.3 şunu döndürdü: "Geçersiz Info.plist Anahtarı. Payload / Galileo.appInfo.plist dosyasındaki 'UIUserInterfaceStyle' anahtarı geçerli değil."
Evgen Bodunov

64

Xcode 11 veya daha yenisini (i, e iOS 13 veya üstü SDK) kullanmıyorsanız, uygulamanız otomatik olarak karanlık modu desteklemeyi seçmedi. Yani, karanlık moddan çıkmanıza gerek yok.

Xcode 11 veya üstünü kullanıyorsanız, sistem uygulamanız için otomatik olarak karanlık modu etkinleştirmiştir. Tercihinize bağlı olarak karanlık modu devre dışı bırakmak için iki yaklaşım vardır. Tamamen devre dışı bırakabilir veya herhangi bir pencere, görünüm veya görünüm denetleyicisi için devre dışı bırakabilirsiniz.

Uygulamanız için Karanlık Modu Tamamen Devre Dışı Bırakın

Karanlık modu , uygulamanızın Info.plist dosyasında UIUserInterfaceStyleolduğu gibi bir değere sahip anahtarı Lightekleyerek devre dışı bırakabilirsiniz . Bu, kullanıcının tercihini göz ardı eder ve uygulamanıza her zaman hafif bir görünüm uygular.
Işık olarak UIUserInterfaceStyle

Pencere, Görünüm veya Görünüm Denetleyicisi için karanlık modu devre dışı bırakma

overrideUserInterfaceStyleUygun pencere, görünüm veya görünüm denetleyicisinin özelliğini ayarlayarak arabiriminizi her zaman açık veya koyu bir stilde görünmeye zorlayabilirsiniz .

Denetleyicileri görüntüle:

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

Görüntüleme:

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

Pencere:

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

Not: Apple, uygulamanızda karanlık modu desteklemeyi şiddetle tavsiye eder. Böylece, karanlık modu yalnızca geçici olarak devre dışı bırakabilirsiniz.

Daha fazla bilgiyi buradan edinebilirsiniz: iOS Uygulamanız için Belirli Bir Arayüz Stili Seçme


34

********** Xcode 11 ve üstü için en kolay yol ***********

Bunu daha önce info.plist'e ekle </dict></plist>

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

Bu çözüm, Xcode 10.x üzerinde uygulama gönderildiğinde başarısız olacaktır
Tawfik Bouabid

27

Sanırım çözümü buldum. Başlangıçta UIUserInterfaceStyle - Bilgi Özellik Listesi ve UIUserInterfaceStyle - UIKit'ten bir araya getirdim , ancak şimdi aslında iOS uygulamanız için belirli bir arayüz stili seçme konusunda belgelenmiş bulduk .

Senin içinde info.plist, set UIUserInterfaceStyle( Kullanıcı Arayüzü Stil için) 1 ( UIUserInterfaceStyle.light).

DÜZENLEME: Dorbeetle'ın cevabına göre, daha uygun bir ayar UIUserInterfaceStyleolabilir Light.


Değeri 2 olarak ayarlayarak karanlık modu [UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
zorlamak

3
Bu anahtarın plist'e sahip olması App Store'un reddedilmesine neden olacaktır.
José

1
AppStore artık plist.info dosyasında bu özelliği reddetmiyor. Bizim app zaten karanlık olduğundan "Karanlık" (büyük harf) koymak. Sorun yok. Bu, sistem kontrollerini doğru şekilde kullanmamızı sağlar.
nickdnk

@nickdnk Uygulamanızı Apple tarafından önerilen Xcode 11 ile oluşturduğunuzu düşünüyorum.
DawnSong

1
Evet yaptım. Apple'ın bu parametreyi plist'te kabul ettiği gerçeğini değiştirmez, ki bu açıklamaya çalıştığım şeydi.
nickdnk

23

Yukarıdaki yanıt, tüm uygulamayı devre dışı bırakmak istiyorsanız çalışır. Kullanıcı arabirimi olan bir lib üzerinde çalışıyorsanız ve .plist düzenleme lüksünüz yoksa, bunu kod yoluyla da yapabilirsiniz.

İOS 13 SDK'ya karşı derliyorsanız, aşağıdaki kodu kullanabilirsiniz:

Swift:

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

Obj-C:

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

Ancak , kodunuzun iOS 12 SDK'ya karşı da derlenmesini istiyorsanız (şu anda hala en son kararlı SDK'dır), seçicileri kullanmaya başvurmalısınız. Seçicili kod:

Swift (XCode bu kod için uyarılar gösterecektir, ancak şimdilik SDK 12'de özellik bulunmadığından bunu yapmanın tek yolu bu yüzden derlenmeyecektir):

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

Mülkün overrideUserInterfaceStyleait olduğunu belirtmeniz daha iyi olacaktır .
DawnSong

12

Son Güncelleme-

Xcode 10.x kullanıyorsanız, varsayılan UIUserInterfaceStyleolan lightiOS 13.x. için Bir iOS 13 cihazında çalıştırıldığında, yalnızca Işık Modunda çalışır.

UIUserInterfaceStyleAnahtarı Info.plist dosyasına açıkça eklemenize gerek yoktur , uygulamanızı doğruladığınızda bir hata verir ve şunu söyler:

Geçersiz Info.plist Anahtarı. Payload / AppName.appInfo.plist dosyasındaki 'UIUserInterfaceStyle' anahtarı geçerli değil.

UIUserInterfaceStyleAnahtarı yalnızca Xcode 11.x kullanırken Info.plist dosyasına ekleyin.


1
Bunun Xcode 10 veya 11 ile bir ilgisi yok. Kullanıcı Xcode 10 uygulama formunu dağıtır ve karanlık modla ilgilenmezse, iPhone 11, Pro veya Pro Max'e yüklendiğinde uygulama karanlık mod sorunları yaşayacaktır. Xcode 11'e güncellemeniz ve bu sorunu gidermeniz gerekir.
Niranjan Molkeri

3
@NiranjanMolkeri Bunun yeni iPhone'larla bir ilgisi yok. Bu, iOS 13'teki Karanlık mod hakkında. Önceki iOS 13 beta sürümü uygulamalarında kullanıcı arayüzü açıkça ele alınmazsa karanlık mod sorunları yaşıyordu. Ancak en son sürümde bu düzeltildi. XCode 10 kullanıyorsanız, varsayılan UIUserInterfaceStyle iOS13 için açıktır. Xode11 kullanıyorsanız, işlemeniz gerekir.
kumarsiddharth123

Xcode 10.3 kullanarak TestFligth'e bir uygulama yüklerseniz ve plist UIUserInterfaceStyle anahtarını içeriyorsa sorun yaşarsınız. Geçersiz bir plist dosyası olduğunu söyleyecektir. Xcode 10'da bina yapıyorsanız veya Xcode 11 kullanarak yükleme
yapmalısınız

9

UIUserInterfaceStylePlist dosyasına anahtar ekleyecekseniz , muhtemelen Apple, burada belirtildiği gibi sürüm derlemesini reddedecektir: https://stackoverflow.com/a/56546554/7524146 Her neyse, her ViewController'a açıkça söylemek rahatsız edici self.overrideUserInterfaceStyle = .light. Ancak bu kod huzuru kök windownesneniz için bir kez kullanabilirsiniz :

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

Bunu içeride yapamayacağınıza dikkat edin, application(application: didFinishLaunchingWithOptions:)çünkü bu seçici trueo erken aşamada yanıt vermeyecektir . Ama daha sonra yapabilirsiniz. AppDelegate'te kullanıcı arayüzünü otomatik olarak başlatmak yerine uygulamanızda özel AppPresenterveya AppRoutersınıf kullanıyorsanız çok kolaydır .


9

Sen açabilirsiniz Koyu Modu Xcode 11'de tüm uygulamada kapalı:

  1. Info.plist'e gidin
  2. Aşağıdaki gibi feryat ekle

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

Info.plist aşağıdaki gibi görünecektir ...

resim açıklamasını buraya girin


1
nedense Xcode Sürüm 11.3.1 (11C504) için çalışmıyor
Andrew

7

- Tüm Uygulama için (Pencere):

window!.overrideUserInterfaceStyle = .light

Pencereyi şuradan alabilirsiniz: SceneDelegate

- Tek bir ViewController için:

viewController.overrideUserInterfaceStyle = .light

Herhangi ayarlayabilirsiniz viewControllerbile viewController onu içeride kendini

- Tek bir Görünüm için:

view.overrideUserInterfaceStyle = .light

viewGörünümün içinde bile kendiniz ayarlayabilirsiniz

Daha if #available(iOS 13.0, *) { ,,, }önceki iOS sürümlerini destekliyorsanız kullanmanız gerekebilir .


6

Diğer yanıtların yanı sıra, aşağıdakileri anladığım kadarıyla, yalnızca iOS 13 SDK'ya (XCode 11 kullanarak) derlerken Karanlık moda hazırlanmanız gerekir.

Sistem, iOS 13 veya sonraki SDK'ya bağlı uygulamaların hem açık hem de karanlık görünümleri desteklediğini varsayar. İOS'ta, pencerenize, görünümünüze veya görünüm denetleyicinize belirli bir arabirim stili atayarak istediğiniz görünümü belirlersiniz. Ayrıca Info.plist tuşunu kullanarak Karanlık Mod desteğini tamamen devre dışı bırakabilirsiniz.

bağlantı


2

Evet, viewDidLoad içinde aşağıdaki kodu ekleyerek atlayabilirsiniz:

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

2

Uygulamam şu an için karanlık modu desteklemiyor ve açık bir uygulama çubuğu rengi kullanıyor. Durum anahtarını karanlık metinlere ve simgelere zorlayabildim Info.plist:

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

Diğer olası değerleri burada bulabilirsiniz: https://developer.apple.com/documentation/uikit/uistatusbarstyle


2

Objective-c sürümü

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

1

Karanlık modu desteklemek veya atlamak için uygulamanızda kullanabileceğiniz birkaç ipucu ve püf noktası.

İlk ipucu: ViewController stilini geçersiz kılmak için

UIViewController'ın arayüz stilini şu şekilde geçersiz kılabilirsiniz:

1: overrideUserInterfaceStyle = .dark // Karanlık mod için

2: overrideUserInterfaceStyle = .light // Işık modu için

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

İkinci ipucu: info.plist'e anahtar ekleme

Yeni bir anahtar ekleyebilirsiniz

UIUserInterfaceStyle

ekleyin ve değerini Açık veya Koyu olarak ayarlayın. bu, uygulamanın varsayılan stilini sağladığınız değere göre geçersiz kılar. OverrideUserInterfaceStyle = .light eklemeniz gerekmez.


1

info.plistDosyanıza aşağıdaki anahtarı eklemeniz yeterlidir :

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

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

Sadece kod yanıtı göndermek yerine bu cevabın sorunu nasıl çözeceğini biraz açıklayabilir misiniz?
Arun Vinoth

Evet emin @ArunVinoth IOS 13'te karanlık mod tanıtılır, böylece dağıtım hedefiniz 13'ten düşükse yukarıdaki kodu kullanın, eğer if bloğunda yazılmış basit bir ifadeyi kullanabilirsiniz.
Talha Rasool

1

Hızlı 5

Karanlıktan ışık moduna geçmenin iki yolu:

1- info.plist

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

2- Programlı olarak

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

0

Bu özelliği, pencere özelliği uygulama yaşam döngüsü sırasında değiştirilebileceğinden kullanırım. Bu nedenle "overrideUserInterfaceStyle = .light" atamasının tekrarlanması gerekir. UIWindow.appearance (), yeni oluşturulan UIWindow nesneleri için kullanılacak varsayılan değeri ayarlamamızı sağlar.

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

Bu satırı info.plist dosyasına ekleyin:

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

Bu, uygulamayı yalnızca ışık modunda çalışmaya zorlar.


Bu zaten birçok kez yorumlanmış ve cevaplanmıştır. Kabul edilen cevap bile bunu öneriyor. Bu nedenle bu yorum yeni bilgi eklemiyor.
JeroenJK

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
Lütfen cevabınızı düzenleyerek yanıtınıza bir açıklama ekleyin, böylece başkaları ondan öğrenebilir
Nico Haase

0

Yapabilirsiniz: Bu yeni UIUserInterfaceStyle anahtarını Info.plist'e ekleyin ve değerini Light olarak ayarlayın. ve ışık moduyla uyarı denetleyicisinin görüntülendiğini kontrol edin.

UIUserInterfaceStyle Light Info.plist dosyanıza UIUserInterfaceStyle anahtarını ekleyerek ve değerini Açık veya Koyu olarak ayarlayarak, kullanıcı ayarlarından bağımsız olarak tüm uygulamanızda açık / koyu modunu zorlarsanız.


0

Bu sorunun çok fazla yanıtı var, onu kullanmak yerine bunu info.plistşu şekilde ayarlayabilirsiniz AppDelegate:

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

Xcode 11.3, iOS 13.3 üzerinde test


-8

Aslında sadece uygulamanızdaki her bir viw denetleyicisi ile putz zorunda kalmadan koddaki karanlık moddan çıkmanıza izin verecek bazı kodlar yazdım. Bu muhtemelen, bir sınıflar listesi yöneterek sınıf bazında sınıftan çıkarmayı seçebilir. Benim için istediğim, kullanıcılarımın uygulamanızın karanlık mod arayüzünü beğenip beğenmediklerini görmeleri ve beğenmedikleri takdirde kapatabilirler. Bu, uygulamalarının geri kalanı için karanlık modu kullanmaya devam etmelerini sağlayacaktır.

Kullanıcı seçimi iyidir (Ahem, Apple'a bakmak, bunu nasıl uygulamanız gerekir).

Bunun nasıl çalıştığı sadece bir UIViewController kategorisidir. Yüklendiğinde, yerel viewDidLoad yöntemini, karanlık modun her şey için devre dışı bırakılıp bırakılmadığını görmek için genel bayrağı kontrol edecek bir yöntemle değiştirir.

UIViewController yüklemesinde tetiklendiğinden, varsayılan olarak karanlık modu otomatik olarak başlatmalı ve devre dışı bırakmalıdır. İstediğiniz bu değilse, oraya erken bir yere girip bayrağı ayarlamanız veya varsayılan bayrağı ayarlamanız gerekir.

Bayrağı açıp kapatan kullanıcıya yanıt vermek için henüz bir şey yazmadım. Yani bu temelde örnek kod. Kullanıcının bununla etkileşime girmesini istiyorsak, tüm görünüm denetleyicilerinin yeniden yüklenmesi gerekir. Bunu nasıl hazırlıksız yapacağımı bilmiyorum ama muhtemelen bazı bildirim göndermek hile yapacak. Şu anda, karanlık mod için bu genel açma / kapama sadece uygulamanın başlangıcında veya yeniden başlatılmasında çalışacaktır.

Şimdi, büyük uygulamanızdaki her bir MFING viewController'da karanlık modu kapatmaya çalışmak yeterli değildir. Renk varlıkları kullanıyorsanız, tamamen kemiklersiniz. 10 yılı aşkın bir süredir değişmez cisimlerin değişmez olduğunu anladık. Renk varlığı kataloğundan aldığınız renkler, bunların UIColor olduğunu ancak dinamik (değiştirilebilir) renkler olduğunu ve sistem karanlıktan aydınlık moduna geçtikçe altınızda değişeceğini söylüyor. Bunun bir özellik olması gerekiyordu. Ama elbette, bu şeylerin bu değişikliği yapmayı bırakmasını istemek için usta bir geçiş yok (şu anda bildiğim kadarıyla, belki biri bunu geliştirebilir).

Yani çözüm iki bölümden oluşuyor:

  1. UIViewController'da bazı yardımcı program ve kolaylık yöntemleri sunan genel bir kategori ... örneğin, apple'ın bazılarımızın web kodumuzu uygulamalarımıza karıştırması gerçeğini düşündüğünü sanmıyorum. Bu nedenle, karanlık veya açık moda göre değiştirilmesi gereken stil sayfalarımız var. Bu nedenle, bir tür dinamik stil sayfası nesnesi oluşturmalısınız (ki bu iyi olur) veya sadece mevcut durumun ne olduğunu sormanız gerekir (kötü ama kolay).

  2. yüklendiğinde bu kategori UIViewController sınıfının viewDidLoad yönteminin yerini alır ve aramaları engeller. Bunun uygulama mağazası kurallarını ihlal edip etmediğini bilmiyorum. Eğer öyleyse, bunun etrafında muhtemelen başka yollar da vardır, ancak bunu bir kavram kanıtı olarak düşünebilirsiniz. Örneğin, tüm ana görünüm denetleyicisi türlerinden bir alt sınıf oluşturabilir ve kendi görünüm denetleyicilerinizin bunlardan devralmasını sağlayabilirsiniz ve daha sonra DarkMode kategori fikrini kullanabilir ve tüm görünüm denetleyicilerinizi devre dışı bırakmaya zorlayabilirsiniz. Bu daha çirkin ama kurallara aykırı olmayacak. Çalışma zamanını kullanmayı tercih ederim, çünkü çalışma zamanının yapılması budur. Yani benim versiyonumda sadece kategoriyi ekliyorsunuz, karanlık modu engellemesini isteyip istemediğinize dair bir global değişken belirlersiniz ve bunu yapar.

  3. Henüz ormandan çıkmadınız, belirtildiği gibi, diğer sorun UIColor'un temelde istediği her şeyi yapmasıdır. Bu nedenle, görünüm denetleyicileriniz karanlık modu engelliyor olsa bile UIColor, nerede veya nasıl kullandığınızı bilmiyor, bu yüzden uyum sağlayamıyor. Sonuç olarak, doğru bir şekilde getirebilirsiniz, ancak gelecekte bir noktada size geri dönecektir. Belki yakında belki sonra. Bunun etrafındaki yol, bir CGColor kullanarak iki kez ayırarak ve statik bir renge dönüştürmektir. Bu, kullanıcı geri dönüp ayarlar sayfanızda karanlık modu yeniden etkinleştirirse (buradaki fikir, kullanıcının uygulamanız üzerinde sistemin geri kalanının üstünde ve üstünde kontrol sahibi olmasını sağlamak için bu işi yapmaktır), tüm bu statik renklerin değiştirilmesi gerekiyor. Şimdiye kadar bu başka birinin çözmesi için kaldı. Bunu yapmanın kolay yolu, bir varsayılan karanlık moddan çıkmayı seçerseniz, uygulamadan çıkmanız ve kullanıcıya yeniden başlatmasını söylemeniz nedeniyle uygulamayı çökertmek için sıfıra bölün. Bu muhtemelen uygulama mağazası yönergelerini de ihlal eder, ancak bu bir fikirdir.

UIColor kategorisinin açıklanması gerekmez, sadece colorNamed çağrılmasıyla çalışır: ... DarkMode ViewController sınıfına karanlık modu engellemesini söylemediyseniz, beklendiği gibi mükemmel bir şekilde çalışacaktır. Karanlık moddan programlı olarak çıkmak veya değiştirmek için uygulamanızın çoğunu değiştirmek zorunda kalacağınız anlamına gelen standart elma sphaghetti kodu yerine zarif bir şey yapmaya çalışın. Artık Info.plist'i karanlık modu gerektiği gibi kapatmak için programlı olarak değiştirmenin daha iyi bir yolu olup olmadığını bilmiyorum. Anladığım kadarıyla bu derleme zamanı özelliği ve bundan sonra kemikli.

İşte ihtiyacınız olan kod. Bırakın ve sadece UI Stilini ayarlamak veya kodda varsayılanı ayarlamak için bir yöntemi kullanın. Herhangi bir amaç için bunu kullanmak, değiştirmek, istediğinizi yapmakta özgürsünüz ve hiçbir garanti verilmemektedir ve uygulama mağazasını geçip geçmeyeceğini bilmiyorum. Gelişmeler çok hoş geldiniz.

Adil uyarı ARC veya başka bir tutma yöntemi kullanmıyorum.

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

Bunun, yöntem değiştirme işlemi için kullandığı bir dizi yardımcı program işlevi vardır. Ayrı dosya. Bu standart şeyler olsa ve benzer kodu her yerde bulabilirsiniz.

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

Kopyalama ve q-runtime.h yeniden kullanılabilir kütüphane ve bu sadece bir parçası beri birkaç dosyadan yapıştırma. Bir şey derlenmezse bana bildirin.


Bu soruda tartışıldığı gibi UIColor davranışını kontrol etme konusunda şansınız yok: stackoverflow.com/questions/56487679/…
raven_raven
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.