Можно ли отказаться от темного режима на iOS 13?

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

Можно ли отказаться от поддержки темного режима, чтобы наше приложение всегда отображало светлый режим в соответствии с темой веб-сайта?

Установите UIUserInterfaceStyle на Light в вашем Info.Plist. См. developer.apple.com/library/archive/documentation/General/…

Tieme 20.08.2019 16:01

Спасибо за вопрос - для всех нас. Много приложений, чтобы пройти. Это необходимо, чтобы приложения работали до тех пор, пока переключатель не будет готов.

user3741598 27.09.2019 01:43

import Foundation import UIKit extension UIViewController { override open func awakeFromNib() { super.awakeFromNib() if # available(iOS 13.0, *) { // Всегда используйте облегченный стиль интерфейса. overrideUserInterfaceStyle = .light } } }

Mohammad Razipour 27.09.2019 09:19

просто добавьте UIUserInterfaceStyle в plist. это так просто

Fattie 08.10.2019 14:51

При отправке приложения в магазин приложений Apple принимает из-за UIUserInterfaceStyle в облегченном режиме.

kiran 31.10.2019 05:28

@kiran - Да, только что сделал это для сборки TF.

benc 26.05.2020 09:44

Я думаю, что мы не можем отключить всплывающие окна разрешений и другие системные оповещения. у кого-нибудь есть решение для этого или это невозможно, потому что ОС управляет этим?

Shrikant Phadke 13.10.2020 15:28
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
444
7
207 249
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

Думаю, я нашел решение. Сначала я собрал его по кусочкам из UIUserInterfaceStyle — список информационных свойств и UIUserInterfaceStyle — UIKit, но теперь обнаружил, что он фактически задокументирован в Выбор определенного стиля интерфейса для вашего приложения iOS.

В своем info.plist установите UIUserInterfaceStyle (Стиль пользовательского интерфейса) на 1 (UIUserInterfaceStyle.light).

Обновлено: Согласно ответу dorbeetle, более подходящей настройкой для UIUserInterfaceStyle может быть Light.

Однако принудительное использование темного режима путем установки значения 2 не работает: [UIInterfaceStyle] '2' is not a recognized value for UIUserInterfaceStyle. Defaulting to Light.

funkenstrahlen 11.06.2019 11:09

Наличие этого ключа в plist приведет к отклонению App Store.

José 20.08.2019 09:53

AppStore больше не отклоняет это свойство в plist.info. Я поставил «Темный» (с большой буквы), так как наше приложение уже темное. Нет проблем. Это правильно позволяет нам использовать элементы управления системой.

nickdnk 25.09.2019 18:31

@nickdnk Я думаю, вы создали свое приложение с помощью Xcode 11, рекомендованного Apple.

DawnSong 26.09.2019 04:04

Да, я сделал. Это не меняет того факта, что Apple принимает этот параметр в plist, что я и пытался прояснить.

nickdnk 26.09.2019 13:06

Пробовал ставить "LIGHT", "Light", "light", ".light", "1" - все одно и то же - не работает. Xcode: версия 11.3.1 (11C504)

Andrew 16.01.2020 19:53

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

Как уже упоминал SeanR, вы можете установить UIUserInterfaceStyle на Light или Dark в plist-файле вашего приложения, чтобы изменить это для всего приложения.

Если вы установите ключ UIUserInterfaceStyle, ваше приложение будет отклонено в магазине приложений.

Sonius 20.08.2019 09:44

Apple отклонена с кодом ошибки ITMS-90190 forums.developer.apple.com/thread/121028

PRASAD1240 20.08.2019 16:04

Скорее всего, отказ произойдет из-за того, что бета-версия iOS 13 SDK еще не вышла. Я думаю, что это должно работать, как только Xcode 11 GM будет доступен.

dorbeetle 21.08.2019 11:24

@dorbeetle это неправда, я успешно загрузил свое приложение с этим ключом, как 1 месяц назад с Xcode 10. Отказы происходят недавно. Кажется, какая-то новая стратегия Apple.

steven 28.08.2019 07:09

Странно слышать об отказе. Что касается документации ios 13, в ней говорится, что установите ключ «UIUserInterfaceStyle» в вашем Info.plist.

dpart 05.09.2019 11:59

Это все еще происходит. Xcode GM2 вернул ошибку подписи приложения. Xcode 10.3 вернул: «Недопустимый ключ Info.plist. Ключ« UIUserInterfaceStyle »в файле Payload/Galileo.appInfo.plist недействителен».

Evgen Bodunov 20.09.2019 22:29
Ответ принят как подходящий

Во-первых, вот Вход Apple, связанный с отказом от темного режима. Контент по этой ссылке написан для Xcode 11 и iOS 13.:

Все приложение через файл info.plist (Xcode 12)

Используйте следующий ключ в файле info.plist:

UIUserInterfaceStyle

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

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

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

Документация Apple для UIUserInterfaceStyle


Все приложение через info.plist в настройках сборки (Xcode 13)


Все окно приложения через свойство окна

Вы можете установить overrideUserInterfaceStyle для переменной window приложения. Это будет применяться ко всем видам, которые появляются в окне. Это стало доступно в iOS 13, поэтому для приложений, поддерживающих предыдущие версии, необходимо включить проверку доступности.

В зависимости от того, как был создан ваш проект, это может быть файл AppDelegate или SceneDelegate.

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

Индивидуальный UIViewController или UIView

Вы можете установить overrideUserInterfaceStyle против переменной UIViewControllers или UIView's overrideUserInterfaceStyle. Это стало доступно в iOS 13, поэтому для приложений, поддерживающих предыдущие версии, необходимо включить проверку доступности.

Swift

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

For those poor souls in Objective-C

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

При установке против UIViewController контроллер представления и его дочерние элементы принимают определенный режим.

При установке против UIView представление и его дочерние элементы принимают определенный режим.

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


Индивидуальные представления через SwiftUI View

Вы можете установить preferredColorScheme как light или dark. Предоставленное значение установит цветовую схему для презентации.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Light Only")
            .preferredColorScheme(.light)
    }
}

Документация Apple для PreferredColorScheme


Спасибо @Арон Нельсон, @Раймундас Сакалаускас, @NSLeader и @rmaddy за улучшение этого ответа благодаря их отзывам.

Индикатор UIUserInterfaceStyle теперь блокируется при обновлении/загрузке вашего приложения. Он помечен как недопустимая запись plist. (Неверный plist-ключ)

Aron Nelson 15.08.2019 20:20

Это не будет компилироваться с iOS SDK 12 (в настоящее время последний стабильный SDK). См. stackoverflow.com/a/57521901/2249485 решение, которое будет работать и с iOS 12 SDK.

Raimundas Sakalauskas 16.08.2019 11:13

Это настолько несправедливо, что вопрос, который имеет гораздо больше просмотров, чем «исходный вопрос», заблокирован для предоставления ответов. :(

Raimundas Sakalauskas 18.09.2019 14:35

Я только что загрузил запись .plist, и она прошла.. очень интересно, как это воспримет обзор... Хм

thisIsTheFoxe 19.09.2019 00:14

Вместо того, чтобы устанавливать overrideUserInterfaceStyle в viewDidLoad каждого контроллера представления, вы можете установить его один раз в главном окне приложения. Гораздо проще, если вы хотите, чтобы все приложение вело себя одним образом.

rmaddy 19.09.2019 09:38

Я предполагаю, что ошибка App Store, опубликованная выше, была ошибкой со стороны Apple. Я только что загрузил приложение сегодня с этим plist-ключом, и все прошло нормально.

Bek 23.09.2019 23:02

Xcode 11.0 (11A420a), загруженный из магазина приложений (не GM seed).

Bek 23.09.2019 23:54

@Bek Хорошо, извините, я не сказал об этом явно, но это сообщение об ошибке относится только к Xcode 10.

CodeBender 24.09.2019 00:43

Используйте #if compiler(>=5.1) вместо responds(to:) и setValue

NSLeader 24.09.2019 13:28

как насчет стиля строки состояния? он показывает белый в темном режиме

Rohit Funde 01.10.2019 11:15

@thisIsTheFoxe Есть обновления? Мне любопытно, смогу ли я использовать это для наших живых приложений, поскольку у нас пока нет планов по его поддержке, а части нашего приложения «ломаются», например, tableViews получают темный фон, а текст также темный.

Jeroen 04.10.2019 11:18

@JeroenJK Я думаю, ты должен быть в порядке. Если вы посмотрите, вы также сможете отказаться прямо из раскадровки, поэтому не должно быть невозможно пройти проверку AS. Но вы определенно должны рассмотреть это - imo :)

thisIsTheFoxe 04.10.2019 13:16

Да, я успешно развернул новые версии наших приложений. :-)

Jeroen 10.10.2019 12:43

Я попытался изменить все приложение с помощью xcode 10.5. Но получил "Функционально-подобный макрос "компилятор" не определен".

7xRobin 12.12.2019 17:43

Ключ в Info.plist изменился на Appearance. <key> Appearance</key> <string>Light</string>

huync 18.03.2021 04:49

В более новых версиях Info.plist добавляет внешний вид: светлый/темный.

Amit Khetan 31.03.2021 22:59

На самом деле я только что написал некоторый код, который позволит вам глобально отказаться от темного режима в коде без необходимости возиться с каждым контроллером viw в вашем приложении. Это, вероятно, можно уточнить, чтобы отказаться от класса за классом, управляя списком классов. Что касается меня, то я хочу, чтобы мои пользователи видели, нравится ли им интерфейс темного режима для моего приложения, и если им это не нравится, они могут его отключить. Это позволит им продолжать использовать темный режим для остальных своих приложений.

Пользовательский выбор хорош (кхм, глядя на тебя, Apple, вот как ты должен был это реализовать).

Итак, как это работает, так это то, что это просто категория UIViewController. Когда он загружается, он заменяет собственный метод viewDidLoad на тот, который будет проверять глобальный флаг, чтобы увидеть, отключен ли темный режим для всего или нет.

Поскольку он запускается при загрузке UIViewController, он должен автоматически запускаться и отключать темный режим по умолчанию. Если это не то, что вы хотите, то вам нужно попасть туда где-то пораньше и установить флаг, либо просто установить флаг по умолчанию.

Я еще ничего не написал в ответ на то, что пользователь включает или выключает флаг. Итак, это в основном пример кода. Если мы хотим, чтобы пользователь взаимодействовал с этим, все контроллеры представления должны будут перезагрузиться. Я не знаю, как это сделать навскидку, но, вероятно, отправка какого-либо уведомления поможет. Так что прямо сейчас это глобальное включение/выключение темного режима будет работать только при запуске или перезапуске приложения.

Теперь недостаточно просто попытаться отключить темный режим в каждом MFING viewController в вашем огромном приложении. Если вы используете цветовые активы, вы полностью обречены. Мы уже более 10 лет понимаем, что неизменяемые объекты неизменны. Цвета, которые вы получаете из каталога цветовых активов, говорят, что они UIColor, но они являются динамическими (изменяемыми) цветами и будут меняться под вами, когда система переходит из темного в светлый режим. Это должно быть особенностью. Но, конечно, нет главного переключателя, чтобы попросить эти вещи прекратить вносить это изменение (насколько я знаю прямо сейчас, может быть, кто-то может это улучшить).

Итак, решение состоит из двух частей:

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

  2. эта категория при загрузке заменит метод viewDidLoad класса UIViewController и вызовы перехвата. Я не знаю, нарушает ли это правила магазина приложений. Если это так, возможно, есть и другие способы обойти это, но вы можете считать это доказательством концепции. Например, вы можете создать один подкласс всех основных типов контроллеров представлений и сделать все ваши собственные контроллеры представлений унаследованными от них, а затем вы можете использовать идею категории DarkMode и вызвать ее, чтобы принудительно отказаться от всех ваших контроллеров представлений. Это уродливее, но не нарушает никаких правил. Я предпочитаю использовать среду выполнения, потому что именно для этого она и была создана. Итак, в моей версии вы просто добавляете категорию, устанавливаете глобальную переменную для категории, хотите ли вы, чтобы она блокировала темный режим, и она это сделает.

  3. Как уже упоминалось, вы еще не вышли из леса, другая проблема заключается в том, что UIColor в основном делает все, что, черт возьми, хочет. Таким образом, даже если ваши контроллеры представления блокируют темный режим, UIColor не знает, где и как вы его используете, поэтому не может адаптироваться. В результате вы можете получить его правильно, но в какой-то момент в будущем он вернется к вам. Может скоро может позже. Таким образом, способ обойти это — выделить его дважды, используя CGColor, и превратить его в статический цвет. Это означает, что если ваш пользователь вернется и снова включит темный режим на вашей странице настроек (идея здесь состоит в том, чтобы заставить это работать так, чтобы пользователь имел контроль над вашим приложением над остальной частью системы), все эти статические цвета нуждаются в замене. Пока это остается решать кому-то другому. Самый простой способ сделать это — сделать по умолчанию, что вы отказываетесь от темного режима, разделить на ноль, чтобы разбить приложение, поскольку вы не можете выйти из него, и попросить пользователя просто перезапустить его. Это, вероятно, также нарушает правила магазина приложений, но это идея.

Категорию UIColor не нужно раскрывать, она просто работает, вызывая colorNamed: ... если вы не указали классу DarkMode ViewController блокировать темный режим, он будет работать прекрасно, как и ожидалось. Попытка сделать что-то элегантное вместо стандартного кода Apple sphaghetti, что будет означать, что вам придется изменить большую часть вашего приложения, если вы хотите программно отказаться от темного режима или переключить его. Теперь я не знаю, есть ли лучший способ программного изменения 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 05.09.2019 15:06

Ответ выше работает, если вы хотите отказаться от всего приложения. Если вы работаете над библиотекой с пользовательским интерфейсом, и у вас нет возможности редактировать .plist, вы также можете сделать это с помощью кода.

Если вы компилируете iOS 13 SDK, вы можете просто использовать следующий код:

Быстрый:

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

Объект-C:

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

ОДНАКО, если вы хотите, чтобы ваш код также компилировался с SDK для iOS 12 (который на данный момент все еще является последним стабильным SDK), вам следует прибегнуть к использованию селекторов. Код с селекторами:

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

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

Объект-C:

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

Будет лучше, если вы уточните, к какому объекту относится overrideUserInterfaceStyle.

DawnSong 26.09.2019 16:25

Если вы добавите ключ UIUserInterfaceStyle в файл plist, возможно, Apple отклонит сборку выпуска, как указано здесь: https://stackoverflow.com/a/56546554/7524146 В любом случае, это раздражает явно указывать каждый ViewControllerself.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.

Помимо других ответов, насколько я понимаю следующее, вам нужно только подготовиться к темному режиму при компиляции с iOS 13 SDK (с использованием XCode 11).

The system assumes that apps linked against the iOS 13 or later SDK support both light and dark appearances. In iOS, you specify the specific appearance you want by assigning a specific interface style to your window, view, or view controller. You can also disable support for Dark Mode entirely using an Info.plist key.

Связь

Последнее обновление-

Если вы используете Xcode 10.x, то UIUserInterfaceStyle по умолчанию — light для iOS 13.x. При запуске на устройстве iOS 13 он будет работать только в облегченном режиме.

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

Invalid Info.plist Key. The key 'UIUserInterfaceStyle' in the Payload/AppName.appInfo.plist file is not valid.

Добавляйте ключ UIUserInterfaceStyle в файл Info.plist только при использовании Xcode 11.x.

Это не имеет ничего общего с Xcode 10 или 11. Если пользователь развертывает приложение из Xcode 10 и не позаботится о темном режиме, приложение при установке на iPhone 11, Pro или Pro Max будет иметь проблемы с темным режимом. вам нужно обновить до Xcode 11 и решить эту проблему.

Niranjan Molkeri 16.09.2019 20:35

@NiranjanMolkeri Это не имеет ничего общего с новыми iPhone. Речь идет о темном режиме в iOS 13. В предыдущих бета-версиях iOS 13 пользовательский интерфейс приложений имел проблемы с темным режимом, если они не обрабатывались явно. Но в последней версии это исправлено. Если вы используете XCode 10, то UIUserInterfaceStyle по умолчанию является легким для iOS13. Если вы используете Xode11, вам нужно с этим справиться.

kumarsiddharth123 17.09.2019 09:28

У вас возникнут проблемы, если вы загрузите приложение в TestFligth с помощью Xcode 10.3, а plist включает ключ UIUserInterfaceStyle. Он скажет, что это недопустимый файл plist. Вы должны либо удалить его при сборке в Xcode 10, либо загрузить с помощью Xcode 11.

eharo2 23.10.2019 21:45

Для всего приложения: (в файле info.plist):

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

plist


Окно (обычно все приложение):

window!.overrideUserInterfaceStyle = .light

Вы можете получить окно от SceneDelegate


UIViewController:

viewController.overrideUserInterfaceStyle = .light

Вы можете установить любой viewController, даже внутри viewController itсебя


UIView:

view.overrideUserInterfaceStyle = .light

Вы можете установить любой view, даже внутри вида себя

Возможно, вам придется использовать if #available(iOS 13.0, *) { ,,, }, если вы поддерживаете более ранние версии iOS.


Просмотр SwiftUI:

.preferredColorScheme(.light) <- This Modifier

или

.environment(\.colorScheme, .light) <- This Modifier

Если вы не используете Xcode 11 или более позднюю версию (т. е. iOS 13 или более позднюю версию SDK), ваше приложение не выбрало автоматически поддержку темного режима. Таким образом, нет необходимости отказываться от темного режима.

Если вы используете Xcode 11 или более позднюю версию, система автоматически включила темный режим для вашего приложения. Есть два подхода к отключению темного режима в зависимости от ваших предпочтений. Вы можете отключить его полностью или отключить для любого конкретного окна, представления или контроллера представления.

Полностью отключите темный режим для вашего приложения

Вы можете отключить темный режим, включив ключ UIUserInterfaceStyle со значением Light в файл Info.plist вашего приложения.
UIUserInterfaceStyle as Light
Это игнорирует предпочтения пользователя и всегда применяет светлый вид к вашему приложению.

Отключить темный режим для окна, представления или контроллера представления

Вы можете заставить свой интерфейс всегда отображаться в светлом или темном стиле, установив свойство 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

Note: Apple strongly encourages to support dark mode in your app. So, you can only disable dark mode temporarily.

Подробнее здесь: Выбор определенного стиля интерфейса для вашего приложения iOS

В XCode 12.4 он отображался как «Внешний вид», а не как стиль пользовательского интерфейса.

CSchwarz 29.04.2021 14:08

Да, вы можете пропустить, добавив следующий код в viewDidLoad:

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

Мое приложение пока не поддерживает темный режим и использует светлый цвет панели приложений. Мне удалось сделать содержимое строки состояния темным текстом и значками, добавив следующий ключ к моему 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

Пользователи флаттера

Не забудьте установить атрибут яркости панели приложений на панели приложений Flutter следующим образом:

AppBar(
    backgroundColor: Colors.grey[100],
    brightness: Brightness.light, // <---------
    title: const Text('Hi there'),
),

********** Самый простой способ для Xcode 11 и выше *************

Добавьте это в info.plist перед </dict></plist>

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

это решение не удастся при отправке приложения на Xcode 10.x

Tawfik Bouabid 23.10.2019 09:33

Я бы использовал это решение, поскольку свойство окна может быть изменено в течение жизненного цикла приложения. Поэтому назначение «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
    }
}

Вот несколько советов и приемов, которые вы можете использовать в своем приложении для поддержки или обхода темного режима.

Первый совет: переопределить стиль ViewController

вы можете переопределить стиль интерфейса UIViewController с помощью

1: overrideUserInterfaceStyle = .dark //For dark mode

2: overrideUserInterfaceStyle = .light //For light mode

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

Второй совет: добавление ключа в info.plist

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

UIUserInterfaceStyle

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

Просто добавьте следующий ключ в свой файл info.plist:

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

Вы можете отключить Темный режим во всем приложении в Xcode 11:

  1. Перейти Info.plist
  2. Добавьте ниже лайк

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

Info.plist будет выглядеть следующим образом...

по какой-то причине не работает для версии Xcode 11.3.1 (11C504)

Andrew 16.01.2020 19:43

Просто добавьте эту строку в файл info.plist:

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

Это заставит приложение работать только в облегченном режиме.

Это уже было прокомментировано и ответили много раз. Даже принятый ответ предполагает это. Поэтому этот комментарий не добавляет никакой новой информации.

Jeroen 18.12.2019 13:22
 if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light
        } else {
            // Fallback on earlier versions
        }

Можете ли вы немного объяснить, как этот ответ решит проблему, вместо того, чтобы публиковать ответ только для кода.

Arun Vinoth - MVP 26.12.2019 18:19

Да, конечно @ArunVinoth В IOS 13 представлен темный режим, поэтому, если ваша цель развертывания ниже 13, используйте приведенный выше код, иначе вы можете использовать простой оператор, написанный в блоке if.

Talha Rasool 27.12.2019 19:01

Версия Objective-C

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

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}

Пожалуйста, добавьте объяснение к своему ответу, отредактировав его, чтобы другие могли извлечь из него уроки.

Nico Haase 07.04.2020 08:45

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

UIUserInterfaceStyle Свет Если вы используете светлый/темный режим во всем приложении независимо от пользовательских настроек, добавьте ключ UIUserInterfaceStyle в файл Info.plist и установите для него значение Light или Dark.

У этого вопроса так много ответов, что, используя его в info.plist, вы можете установить его в AppDelegate следующим образом:

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

Тест на Xcode 11.3, iOS 13.3

Свифт 5

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

1- info.plist

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

2- Programmatically or Runtime

  @IBAction private func switchToDark(_ sender: UIButton){
        UIApplication.shared.windows.forEach { window in
            //here you can switch between the dark and light
            window.overrideUserInterfaceStyle = .dark
        }
    }

Обновление Xcode 12 и iOS 14. Я попробовал предыдущие варианты отказа от темного режима, и это предложение в файле info.plist у меня не работает:

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

Теперь он переименован в:

<key>Appearance</key>
<string>Light</string>

Этот параметр заблокирует все темные режимы в полном приложении.

ОТРЕДАКТИРОВАНО:

Исправлена ​​опечатка, спасибо @sarah

немного опечатка, должно быть Внешний вид :)

sarah 06.11.2020 10:23

В Xcode 12 вы можете изменить добавление как «внешний вид». Это сработает!!

Добавьте это в info.plist

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

Обновление iOS 14.3 и Xcode 12.3

В файле info.plist добавьте Появление как Свет.

<key>Appearance</key>
<string>Light</string>

@Yodagama, пожалуйста, проверьте версию iOS вашего симулятора и версию Xcode. Это должно работать нормально, я только что проверил это на своем симуляторе.

alvin 23.12.2020 14:06

Он отлично работает в Xcode 12.3 и iOS 14.3. Для вашей версии попробуйте следующий <key>UIUserInterfaceStyle</key> <string>Light</string>

alvin 23.12.2020 14:10

Да .. вы можете добавить настройку ниже в проект iOS.

В файле info.plist добавьте UIUserInterfaceStyle в Light.

Если ваш проект находится в IONIC. Вы можете добавить настройку ниже в файле конфигурации.

<platform name = "ios">
  <edit-config file = "*-Info.plist" mode = "merge" target = "UIUserInterfaceStyle">
  <string>Light</string>
 </edit-config>
</platform>

При использовании этих настроек темный режим устройства не повлияет на ваше приложение.

добавьте overrideUserInterfaceStyle = .light в файл ViewController.swift или измените внешний вид на «светлый» в файле info.plist

Другие вопросы по теме