Apakah mungkin untuk memilih keluar dari mode gelap di iOS 13?

295

Sebagian besar aplikasi saya terdiri dari tampilan web untuk menyediakan fungsionalitas yang belum tersedia melalui implementasi asli. Tim web tidak memiliki rencana untuk menerapkan tema gelap untuk situs web. Dengan demikian, aplikasi saya akan terlihat setengah / setengah dengan dukungan Mode Gelap di iOS 13.

Apakah mungkin untuk memilih keluar dari dukungan Mode Gelap sehingga aplikasi kami selalu menunjukkan mode cahaya agar sesuai dengan tema situs web?

SeanR
sumber
70
Tetapkan UIUserInterfaceStyleke Lightdalam Info.Plist Anda. Lihat developer.apple.com/library/archive/documentation/General/…
Tieme
1
Terima kasih telah meminta - untuk kita semua. Banyak aplikasi yang harus dilalui. Ini diperlukan agar aplikasi tetap berfungsi sampai sakelar siap.
user3741598
import Foundation import ekstensi UIKit UIViewController {override open func awakeFromNib () {super.awakeFromNib () jika # tersedia (iOS 13.0, *) {// Selalu mengadopsi gaya antarmuka yang ringan. overrideUserInterfaceStyle = .light}}}
Mohammad Razipour
1
cukup tambahkan UIUserInterfaceStyle di plist. semudah itu
Fattie
Saat mengirimkan aplikasi ke appstore, lakukan penerimaan apel karena UIUserInterfaceStyle dalam mode Ringan.
kiran

Jawaban:

683

Pertama, di sini adalah entri Apple terkait dengan memilih keluar dari mode gelap. Konten di tautan ini ditulis untuk Xcode 11 & iOS 13 :

Bagian ini berlaku untuk penggunaan Xcode 11


Jika Anda ingin menyisih keluar SELURUH aplikasi Anda

Pendekatan # 1

Gunakan kunci berikut dalam file info.plist Anda :

UIUserInterfaceStyle

Dan berikan nilai Light.

The XML untuk UIUserInterfaceStyletugas:

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

Pendekatan # 2

Anda dapat mengatur overrideUserInterfaceStyleterhadap windowvariabel aplikasi .

Bergantung pada bagaimana proyek Anda dibuat, ini mungkin dalam AppDelegatefile atau SceneDelegate.

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


Jika Anda ingin memilih keluar dari UIViewController Anda secara individual

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

Dokumentasi Apple untuk overrideUserInterfaceStyle

Bagaimana kode di atas akan terlihat dalam Xcode 11:

masukkan deskripsi gambar di sini

Bagian ini berlaku untuk penggunaan Xcode 10.x


Jika Anda menggunakan Xcode 11 untuk pengiriman Anda, Anda dapat dengan aman mengabaikan semua yang ada di bawah baris ini.

Karena API yang relevan tidak ada di iOS 12, Anda akan mendapatkan kesalahan ketika mencoba menggunakan nilai yang diberikan di atas:

Untuk pengaturan overrideUserInterfaceStylediUIViewController

masukkan deskripsi gambar di sini

Jika Anda ingin memilih keluar dari UIViewController Anda secara individual

Ini dapat ditangani dalam Xcode 10 dengan menguji versi kompiler dan versi iOS:

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

Jika Anda ingin menyisih keluar SELURUH aplikasi Anda

Anda dapat memodifikasi cuplikan di atas agar bekerja melawan seluruh aplikasi untuk Xcode 10, dengan menambahkan kode berikut ke AppDelegatefile Anda .

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

Namun, pengaturan plist akan gagal saat menggunakan Xcode versi 10.x:

masukkan deskripsi gambar di sini

Penghargaan untuk @Aron Nelson , @Raimundas Sakalauskas , @NSLeader dan rmaddy untuk meningkatkan jawaban ini dengan umpan balik mereka.

CodeBender
sumber
2
Lampu UIUserInterfaceStyle diblokir saat memperbarui / mengunggah aplikasi Anda sekarang. Itu akan ditandai sebagai entri plist yang tidak valid. (Kunci plist tidak valid)
Aron Nelson
2
Ini tidak dapat dikompilasi dengan iOS SDK 12 (SDK stabil terbaru saat ini). Lihat stackoverflow.com/a/57521901/2249485 untuk solusi yang juga akan bekerja dengan iOS 12 SDK.
Raimundas Sakalauskas
Ini sangat tidak adil sehingga pertanyaan yang memiliki lebih banyak pandangan daripada "pertanyaan asli" dikunci untuk memberikan jawaban. :(
Raimundas Sakalauskas
7
Alih-alih pengaturan overrideUserInterfaceStyledi viewDidLoadsetiap controller tampilan, Anda dapat mengaturnya sekali pada jendela utama aplikasi. Jauh lebih mudah jika Anda ingin seluruh aplikasi berperilaku satu arah.
rmaddy
2
Gunakan #if compiler(>=5.1)sebagai gantinya responds(to:)dansetValue
NSLeader
162

Menurut sesi Apple tentang "Menerapkan Mode Gelap di iOS" ( https://developer.apple.com/videos/play/wwdc2019/214/ mulai dari 31:13) dimungkinkan untuk mengatur overrideUserInterfaceStyleke UIUserInterfaceStyleLightatau UIUserInterfaceStyleDarkpada pengontrol tampilan atau tampilan , yang akan digunakan traitCollectionuntuk subview atau view controller.

Seperti yang telah disebutkan oleh SeanR, Anda dapat mengatur UIUserInterfaceStyleke Lightatau Darkdalam file plist aplikasi Anda untuk mengubahnya ke seluruh aplikasi Anda.

dorbeetle
sumber
17
Jika Anda mengatur kunci UIUserInterfaceStyle aplikasi Anda akan ditolak di App store
Sonius
2
Apple menolak dengan ITMS-90190 kode kesalahan forums.developer.apple.com/thread/121028
PRASAD1240
11
Penolakan kemungkinan besar terjadi karena iOS 13 SDK belum keluar dari beta. Saya pikir ini akan berfungsi segera setelah Xcode 11 GM tersedia.
dorbeetle
2
@dorbeetle itu tidak benar, saya berhasil mengunggah aplikasi saya dengan kunci ini seperti 1 bulan yang lalu dengan Xcode 10. Penolakan terjadi baru-baru ini. Sepertinya beberapa jenis strategi Apple baru.
steven
1
Itu masih terjadi. Xcode GM2 mengembalikan kesalahan penandatanganan aplikasi. Xcode 10.3 dikembalikan: "Kunci Info.plist tidak valid. Kunci 'UIUserInterfaceStyle' dalam file Payload / Galileo.appInfo.plist tidak valid."
Evgen Bodunov
64

Jika Anda tidak menggunakan Xcode 11 atau lebih baru (i, e iOS 13 atau lebih baru SDK), aplikasi Anda belum secara otomatis memilih untuk mendukung mode gelap. Jadi, tidak perlu keluar dari mode gelap.

Jika Anda menggunakan Xcode 11 atau lebih baru, sistem telah secara otomatis mengaktifkan mode gelap untuk aplikasi Anda. Ada dua pendekatan untuk menonaktifkan mode gelap tergantung pada preferensi Anda. Anda dapat menonaktifkannya sepenuhnya atau menonaktifkannya untuk jendela tertentu, tampilan, atau pengontrol tampilan.

Nonaktifkan Mode Gelap Sepenuhnya untuk Aplikasi Anda

Anda dapat menonaktifkan mode gelap dengan memasukkan UIUserInterfaceStylekunci dengan nilai seperti Lightdalam file Info.plist aplikasi Anda. Ini mengabaikan preferensi pengguna dan selalu menerapkan tampilan ringan untuk aplikasi Anda.
UIUserInterfaceStyle as Light

Nonaktifkan mode gelap untuk Window, View, atau View Controller

Anda dapat memaksa antarmuka Anda untuk selalu tampil dalam gaya terang atau gelap dengan mengatur overrideUserInterfaceStyleproperti jendela, tampilan, atau pengontrol tampilan yang sesuai.

Lihat pengontrol:

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

Dilihat:

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

Jendela:

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

Catatan: Apple sangat menyarankan untuk mendukung mode gelap di aplikasi Anda. Jadi, Anda hanya dapat menonaktifkan mode gelap sementara.

Baca lebih lanjut di sini: Memilih Gaya Antarmuka Tertentu untuk Aplikasi iOS Anda

Ajith R Nayak
sumber
34

********** Cara termudah untuk Xcode 11 ke atas ***********

Tambahkan ini ke info.plist sebelumnya </dict></plist>

<key>UIUserInterfaceStyle</key>
<string>Light</string>
Kingsley Mitchell
sumber
solusi ini akan gagal ketika mengirimkan aplikasi pada Xcode 10.x
Tawfik Bouabid
27

Saya pikir saya sudah menemukan solusinya. Saya awalnya menyatukannya dari UIUserInterfaceStyle - Daftar Informasi Properti dan UIUserInterfaceStyle - UIKit , tetapi sekarang telah menemukannya didokumentasikan pada Memilih gaya antarmuka spesifik untuk aplikasi iOS Anda .

Di info.plistset AndaUIUserInterfaceStyle ( Gaya Antarmuka Pengguna ) ke 1 ( UIUserInterfaceStyle.light).

EDIT: Sesuai jawaban dorbeetle, pengaturan yang lebih tepat untuk UIUserInterfaceStyle mungkin Light.

SeanR
sumber
Menegakkan mode gelap dengan menetapkan nilai ke 2 tidak bekerja:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen
3
Memiliki kunci ini dalam daftar akan menghasilkan penolakan App Store.
José
1
AppStore tidak lagi menolak properti ini di plist.info. Saya meletakkan "Gelap" (huruf besar) karena aplikasi kami sudah gelap. Tidak ada masalah. Ini memungkinkan kami menggunakan kontrol sistem.
nickdnk
@nickdnk Saya pikir Anda membangun aplikasi Anda dengan Xcode 11, yang direkomendasikan oleh Apple.
DawnSong
1
Ya saya lakukan. Itu tidak mengubah fakta bahwa Apple tidak menerima parameter ini di plist, yang saya coba jelaskan.
nickdnk
23

Jawaban di atas berfungsi jika Anda ingin menyisih dari seluruh aplikasi. Jika Anda bekerja pada lib yang memiliki UI, dan Anda tidak memiliki mengedit .plist, Anda dapat melakukannya melalui kode juga.

Jika Anda mengompilasi dengan iOS 13 SDK, Anda dapat menggunakan kode berikut:

Cepat:

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

Obj-C:

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

NAMUN , jika Anda ingin kode Anda dikompilasi iOS 12 SDK juga (yang saat ini masih SDK stabil terbaru), Anda harus menggunakan penyeleksi. Kode dengan pemilih:

Swift (XCode akan menampilkan peringatan untuk kode ini, tetapi itulah satu-satunya cara untuk melakukannya untuk saat ini karena properti tidak ada di SDK 12 karena itu tidak akan dikompilasi):

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
sumber
Akan lebih baik jika Anda menentukan overrideUserInterfaceStylemilik properti apa .
DawnSong
12

Pembaruan Terbaru-

Jika Anda menggunakan Xcode 10.x, maka defaultnya UIUserInterfaceStyleadalah lightuntuk iOS 13.x. Saat dijalankan pada perangkat iOS 13, itu hanya akan berfungsi dalam Mode Ringan.

Tidak perlu menambahkan UIUserInterfaceStylekunci secara eksplisit dalam file Info.plist, menambahkannya akan memberikan kesalahan ketika Anda memvalidasi aplikasi Anda, mengatakan:

Kunci Info.plist tidak valid. Kunci 'UIUserInterfaceStyle' di file Payload / AppName.appInfo.plist tidak valid.

Hanya tambahkan UIUserInterfaceStylekunci dalam file Info.plist saat menggunakan Xcode 11.x.

kumarsiddharth123
sumber
1
Ini tidak ada hubungannya dengan Xcode 10 atau 11. Jika pengguna menyebarkan formulir aplikasi Xcode 10 dan tidak menangani mode gelap, aplikasi ketika diinstal di iPhone 11, Pro atau Pro Max itu akan memiliki masalah mode gelap. Anda perlu memperbarui ke Xcode 11 dan mengatasi masalah ini.
Niranjan Molkeri
3
@NiranjanMolkeri Ini tidak ada hubungannya dengan iPhone yang lebih baru. Ini tentang mode Gelap di iOS 13. Dalam iOS 13 versi sebelumnya aplikasi iOS UI mengalami masalah mode gelap jika tidak ditangani secara eksplisit. Namun dalam versi terbaru, itu sudah diperbaiki. Jika Anda menggunakan XCode 10, maka UIUserInterfaceStyle default ringan untuk iOS13. Jika Anda menggunakan Xode11, Anda harus menanganinya.
kumarsiddharth123
Anda akan memiliki masalah jika Anda mengunggah aplikasi ke TestFligth menggunakan Xcode 10.3 dan daftar itu menyertakan kunci UIUserInterfaceStyle. Itu akan mengatakan bahwa itu adalah file plist yang tidak valid. Anda harus menghapusnya jika membangun di Xcode 10, atau mengunggah menggunakan Xcode 11
eharo2
9

Jika Anda akan menambahkan UIUserInterfaceStylekunci ke file plist, mungkin Apple akan menolak rilis rilis seperti yang disebutkan di sini: https://stackoverflow.com/a/56546554/7524146 Pokoknya itu menjengkelkan untuk secara eksplisit memberi tahu setiap ViewController self.overrideUserInterfaceStyle = .light . Tetapi Anda dapat menggunakan ketenangan kode ini sekali untuk windowobjek root Anda :

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

Hanya perhatikan Anda tidak dapat melakukan ini di dalam application(application: didFinishLaunchingWithOptions:)karena pemilih ini tidak akan merespons truepada tahap awal itu. Tetapi Anda bisa melakukannya nanti. Ini sangat mudah jika Anda menggunakan kustom AppPresenteratau AppRouterkelas di aplikasi Anda daripada memulai UI di AppDelegate secara otomatis.

SerhiiK
sumber
9

Anda dapat mematikan Mode Gelap di seluruh aplikasi dalam Xcode 11:

  1. Go Info.plist
  2. Tambahkan di bawah seperti

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

Info.plist akan terlihat seperti di bawah ini ...

masukkan deskripsi gambar di sini

Enamul Haque
sumber
1
tidak berfungsi untuk Xcode Versi 11.3.1 (11C504) karena alasan tertentu
Andrew
7

- Untuk seluruh Aplikasi (Jendela):

window!.overrideUserInterfaceStyle = .light

Anda bisa mendapatkan jendela dari SceneDelegate

- Untuk satu ViewController:

viewController.overrideUserInterfaceStyle = .light

Anda dapat mengatur apa saja viewController, bahkan di dalam viewController sendiri

- Untuk satu Tampilan:

view.overrideUserInterfaceStyle = .light

Anda dapat mengatur apa saja view, bahkan di dalam tampilan itu sendiri

Anda mungkin perlu menggunakan if #available(iOS 13.0, *) { ,,, }jika Anda mendukung versi iOS sebelumnya.

Mojtaba Hosseini
sumber
6

Terlepas dari tanggapan lain, dari pemahaman saya tentang hal berikut, Anda hanya perlu mempersiapkan mode Gelap saat dikompilasi dengan iOS 13 SDK (menggunakan XCode 11).

Sistem mengasumsikan bahwa aplikasi yang ditautkan dengan iOS 13 atau yang lebih baru SDK mendukung tampilan terang dan gelap. Di iOS, Anda menentukan tampilan spesifik yang Anda inginkan dengan menetapkan gaya antarmuka spesifik ke jendela, tampilan, atau pengontrol tampilan Anda. Anda juga dapat menonaktifkan dukungan untuk Mode Gelap sepenuhnya menggunakan kunci Info.plist.

Tautan

Claudio
sumber
2

Ya, Anda dapat melewati dengan menambahkan kode berikut di viewDidLoad:

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

Aplikasi saya tidak mendukung mode gelap seperti sekarang dan menggunakan warna bilah aplikasi terang. Saya dapat memaksa konten bilah status ke teks dan ikon gelap dengan menambahkan kunci berikut ke saya Info.plist:

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

Temukan nilai lain yang mungkin di sini: https://developer.apple.com/documentation/uikit/uistatusbarstyle

ToniTornado
sumber
2

Versi obyektif-c

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

Berikut adalah beberapa tips dan trik yang dapat Anda gunakan di aplikasi Anda untuk mendukung atau melewati mode gelap.

Kiat pertama: Untuk mengganti gaya ViewController

Anda dapat mengganti gaya antarmuka UIViewController dengan

1: overrideUserInterfaceStyle = .dark // Untuk mode gelap

2: overrideUserInterfaceStyle = .light // Untuk mode cahaya

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

Kiat kedua: Menambahkan kunci di info.plist

Cukup Anda dapat menambahkan kunci baru

UIUserInterfaceStyle

di info.plist aplikasi Anda dan atur nilainya menjadi Terang atau Gelap. ini akan menimpa gaya default aplikasi ke nilai yang Anda berikan. Anda tidak perlu menambahkan overrideUserInterfaceStyle = .light baris ini di setiap viewController, cukup satu baris di info.plist itu saja.

Mohammed Ebrahim
sumber
1

Cukup tambahkan kunci berikut di info.plistfile Anda :

<key>UIUserInterfaceStyle</key>
    <string>Light</string>
Moeen Ahmad
sumber
1
 if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light
        } else {
            // Fallback on earlier versions
        }
Talha Rasool
sumber
Bisakah Anda jelaskan sedikit bagaimana jawaban ini akan menyelesaikan masalah, alih-alih memposting jawaban hanya kode.
Arun Vinoth
Ya tentu @ArunVinoth Di iOS 13 mode gelap diperkenalkan jadi jika target penyebaran Anda lebih rendah dari 13 gunakan kode di atas yang lain Anda dapat menggunakan pernyataan sederhana yang ditulis dalam if block.
Talha Rasool
1

Cepat 5

Dua cara untuk beralih dari mode gelap ke terang:

1- info. Daftar

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

2- Secara terprogram

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

Saya akan menggunakan solusi ini karena properti jendela dapat diubah selama siklus hidup aplikasi. Jadi menugaskan "overrideUserInterfaceStyle = .light" perlu diulang. UIWindow.appearance () memungkinkan kami untuk menetapkan nilai default yang akan digunakan untuk objek UIWindow yang baru dibuat.

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
sumber
0

Cukup tambahkan baris ini dalam file info.plist:

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

Ini akan memaksa aplikasi untuk berjalan dalam mode cahaya saja.

Rahul Gusain
sumber
Ini sudah dikomentari dan dijawab berkali-kali. Bahkan jawaban yang diterima menyarankan ini. Karenanya komentar ini tidak menambahkan informasi baru.
JeroenJK
0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}
Mohammad Razipour
sumber
2
Silakan tambahkan beberapa penjelasan untuk jawaban Anda dengan mengeditnya, sehingga orang lain dapat belajar darinya
Nico Haase
0

Anda dapat melakukannya: tambahkan kunci UIUserInterfaceStyle baru ini ke Info.plist dan tetapkan nilainya ke Light. dan periksa pengontrol peringatan muncul dengan mode cahaya.

UIUserInterfaceStyle Light Jika Anda memaksa mode terang / gelap di seluruh aplikasi Anda terlepas dari pengaturan pengguna dengan menambahkan kunci UIUserInterfaceStyle ke file Info.plist Anda dan mengatur nilainya ke Cahaya atau Gelap.

Hominda
sumber
0

Pertanyaan ini memiliki begitu banyak jawaban, alih-alih menggunakannya info.plistAnda dapat mengaturnya AppDelegateseperti ini:

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

Uji pada Xcode 11.3, iOS 13.3

Niraj
sumber
-8

Sebenarnya saya hanya menulis beberapa kode yang akan memungkinkan Anda untuk secara global memilih keluar dari mode gelap dalam kode tanpa harus memasang setiap pengontrol viw tunggal dalam aplikasi Anda. Ini mungkin dapat disempurnakan untuk memilih keluar dari kelas berdasarkan kelas dengan mengelola daftar kelas. Bagi saya, yang saya inginkan adalah agar pengguna melihat apakah mereka menyukai antarmuka mode gelap untuk aplikasi saya, dan jika mereka tidak menyukainya, mereka dapat mematikannya. Ini akan memungkinkan mereka untuk terus menggunakan mode gelap untuk sisa aplikasi mereka.

Pilihan pengguna baik (Ahem, melihat Anda Apple, ini adalah bagaimana Anda seharusnya menerapkannya).

Jadi cara kerjanya adalah hanya kategori UIViewController. Ketika dimuat itu menggantikan metode viewDidLoad asli dengan yang akan memeriksa bendera global untuk melihat apakah mode gelap dinonaktifkan untuk semuanya atau tidak.

Karena dipicu pada pemuatan UIViewController maka secara otomatis akan memulai dan menonaktifkan mode gelap secara default. Jika ini bukan yang Anda inginkan, maka Anda harus masuk ke suatu tempat lebih awal dan mengatur bendera, atau hanya mengatur bendera default.

Saya belum menulis apa pun untuk menanggapi pengguna mengaktifkan atau menonaktifkan bendera. Jadi ini pada dasarnya adalah contoh kode. Jika kami ingin pengguna berinteraksi dengan ini, semua pengontrol tampilan perlu memuat ulang. Saya tidak tahu bagaimana melakukan itu begitu saja, tetapi mungkin mengirimkan beberapa pemberitahuan akan berhasil. Jadi saat ini, mode on / off global ini untuk mode gelap hanya akan berfungsi saat startup atau restart aplikasi.

Sekarang, itu tidak hanya cukup untuk mencoba mematikan mode gelap di setiap viewController MFING tunggal di aplikasi besar Anda. Jika Anda menggunakan aset warna, Anda sepenuhnya bertulang. Kami selama 10+ tahun telah memahami objek yang tidak dapat berubah menjadi tidak berubah. Warna yang Anda dapatkan dari katalog aset warna mengatakan bahwa itu adalah UIColor tetapi warnanya dinamis (bisa berubah) dan akan berubah di bawah Anda saat sistem berubah dari mode gelap ke terang. Itu seharusnya menjadi fitur. Tetapi tentu saja tidak ada master beralih untuk meminta hal-hal ini berhenti membuat perubahan ini (sejauh yang saya tahu sekarang, mungkin seseorang dapat meningkatkan ini).

Jadi solusinya ada dalam dua bagian:

  1. kategori publik di UIViewController yang memberikan beberapa metode utilitas dan kenyamanan ... misalnya saya tidak berpikir apple telah memikirkan fakta bahwa sebagian dari kita mencampur kode web ke dalam aplikasi kita. Karena itu kami memiliki stylesheet yang perlu diubah berdasarkan mode gelap atau terang. Jadi, Anda perlu membangun semacam objek stylesheet dinamis (yang akan baik) atau hanya bertanya seperti apa keadaan saat ini (buruk tapi mudah).

  2. kategori ini saat dimuat akan menggantikan metode viewDidLoad dari kelas UIViewController dan mencegat panggilan. Saya tidak tahu apakah itu melanggar aturan toko aplikasi. Jika ya, ada beberapa cara lain yang mungkin tetapi Anda dapat menganggapnya sebagai bukti konsep. Misalnya Anda dapat membuat satu subkelas dari semua jenis pengontrol tampilan utama dan membuat semua pengontrol tampilan Anda mewarisi dari itu, dan kemudian Anda dapat menggunakan ide kategori DarkMode dan memanggilnya untuk memaksa memilih keluar dari semua pengontrol tampilan Anda. Itu lebih buruk tetapi tidak akan melanggar aturan apa pun. Saya lebih suka menggunakan runtime karena itulah yang harus dilakukan runtime. Jadi dalam versi saya, Anda hanya menambahkan kategori, Anda menetapkan variabel global pada kategori untuk apakah Anda ingin memblokir mode gelap, dan itu akan melakukannya.

  3. Anda belum keluar dari hutan, seperti yang disebutkan, masalah lainnya adalah UIColor pada dasarnya melakukan apa pun yang diinginkannya. Jadi, bahkan jika pengontrol tampilan Anda memblokir mode gelap, UIColor tidak tahu di mana atau bagaimana Anda menggunakannya jadi tidak bisa beradaptasi. Sebagai hasilnya, Anda dapat mengambilnya dengan benar tetapi kemudian akan mengembalikan Anda di beberapa titik di masa depan. Mungkin sebentar lagi nanti. Jadi cara mengatasinya adalah dengan mengalokasikannya dua kali menggunakan CGColor dan mengubahnya menjadi warna statis. Ini berarti jika pengguna Anda kembali dan mengaktifkan kembali mode gelap pada halaman pengaturan Anda (ide di sini adalah untuk membuat ini berfungsi sehingga pengguna memiliki kontrol atas aplikasi Anda di atas dan di atas sisa sistem), semua warna-warna statis perlu diganti. Sejauh ini yang tersisa untuk dipecahkan orang lain. Cara mudah untuk melakukannya adalah dengan membuat default bahwa Anda kembali memilih keluar dari mode gelap, bagi dengan nol untuk crash aplikasi karena Anda tidak dapat keluar dan memberitahu pengguna untuk hanya me-restart itu. Itu mungkin melanggar pedoman toko aplikasi juga tapi itu ide.

Kategori UIColor tidak perlu diekspos, itu hanya berfungsi memanggil colorNamed: ... jika Anda tidak memberi tahu kelas DarkMode ViewController untuk memblokir mode gelap, itu akan berfungsi dengan baik seperti yang diharapkan. Mencoba membuat sesuatu yang elegan alih-alih kode sphaghetti apel standar yang berarti Anda harus memodifikasi sebagian besar aplikasi Anda jika Anda ingin secara sistematis memilih keluar dari mode gelap atau mengubahnya. Sekarang saya tidak tahu apakah ada cara yang lebih baik untuk secara sistematis mengubah Info.plist untuk mematikan mode gelap sesuai kebutuhan. Sejauh pemahaman saya itu adalah fitur waktu kompilasi dan setelah itu Anda boned.

Jadi di sini adalah kode yang Anda butuhkan. Harus drop in dan cukup gunakan satu metode untuk mengatur Gaya UI atau mengatur default dalam kode. Anda bebas untuk menggunakan, memodifikasi, melakukan apa pun yang Anda inginkan dengan tujuan ini dan tidak ada garansi yang diberikan dan saya tidak tahu apakah itu akan melewati app store. Perbaikan sangat disambut.

Peringatan yang adil saya tidak menggunakan ARC atau metode pegangan lainnya.

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

Ada satu set fungsi utilitas yang digunakan untuk melakukan metode swapping. File terpisah. Ini adalah hal standar dan Anda dapat menemukan kode serupa di mana saja.

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

Saya menyalin dan menempelkan ini dari beberapa file sejak q-runtime.h adalah pustaka yang dapat digunakan kembali dan ini hanya sebagian saja. Jika ada sesuatu yang tidak dikompilasi, beri tahu saya.

dbquarrel
sumber
Anda tidak kurang beruntung ketika mengendalikan perilaku UIColor, seperti yang dibahas dalam pertanyaan ini: stackoverflow.com/questions/56487679/…
raven_raven