我的应用程序很大一部分由Web视图组成,以提供本机实现尚不可用的功能。 Web团队没有计划为网站实施深色主题。因此,我的应用在iOS 13上具有暗模式支持时看起来会一半/一半。主题?

评论

在您的Info.Plist中将UIUserInterfaceStyle设置为Light。请参阅developer.apple.com/library/archive/documentation/General/…

感谢您的询问-我们所有人。有很多应用程序要经历。这是保持应用程序运行到切换准备就绪所必需的。

import Foundation import UIKit扩展UIViewController {重写open func awakeFromNib(){super.awakeFromNib()if #available(iOS 13.0,*){//始终采用浅色界面样式。 overrideUserInterfaceStyle = .light}}}

只需在plist中添加UIUserInterfaceStyle。就这么简单

在将应用程序提交到appstore时,由于处于Light模式的UIUserInterfaceStyle,Apple会接受。

#1 楼

首先,这是与退出暗模式相关的Apple条目。
此链接的内容适用于Xcode 11和iOS 13:

此部分适用于Xcode 11的用法




如果您希望退出整个应用程序


方法#1

请在info.plist文件中使用以下密钥:

UIUserInterfaceStyle


,并为其分配值Light

UIUserInterfaceStyle分配的XML: br />方法#2

您可以针对应用程序的overrideUserInterfaceStyle变量设置window。 。

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





如果您希望逐个退出UIViewController


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


Apple文档overrideUserInterfaceStyle

上面的代码在Xcode 11中的外观:



/>本节适用于Xcode 10.x用法



如果您正在使用Xcode 11进行提交,则可以放心忽略此行下的所有内容。

由于iOS 12中不存在相关的API,因此在尝试使用上面提供的值时会出错:

用于在AppDelegate中设置SceneDelegate




如果您希望逐个退出UIViewController,


可以通过测试编译器版本和iOS版本在Xcode 10中进行处理:

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



如果愿意退出整个应用程序


您可以修改上述代码段,使其适用于Xcode 10的整个应用程序,方法是在overrideUserInterfaceStyle文件中添加以下代码。

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


但是,使用Xcode版本10.x时plist设置将失败:



感谢@Aron Nelson,@ Raimundas Sakalauskas,@ NSLeader和rmaddy通过他们的反馈来改善此答案。

评论


立即更新/上传应用程序时,UIUserInterfaceStyle指示灯被阻止。它被标记为无效的plist条目。 (无效的plist键)

–阿伦·尼尔森(Aron Nelson)
19年8月15日在18:20



无法针对iOS SDK 12(当前最新的稳定SDK)进行编译。请参阅stackoverflow.com/a/57521901/2249485,以获得适用于iOS 12 SDK的解决方案。

– Raimundas Sakalauskas
19年8月16日在9:13

这太不公平了,以至于比“原始问题”具有更多视图的问题被锁定以提供答案。 :(

– Raimundas Sakalauskas
19-09-18在12:35

您可以在应用程序的主窗口中将其设置一次,而不是在每个视图控制器的viewDidLoad中设置overrideUserInterfaceStyle。如果您想让整个应用程序表现为一种方式,那就容易得多。

–rmaddy
19-09-19在7:38

使用#if编译器(> = 5.1)代替response(to :)和setValue

– NSLeader
19年9月24日在11:28

#2 楼

根据苹果公司关于“在iOS上实现暗模式”的会议(https://developer.apple.com/videos/play/wwdc2019/214/,从31:13开始),可以在任何视图上将overrideUserInterfaceStyle设置为UIUserInterfaceStyleLightUIUserInterfaceStyleDark控制器或视图,将在traitCollection中用于任何子视图或视图控制器。

正如SeanR所提到的,您可以在应用程序的plist文件中将UIUserInterfaceStyle设置为LightDark,以将其更改为您整个应用程序。

评论


如果您设置UIUserInterfaceStyle键,则您的应用将在应用商店中被拒绝

– Sonius
19年8月20日在7:44

苹果拒绝使用ITMS-90190错误代码forums.developer.apple.com/thread/121028

– PRASAD1240
19年8月20日在14:04



拒绝最有可能发生,因为iOS 13 SDK尚未超出Beta版。我认为这应该在Xcode 11 GM可用后立即生效。

– dorbeetle
19年8月21日在9:24

@dorbeetle,这不是真的,我像1个月前用Xcode 10一样成功地用此密钥上传了我的应用程序。拒绝是最近发生的。苹果似乎采取了一些新的策略。

–steven
19年8月28日在5:09



它仍在发生。 Xcode GM2返回了应用程序签名错误。 Xcode 10.3返回:“无效的Info.plist密钥。有效载荷/Galileo.appInfo.plist文件中的密钥'UIUserInterfaceStyle'无效。

– Evgen Bodunov
19-09-20在20:29

#3 楼

如果您未使用Xcode 11或更高版本(即iOS 13或更高版本的SDK),则您的应用尚未自动选择支持暗模式。因此,无需退出暗模式。

如果您使用的是Xcode 11或更高版本,则系统会自动为您的应用启用暗模式。有两种方法可以根据您的喜好禁用暗模式。您可以完全禁用它,也可以对任何特定的窗口,视图或视图控制器禁用它。

完全为您的应用禁用暗模式

您可以通过在应用的Info.plist文件中包含UIUserInterfaceStyle键(其值为Light)来禁用暗模式。
这会忽略用户的偏好,并始终为您的应用程序添加浅色外观。

禁用Window,View或View Controller的深色模式

您可以强制通过设置适当的窗口,视图或视图控制器的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



注意:苹果强烈建议您在应用中支持暗模式。因此,
您只能暂时禁用黑暗模式。


此处更多:为iOS应用选择特定的界面样式

#4 楼

********** Xcode 11及更高版本的最简单方法***********

</dict></plist>之前将其添加到info.plist

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


评论


在Xcode 10.x上提交应用程序时,此解决方案将失败

– Tawfik Bouabid
19-10-23在7:33

#5 楼

我想我已经找到了解决方案。我最初是通过UIUserInterfaceStyle-信息属性列表和UIUserInterfaceStyle-UIKit进行拼凑而成的,但现在发现它确实记录在为iOS应用选择特定界面样式时。

info.plist中,设置UIUserInterfaceStyle(接口样式)到1(UIUserInterfaceStyle.light)。

编辑:根据dorbeetle的回答,对于UIUserInterfaceStyle更为合适的设置可能是Light

评论


但是,通过将值设置为2来强制使用暗模式是不起作用的:[UIInterfaceStyle]'2'不是UIUserInterfaceStyle的可识别值。默认为Light。

–funkenstrahlen
19年6月11日在9:09

在plist中拥有此密钥将导致App Store拒绝。

–何塞
19年8月20日在7:53

AppStore不再在plist.info中拒绝此属性。我把“ Dark”(大写)放进去,因为我们的应用程序已经很暗。没问题。这可以让我们正确使用系统控件。

– nickdnk
19-09-25在16:31



是的,我做到了。 Apple确实在plist中接受了此参数,这一事实并没有改变,这就是我试图阐明的事实。

– nickdnk
19-09-26在11:06

我尝试设置“ LIGHT”,“ Light”,“ light”,“。light”,“ 1”-都是一样的-它不起作用。 Xcode:版本11.3.1(11C504)

–安德鲁(Andrew)
20 Jan 16 '18:53



#6 楼

如果您想退出整个应用程序,则上述答案有效。如果您使用的是具有UI的lib,并且您对.plist的编辑并不奢侈,那么也可以通过代码来实现。

如果要针对iOS 13 SDK进行编译,则只需使用以下代码即可:

Swift:

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属于什么会更好。

–黎明之歌
19-09-26在14:25

#7 楼

Xcode 12和iOS 14更新。我已经尝试了以前的选项来选择退出暗模式,而info.plist文件中的这句话对我不起作用: >此设置将阻止整个应用程序中的所有黑暗模式。
编辑:
修正错字谢谢@sarah

评论


有点错字,应该是外观:)

–萨拉
20-11-06在9:23



固定!谢谢@Sarah :)

– Wazowski
20年11月6日,12:07

工作和更新的解决方案

–纳粹艾哈迈德(Naveed Ahmad)
昨天

#8 楼

您可以在Xcode 11中的整个应用程序中关闭“暗模式”:


转到Info.plist

添加波纹管,如

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



Info.plist如下所示...



评论


由于某些原因不适用于Xcode 11.3.1(11C504)

–安德鲁(Andrew)
20 Jan 16 '20 at 18:43

#9 楼

最新更新-

如果您使用的是Xcode 10.x,则iOS 13.x的默认UIUserInterfaceStylelight。在iOS 13设备上运行时,它将仅在灯光模式下工作。

无需在Info.plist文件中显式添加UIUserInterfaceStyle密钥,添加它会在验证应用程序时出现错误,提示:


无效信息.plist键。 Payload / AppName.appInfo.plist文件中的键'UIUserInterfaceStyle'无效。


使用Xcode 11.x时,仅在Info.plist文件中添加UIUserInterfaceStyle键。 />

评论


这与Xcode 10或11无关。如果用户部署Xcode 10形式的应用程序并且不处理暗模式,则该应用程序安装在iPhone 11,Pro或Pro Max中时将出现暗模式问题。您需要更新到Xcode 11并解决此问题。

– Niranjan Molkeri
19-09-16在18:35

@NiranjanMolkeri这与较新的iPhone无关。这与iOS 13上的暗模式有关。在以前的iOS 13 Beta版本中,如果未明确处理,则UI会出现暗模式问题。但是在最新版本中,此问题已修复。如果您使用的是XCode 10,则iOS13的默认UIUserInterfaceStyle是light。如果您使用的是Xode11,则需要进行处理。

–kumarsiddharth123
19-09-17在7:28

如果使用Xcode 10.3将应用程序上载到TestFligth,并且plist包含键UIUserInterfaceStyle,则会遇到问题。它将说这是一个无效的plist文件。如果在Xcode 10中进行构建,则必须将其删除,或者使用Xcode 11进行上传

– eharo2
19-10-23在19:45

#10 楼

如果将UIUserInterfaceStyle密钥添加到plist文件中,则Apple可能会拒绝此处所述的发行版本:https://stackoverflow.com/a/56546554/7524146
无论如何,明确告诉每个ViewController self.overrideUserInterfaceStyle = .light都是很烦的。但是,您可以对根window对象使用一次此和平代码:在那个早期。但是您以后可以做。如果您在应用程序中使用自定义application(application: didFinishLaunchingWithOptions:)true类,而不是自动在AppDelegate中启动UI,那么这非常容易。

#11 楼

-对于整个应用程序(窗口):

window!.overrideUserInterfaceStyle = .light


您可以从SceneDelegate获取窗口

-对于单个ViewController:

viewController.overrideUserInterfaceStyle = .light


即使在viewController本身内,也可以设置任何viewController

-对于单个视图: >
即使在视图本身内,您也可以设置任何view

如果支持较早的iOS版本,则可能需要使用if #available(iOS 13.0, *) { ,,, }

#12 楼

除了其他响应之外,据我了解,您仅需要在针对iOS 13 SDK(使用XCode 11)进行编译时为Dark模式做准备。


系统假定应用程序已链接针对iOS 13或更高版本的SDK
支持亮色和深色外观。在iOS中,可以通过为窗口,视图或视图控制器分配特定的界面样式
来指定所需的特定外观。您还可以完全使用Info.plist键禁用对黑暗模式的支持。


链接

#13 楼

Swift 5
将暗模式切换为亮模式的两种方法:

1- info.plist

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



2-以编程方式或运行时

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


#14 楼

我的应用程序目前不支持深色模式,并使用浅色应用程序栏颜色。通过将以下密钥添加到我的Info.plist中,我能够将状态栏内容强制为深色文本和图标: / uikit / uistatusbarstyle
Flutter用户
别忘了在Flutter应用栏上设置应用栏亮度属性,如下所示:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDarkContent</string>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>


#15 楼

是的,您可以在viewDidLoad中添加以下代码来跳过:

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


#16 楼

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


评论


您能否解释一下此答案将如何解决问题,而不是发布仅代码的答案。

– Arun Vinoth
19/12/26在17:19

是的,肯定@ArunVinoth在IOS 13中引入了暗模式,因此,如果您的部署目标低于13,则使用上述代码,否则您可以使用if编写的简单语句。

–塔拉·拉索尔(Talha Rasool)
19/12/27在18:01

#17 楼

Objective-C版本

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


#18 楼

iOS 14.3和Xcode 12.3更新
在info.plist文件中将外观添加为浅色。
<key>Appearance</key>
<string>Light</string>


评论


在模拟器上不起作用

– Yodagama
20 Dec 23'2:02

@Yodagama,请检查您的模拟器的iOS版本和Xcode版本。它应该可以正常工作,我刚刚在模拟器中对其进行了测试。

–阿尔文
20 Dec 23 '13:06

我在xcode 12.1和ios 14.2上做过

– Yodagama
20 Dec 23 '13:08

它可以在Xcode 12.3和iOS 14.3中正常工作。对于您的版本,请尝试以下 UIUserInterfaceStyle Light

–阿尔文
20 Dec 23 '13:10



#19 楼

以下是一些提示和技巧,您可以在应用程序中使用它们来支持或绕过黑暗模式。

第一个提示:要覆盖ViewController样式

,您可以覆盖UIViewController的界面样式,由


1:overrideUserInterfaceStyle = .dark //对于暗模式

2:overrideUserInterfaceStyle = .light //对于亮模式


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


第二个技巧:在info.plist中添加密钥

只需添加一个新密钥


UIUserInterfaceStyle



在您的应用程序info.plist中,并将其值设置为Light或Dark。这会将应用默认样式替换为您提供的值。
您不必添加overrideUserInterfaceStyle = .light在每个viewController中添加此行,仅在info.plist中添加一行即可。

#20 楼

只需在您的info.plist文件中添加以下密钥:

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


#21 楼

只需将这些行添加到info.plist文件中:

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


这将强制应用仅在轻模式下运行。

评论


这已经被评论并回答了很多次。即使是公认的答案也暗示了这一点。因此,此注释不会添加任何新信息。

– Jeroen
19/12/18在12:22

#22 楼

在Xcode 12中,您可以将添加更改为“外观”。这将起作用!


#23 楼

将此添加到info.plist
<key>UIUserInterfaceStyle</key>
    <string>Light</string>


评论


已经在接受的答案中对此进行了解释。

–阿米特·乔希(Amit Joshi)
20/12/21在13:59

#24 楼

由于窗口属性可能会在应用程序生命周期中更改,因此我将使用此解决方案。因此,需要重复分配“ 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
    }
}


#25 楼

import UIKit

extension UIViewController {

    override open func awakeFromNib() {

        super.awakeFromNib()

        if #available(iOS 13.0, *) {

            overrideUserInterfaceStyle = .light

        }

    }
}


评论


请通过编辑为答案添加一些解释,以便其他人可以从中学习

– Nico Haase
20年4月7日在6:45

#26 楼

您可以执行以下操作:将此新键UIUserInterfaceStyle添加到Info.plist并将其值设置为Light。并检查警报控制器以浅色模式出现。您的Info.plist文件并将其值设置为亮或暗。

#27 楼

这个问题有很多答案,而不是在info.plist中使用它,您可以像这样在AppDelegate中设置它:

#28 楼

实际上,我只是编写了一些代码,使您可以在代码中全局选择退出暗模式,而不必在应用程序中使用每个viw控制器。通过管理课程列表,可以将其优化为逐班选择退出。对我而言,我想要的是让用户查看他们是否喜欢我的应用程序的暗模式界面,如果他们不喜欢它,则可以将其关闭。这将使他们能够在其余应用程序中继续使用暗模式。

用户选择是好的(哎呀,看着苹果,这是应该实现的方式)。 。加载时,它将替换本机的viewDidLoad方法,该方法将检查一个全局标志以查看是否对所有内容都禁用了暗模式。

因为在UIViewController加载时触发了它,它应该自动启动并默认情况下禁用黑暗模式。如果这不是您想要的,那么您需要及早到达某个地方并设置标志,否则只需设置默认标志即可。

我还没有写任何内容来回应用户打开或关闭标志。因此,这基本上是示例代码。如果我们希望用户与此交互,则所有视图控制器都需要重新加载。我不知道如何立即执行此操作,但是可能会发送一些通知来解决问题。因此,目前,暗模式下的全局打开/关闭仅在启动或重新启动应用程序时起作用。

现在,仅在大型应用程序中的每个MFING viewController中尝试关闭暗模式是不够的。如果您使用的是色彩资产,那么您将完全陷入困境。我们十多年以来一直将不可变对象理解为不可变的。从颜色资产目录中获得的颜色表示它们是UIColor,但是它们是动态(可变)颜色,并且随着系统从暗到亮模式的变化,它们在您的下方也会发生变化。那应该是一个功能。但是当然,没有主开关要求这些事情停止进行更改(据我所知,也许有人可以对此进行改进)。

所以解决方案分为两部分:


UIViewController上的公共类别提供了一些实用和便捷的方法...例如,我认为苹果公司没有想到我们中有些人将Web代码混入了我们的应用程序。因此,我们需要基于暗或亮模式切换样式表。因此,您要么需要构建某种动态样式表对象(这会很好),要么只是询问当前状态是什么(不好但很容易)。
加载时此类别将替换该类别的viewDidLoad方法UIViewController类并拦截调用。我不知道这是否违反了应用商店的规则。如果是这样,可能还有其他方法可以解决,但您可以认为这是概念证明。例如,您可以使所有主要视图控制器类型的一个子类,并使您自己的所有视图控制器都继承自这些视图控制器,然后可以使用DarkMode类别提示并对其进行调用以强制选择退出所有视图控制器。这很丑陋,但不会违反任何规则。我更喜欢使用运行时,因为这是运行时要做的。因此,在我的版本中,您只需添加类别,就可以在类别上设置一个全局变量,以决定是否要阻止暗模式,并且可以做到这一点。
如前所述,您还没有走出困境,另一个问题是UIColor基本上会做任何想要的事情。因此,即使您的视图控制器阻止了暗模式,UIColor也不知道您在哪里或如何使用它,因此无法适应。因此,您可以正确地获取它,但是将来它会在将来的某个时候恢复。也许很快,也许以后。因此,解决方法是使用CGColor对其分配两次,然后将其转换为静态颜色。这意味着,如果您的用户返回并在设置页面上重新启用了暗模式(此处的目的是使此项工作正常进行,以便用户可以在系统其余部分之上控制您的应用),所有这些静态颜色需要更换。到目前为止,这还有待其他人解决。最简单的方法是将您退出黑暗模式的默认值设为默认值,除以零会使应用程序崩溃,因为您无法退出该应用程序并告诉用户重新启动它。这可能也违反了应用商店的指导方针,但这是一个主意。

不需要公开UIColor类别,它只适用于调用colorNamed:...如果您不告诉DarkMode ViewController,类以阻止黑暗模式,它将按预期完美运行。尝试使某些东西变得优雅,而不是标准的苹果花粉代码,这意味着如果要以编程方式选择退出暗模式或切换它,则必须修改大部分应用程序。现在,我不知道是否有更好的方法以编程方式更改Info.plist以根据需要关闭暗模式。就我的理解而言,这是一个编译时功能,在此之后您便会陷入困境。

所以这是您需要的代码。应该加入,仅使用一种方法来设置UI样式或在代码中设置默认值。您可以出于任何目的随意使用,修改,执行任何您想做的事情,并且不提供任何担保,而且我不知道它是否会通过应用程序商店。改进非常欢迎。

合理的警告我不使用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起,我将其复制并粘贴到几个文件中。是我的可重用库,这只是其中的一部分。如果无法编译,请通知我。

评论


正如这个问题所讨论的,在控制UIColor行为方面,您并没有碰运气:stackoverflow.com/questions/56487679/…

–raven_raven
19年9月5日13:06