Чи можна відмовитися від темного режиму на iOS 13?


297

Значна частина мого додатка складається з веб-представлень, щоб забезпечити функціональність, який ще не доступний завдяки власній реалізації. Веб-команда не планує впроваджувати темну тему для веб-сайту. Таким чином, моя програма буде виглядати трохи наполовину / з підтримкою темного режиму на iOS 13.

Чи можна відмовитися від підтримки темного режиму, щоб наш додаток завжди показував світлий режим, щоб він відповідав темі веб-сайту?


71
Набір UIUserInterfaceStyleдля Lightвашої info.plist. Дивіться developer.apple.com/library/archive/documentation/General/…
Tieme

1
Дякуємо за запитання - для всіх нас. Дуже багато додатків. Це потрібно, щоб додатки працювали до тих пір, поки перемикач не буде готовий.
користувач3741598

import Foundation імпортувати розширення UIKit UIViewController {переосмислити відкритий func awakeFromNib () {super.awakeFromNib (), якщо #available (iOS 13.0, *) {// Завжди прийняти легкий стиль інтерфейсу. overrideUserInterfaceStyle = .light}}}
Мохаммад Разіпур

1
просто додайте UIUserInterfaceStyle в пліст . це так просто
Fattie

Під час подання програми в appstore яблуко приймає завдяки UIUserInterfaceStyle у світлому режимі.
Кіран

Відповіді:


684

По-перше, тут йдеться про запис Apple, пов'язане з відмовою від темного режиму. Вміст за цим посиланням написано для Xcode 11 та iOS 13 :

Цей розділ стосується використання Xcode 11


Якщо ви бажаєте відмовитись від вашої програми ENTIRE

Підхід №1

Використовуйте наступний ключ у файлі info.plist :

UIUserInterfaceStyle

І присвоїти йому значення Light .

XML для UIUserInterfaceStyleзавдання:

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

Підхід №2

Ви можете налаштувати overrideUserInterfaceStyleпроти програмиwindow змінної.

Залежно від того, як створено ваш проект, це може бути у AppDelegateфайлі чи SceneDelegate.

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


Якщо Ви бажаєте відмовитись від UIViewController індивідуально

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

Документація Apple для overrideUserInterfaceStyle

Як буде виглядати наведений вище код у Xcode 11:

введіть тут опис зображення

Цей розділ стосується використання Xcode 10.x


Якщо ви використовуєте Xcode 11 для подання, ви можете сміливо ігнорувати все, що знаходиться нижче цього рядка.

Оскільки відповідного API не існує в iOS 12, ви отримаєте помилки при спробі використання наведених вище значень:

Для налаштування overrideUserInterfaceStyleу вашомуUIViewController

введіть тут опис зображення

Якщо Ви бажаєте відмовитись від UIViewController індивідуально

Це можна вирішити в Xcode 10, випробувавши версію компілятора та версію iOS:

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

Якщо ви бажаєте відмовитись від вашої програми ENTIRE

Ви можете змінити описаний вище фрагмент, щоб він працював проти всієї програми для Xcode 10, додавши наступний код у свій AppDelegateфайл.

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

Однак налаштування плістеру не вдасться при використанні Xcode версії 10.x:

введіть тут опис зображення

Кредит @Aron Нельсон , @Raimundas Сакалаускас , @NSLeader і rmaddy для поліпшення цієї відповіді з їх зворотного зв'язку.


2
Світло UIUserInterfaceStyle блокується під час оновлення / завантаження програми зараз. Він позначений як недійсний запис у списку. (Недійсний плей-ключ)
Арон Нельсон

2
Це не компілюється з iOS SDK 12 (останній стабільний SDK). Дивіться stackoverflow.com/a/57521901/2249485 для рішення, яке також буде працювати з iOS 12 SDK.
Раймундас Сакалаускас

Це настільки несправедливо, що питання, яке має набагато більше поглядів, ніж "оригінальне запитання", блокується для надання відповідей. :(
Раймундас Сакалаускас

7
Замість того щоб встановлювати overrideUserInterfaceStyleв viewDidLoadкожній контролері уявлення, ви можете встановити його один раз в головному вікні програми. Так набагато простіше, якщо ви хочете, щоб весь додаток поводився в один бік.
rmaddy

2
Використовуйте #if compiler(>=5.1)натомість responds(to:)іsetValue
NSLeader

162

Відповідно до сесії Apple щодо "Реалізація темного режиму на iOS" ( https://developer.apple.com/videos/play/wwdc2019/214/ починаючи з 31:13) можна встановити overrideUserInterfaceStyleна UIUserInterfaceStyleLightабо UIUserInterfaceStyleDarkна будь-якому контролері перегляду чи перегляді , який буде використовуватися вtraitCollection будь-якому контролері подання або перегляду.

Як уже згадано SeanR, ви можете встановити UIUserInterfaceStyleна Lightабо Darkв Plist файлу вашої програми , щоб змінити це для всього програми.


17
Якщо ви встановите ключ UIUserInterfaceStyle, ваш додаток буде відхилено в магазині додатків
Sonius

2
Apple відхилила код помилки ITMS-90190 forums.developer.apple.com/thread/121028
PRASAD1240

11
Відмова, швидше за все, відбудеться тому, що SDK для iOS 13 ще не вийшов із бета-версії. Я думаю, що це має спрацювати, як тільки з'явиться Xkod 11 GM.
dorbeetle

2
@dorbeetle це неправда, я додав додаток із цим ключем успішно, як 1 місяць тому з Xcode 10. Відхилення трапляються недавно. Здається, деякі види нової стратегії Apple.
Стівен

1
Це все ще відбувається. Xcode GM2 повернув помилку підписання програми. Повертається Xcode 10.3: "Недійсний ключ Info.plist. Ключ" UIUserInterfaceStyle "у файлі Payload / Galileo.appInfo.plist недійсний."
Євген Бодунов

64

Якщо ви не використовуєте Xcode 11 або новішої версії (i, e iOS 13 або новішої версії SDK), ваш додаток не автоматично вибрав підтримку темного режиму. Отже, не потрібно вимикатись із темного режиму.

Якщо ви використовуєте Xcode 11 або новішої версії, система автоматично включила темний режим для вашої програми. Існує два підходи до відключення темного режиму залежно від ваших уподобань. Ви можете повністю відключити або відключити його для будь-якого конкретного вікна, перегляду чи контролера перегляду.

Вимкнути темний режим повністю для вашої програми

Ви можете відключити темний режим, включивши UIUserInterfaceStyleключ зі значенням, як Lightу файлі Info.plist програми. Це ігнорує переваги користувача та завжди застосовує легкий вигляд до вашої програми.
UIUserInterfaceStyle як світло

Вимкнути темний режим для контролера вікна, подання або перегляду

Ви можете змусити ваш інтерфейс завжди відображатися у світлому або темному стилі, встановивши overrideUserInterfaceStyle властивість відповідного контролера вікна, перегляду чи перегляду.

Переглянути контролери:

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

Перегляди:

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

Вікно:

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

Примітка: Apple наполегливо рекомендує підтримувати темний режим у вашій програмі. Отже, ви можете лише тимчасово відключити темний режим.

Детальніше читайте тут: Вибір конкретного стилю інтерфейсу для додатка iOS


34

********** Найпростіший спосіб для Xcode 11 і вище ***********

Додайте це до info.plist раніше </dict></plist>

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

це рішення не вдасться після подання програми на Xcode 10.x
Tawfik Bouabid

27

Я думаю, що я знайшов рішення. Спочатку я зібрав його разом з UIUserInterfaceStyle - Список інформаційних властивостей та UIUserInterfaceStyle - UIKit , але тепер знайшов це фактично задокументованим при виборі конкретного стилю інтерфейсу для додатка iOS .

У своєму режимі ( Стиль користувальницького інтерфейсу ) info.plistвстановіть значення 1 (UIUserInterfaceStyleUIUserInterfaceStyle.light ).

EDIT: Відповідно до відповіді dorbeetle, UIUserInterfaceStyleможе бути більш підходящим налаштуванням Light.


Закріплення темного режиму, встановивши значення 2, не працює:[UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.
funkenstrahlen

3
Якщо цей ключ у списку, це призведе до відхилення App Store.
Хосе

1
AppStore більше не відкидає цю властивість на plist.info. Я ставлю "Темно" (з великої літери), оскільки наш додаток уже темно. Немає проблем. Це належним чином дозволяє нам використовувати системні елементи управління.
nickdnk

@nickdnk Я думаю, що ви створили свій додаток з Xcode 11, який рекомендує Apple.
Світанок

1
Так. Це не змінює факту, що Apple дійсно приймає цей параметр у списку, про що я намагався зрозуміти.
nickdnk

23

Відповідь вище працює, якщо ви хочете вимкнути весь додаток. Якщо ви працюєте над lib, який має інтерфейс користувача, і у вас немає розкоші редагувати .plist, ви можете це зробити і за допомогою коду.

Якщо ви компілюєте проти iOS 13 SDK, ви можете просто використовувати наступний код:

Швидкий:

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

Obj-C:

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

ЗАРАЗ , якщо ви хочете, щоб ваш код також компілювався проти iOS 12 SDK (який зараз є останнім стабільним SDK), вам слід вдатися до використання селекторів. Код із селекторами:

Swift (XCode покаже попередження для цього коду, але це єдиний спосіб зробити це поки що, оскільки властивість не існує в SDK 12, тому не компілюється):

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

Obj-C:

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

Буде краще, якщо ви вкажете, до якої власності overrideUserInterfaceStyleналежить.
Світанок

12

Останнє оновлення-

Якщо ви використовуєте Xcode 10.x, то типовим UIUserInterfaceStyleєlight iOS 13.x. Під час роботи на пристрої iOS 13 він працюватиме лише в режимі світла.

Не потрібно явно додавати UIUserInterfaceStyleключ у файл Info.plist, додавши його, ви отримаєте помилку під час перевірки програми, сказавши:

Недійсний ключ Info.plist. Ключ "UIUserInterfaceStyle" у файлі Payload / AppName.appInfo.plist недійсний.

Додайте UIUserInterfaceStyleключ у файл Info.plist лише під час використання Xcode 11.x.


1
Це не має нічого спільного з Xcode 10 або 11. Якщо користувач розгортає форму програми Xcode 10 і не піклується про темний режим, додаток при встановленні в iPhone 11, Pro або Pro Max у нього виникнуть проблеми з темним режимом. вам потрібно оновити Xcode 11 та вирішити це питання.
Ніранджан Молкері

3
@NiranjanMolkeri Це не має нічого спільного з новішими айфонами. Йдеться про темний режим на iOS 13. У попередніх програмах бета-версії iOS 13 у користувальницького інтерфейсу виникли проблеми з темним режимом, якщо не керуватися ними явно. Але в останній версії це виправлено. Якщо ви використовуєте XCode 10, то для iOS13 стандартний UIUserInterfaceStyle за замовчуванням. Якщо ви використовуєте Xode11, вам потрібно це обробити.
kumarsiddharth123

У вас виникнуть проблеми, якщо ви завантажите додаток у TestFligth за допомогою Xcode 10.3, а пліст містить ключ UIUserInterfaceStyle. Це скаже, що це недійсний файл плісту. Ви повинні або видалити його, якщо будуєте в Xcode 10, або завантажуєте за допомогою Xcode 11
eharo2

9

Якщо ви додасте UIUserInterfaceStyleключ до файлу plist, можливо, Apple відхилить збірку випусків, як згадувалося тут: https://stackoverflow.com/a/56546554/7524146 У всякому разі, це дратує явно сказати кожному ViewController self.overrideUserInterfaceStyle = .light . Але ви можете використовувати цей мир коду один раз для вашого кореневого windowоб'єкта:

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

Просто зауважте, що ви не можете цього зробити всередині, application(application: didFinishLaunchingWithOptions:)оскільки цей селектор не відповість trueна ранній стадії. Але ви можете це зробити згодом. Це дуже просто, якщо ви використовуєте користувальницький AppPresenterабо AppRouterклас у своєму додатку, а не запускати інтерфейс користувача в AppDelegate автоматично.


9

Можна вимкнути темний режим у всій програмі в Xcode 11:

  1. Перейдіть на Info.plist
  2. Додайте нижче, як

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

Інформаційний список буде виглядати нижче ...

введіть тут опис зображення


1
чомусь не працює для Xcode версії 11.3.1 (11C504)
Андрій

7

- для всього додатка (вікна):

window!.overrideUserInterfaceStyle = .light

Ви можете отримати вікно від SceneDelegate

- Для одного ViewController:

viewController.overrideUserInterfaceStyle = .light

Ви можете встановити будь-яке viewController, навіть усередині viewController, самостійно

- Для одного перегляду:

view.overrideUserInterfaceStyle = .light

Ви можете встановити будь-яке view, навіть усередині перегляду його самостійно

Можливо, вам доведеться використовувати, if #available(iOS 13.0, *) { ,,, }якщо ви підтримуєте більш ранні версії iOS.


6

Окрім інших відповідей, наскільки я розумію наступне, вам потрібно підготуватися до темного режиму лише під час компіляції проти iOS 13 SDK (використовуючи XCode 11).

Система передбачає, що програми, пов’язані з iOS 13 або новішою версією SDK, підтримують світлий і темний вигляд. В iOS ви вказуєте конкретний зовнішній вигляд, призначивши певний стиль інтерфейсу своєму контролеру вікна, перегляду чи перегляду. Ви також можете повністю відключити підтримку темного режиму, використовуючи клавішу Info.plist.

Посилання


2

Так, ви можете пропустити, додавши наступний код у viewDidLoad:

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

2

Моя програма не підтримує темний режим на даний момент і використовує світлий колір панелі додатка. Мені вдалося примусити вміст рядка стану до темного тексту та піктограм, додавши до мене наступну клавішу Info.plist:

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

Знайдіть інші можливі значення тут: https://developer.apple.com/documentation/uikit/uistatusbarstyle


2

Версія Objective-c

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

1

Ось декілька порад та рекомендацій, які ви можете використовувати у своєму додатку для підтримки або обходу темного режиму.

Перша порада: щоб замінити стиль ViewController

ви можете замінити стиль інтерфейсу UIViewController на

1: overrideUserInterfaceStyle = .dark // Для темного режиму

2: overrideUserInterfaceStyle = .light // Для легкого режиму

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

Друга порада: додавання ключа в info.plist

Просто ви можете додати новий ключ

UIUserInterfaceStyle

у вашому додатку info.plist та встановіть його значення на Світле або Темне. це змінить стиль за замовчуванням програми на вказане вами значення. Вам не доведеться додавати overrideUserInterfaceStyle = .освітліть цей рядок у кожному viewController, лише один рядок у info.plist, це все.


1

Просто додайте у свій info.plistфайл наступний ключ :

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

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

Чи можете ви трохи пояснити, як ця відповідь вирішить проблему, замість того, щоб розміщувати відповідь лише для коду.
Арун Вінот

Так впевнено @ArunVinoth У IOS 13 вводиться темний режим, тому якщо ваша мета розгортання нижче 13, використовуйте наведений вище код, інакше ви можете використовувати просту заяву, записану в блоці.
Talha Расул

1

Швидкий 5

Два способи переключення темного в світлий режим:

1- info.plist

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

2- Програмно

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

0

Я б використав це рішення, оскільки властивість вікон може змінюватися протягом життєвого циклу програми. Тому призначення "overrideUserInterfaceStyle = .light" потрібно повторити. UIWindow.appearance () дозволяє нам встановити значення за замовчуванням, яке буде використовуватися для новостворених об'єктів UIWindow.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

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

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

      return true
    }
}

0

Просто додайте цей рядок у файл info.plist:

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

Це змусить додаток працювати лише в легкому режимі.


Про це вже коментували і відповіли багато разів. Про це говорить навіть прийнята відповідь. Тому цей коментар не додає нової інформації.
JeroenJK

0
import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

2
Будь ласка, додайте пояснення до своєї відповіді, відредагувавши її, щоб інші могли дізнатися з неї
Ніко Хааз,

0

Ви можете зробити: додати цей новий ключ UIUserInterfaceStyle до Info.plist і встановити його значення Light. і у режимі освітлення з'являється контрольний сигнал тривоги.

UIUserInterfaceStyle Light Якщо у вашій програмі застосовується режим світлого / темного світла, незалежно від налаштувань користувача, додавши ключ UIUserInterfaceStyle до файлу Info.plist і встановивши його значення на світло або темно.


0

На це запитання існує стільки відповідей, а скориставшись ним, info.plistви можете встановити його AppDelegateтак:

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

Тест на Xcode 11.3, iOS 13.3


-8

Насправді я просто написав якийсь код, який дозволить вам глобально відмовитися від темного режиму в коді без необхідності путцу з кожним контролером viw у вашій програмі. Це, ймовірно, може бути вдосконалено для відмови від класу за класом, керуючи списком класів. Для мене те, що я хочу, щоб мої користувачі бачили, чи подобається їм темний режим інтерфейсу для моєї програми, і якщо їм це не подобається, вони можуть вимкнути його. Це дозволить їм продовжувати використовувати темний режим для решти програм.

Вибір користувача хороший (Ахм, дивлячись на тебе Apple, саме так ти і мав це реалізувати).

Отже, як це працює, це лише категорія UIViewController. Коли він завантажується, він замінює нативний метод viewDidLoad на той, який перевіряє глобальний прапор, щоб побачити, чи не вимкнено темний режим для всього чи ні.

Оскільки він запускається під час завантаження UIViewController, він повинен автоматично запускатись та вимикати темний режим за замовчуванням. Якщо це не те, що ви хочете, тоді вам потрібно десь рано потрапити туди і встановити прапор, а також просто встановити прапор за замовчуванням.

Я ще не написав нічого, щоб відповісти на те, що користувач вмикає або вимикає прапор. Отже, це в основному приклад коду. Якщо ми хочемо, щоб користувач взаємодіяв з цим, всі контролери перегляду повинні буде перезавантажитись. Я не знаю, як це зробити напролом, але, ймовірно, відправлення якогось повідомлення збирається зробити трюк. Тож зараз цей глобальний режим увімкнення / вимкнення темного режиму працює лише при запуску або перезапуску програми.

Тепер недостатньо лише намагатися вимкнути темний режим у кожному окремому представленні MFING viewController у величезному додатку. Якщо ви використовуєте кольорові активи, то ви повністю виснажені. Ми протягом 10+ років розуміли незмінні предмети як непорушні. Кольори, які ви отримуєте з каталогу кольорових активів, говорять про те, що вони є UIColor, але вони є динамічними (змінні) кольорами і змінюватимуться під вами під час зміни системи від темного до світлого режиму. Це має бути особливістю. Але, звичайно, немає головного перемикання, щоб попросити ці речі припинити вносити ці зміни (наскільки я зараз знаю, можливо, хтось може це покращити).

Тож рішення складається з двох частин:

  1. публічна категорія на UIViewController, яка дає деякі корисні та зручні методи ... наприклад, я не думаю, що Apple задумався над тим, що деякі з нас змішують веб-код у наші додатки. Як такі, у нас є таблиці стилів, які потрібно змінювати на основі темного або світлого режиму. Таким чином, вам або потрібно побудувати якийсь об’єкт динамічного стилю (що було б добре), або просто запитати, що таке поточний стан (поганий, але легкий).

  2. ця категорія, коли вона завантажується, замінить метод viewDidLoad класу UIViewController і перехопить виклики. Я не знаю, чи це порушує правила магазину додатків. Якщо це так, можливо, є й інші способи, але ви можете вважати це доказом концепції. Наприклад, ви можете зробити один підклас усіх основних типів контролерів перегляду і зробити так, щоб усі ваші власні контролери перегляду успадковували їх, і тоді ви можете використовувати ідею категорії DarkMode і закликати її, щоб змусити вимкнути всі контролери перегляду. Це гірше, але порушувати правила не збирається. Я вважаю за краще використовувати час виконання, оскільки саме так було виконано час виконання. Тож у моїй версії ви просто додаєте категорію, ви встановлюєте глобальну змінну на категорію для того, чи хочете ви, щоб вона блокувала темний режим, і вона зробить це.

  3. Ви ще не вийшли з лісу, як уже згадувалося, інша проблема - це UIColor, в основному робить все, що чорт хоче. Тож навіть якщо ваші контролери перегляду блокують темний режим, UIColor не знає, де і як ви його використовуєте, тому не може адаптуватися. Як результат, ви можете отримати це правильно, але тоді він повернеться до вас в якийсь момент майбутнього. Може, скоро, можливо, пізніше. Тож обхід цього полягає в тому, щоб виділити його двічі за допомогою CGColor і перетворити його в статичний колір. Це означає, що якщо ваш користувач повернеться і знову ввімкне темний режим на вашій сторінці налаштувань (ідея тут полягає в тому, щоб зробити цю роботу так, щоб користувач мав контроль над вашим додатком над і над усією системою), всі ці статичні кольори потребують заміни. Поки що це залишається, щоб хтось інший вирішив. Найпростіший спосіб зробити це - зробити за замовчуванням те, що ви ' вимкнувши темний режим, розділіть на нуль, щоб вийти з програми, оскільки ви не можете вийти з нього та скажіть користувачеві просто перезапустити його. Це, ймовірно, також порушує правила використання магазину додатків, але це ідея.

Категорію UIColor не потрібно виставляти, вона просто працює з викликом colorNamed: ... якщо ви не сказали класу DarkMode ViewController блокувати темний режим, він буде працювати прекрасно, як очікувалося. Намагаючись зробити щось елегантне замість стандартного коду яблучного сфагетті, що означає, що вам доведеться змінити більшість додатків, якщо ви хочете програмно вимкнути темний режим або переключити його. Тепер я не знаю, чи є кращий спосіб програмної зміни програми Info.plist для відключення темного режиму за необхідності. Наскільки я розумію, це функція часу компіляції, і після цього ви змушені.

Отже ось код, який вам потрібен. Слід запустити і просто використовувати один метод, щоб встановити стиль інтерфейсу або встановити за замовчуванням у коді. Ви можете користуватися, змінювати, робити все, що завгодно, з будь-якими цілями, і гарантії не даються, і я не знаю, чи пройде це магазин додатків. Покращення дуже вітаються.

Справедливе попередження: я не використовую ARC або будь-які інші методи тримання.

////// H file

#import <UIKit/UIKit.h>

@interface UIViewController(DarkMode)

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

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

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

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

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

@end


////// M file


//
//  QDarkMode.m

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


@implementation UIViewController(DarkMode)

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

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

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

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

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

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

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


@end

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

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

@implementation UIColor (DarkMode)

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


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

Існує набір функцій утиліти, які використовується для заміни методів. Окремий файл. Це стандартні речі, але подібний код ви можете знайти де завгодно.

// q-runtime.h

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

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

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


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

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


// q-runtime.m

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

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


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

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

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

Я копіюю та вставляю це з декількох файлів, оскільки q-runtime.h - це моя багаторазова бібліотека, і це лише частина її. Якщо щось не складається, дайте мені знати.


Ви не пощастило , коли справа доходить до контролю поведінки UIColor, як описано в цьому питанні: stackoverflow.com/questions/56487679 / ...
raven_raven
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.