SwiftUI:如何让用户使用“亮”、“暗”和“系统”选项实时设置应用程序外观?

如何解决SwiftUI:如何让用户使用“亮”、“暗”和“系统”选项实时设置应用程序外观?

我目前正在尝试在应用中实施一个解决方案,让用户能够使用以下选项实时切换应用的外观:

  • 系统(应用在设备的 iOS 设置中设置的任何外观)
  • 浅色(应用 .light 配色方案)
  • 深色(应用 . 深色配色方案)

事实证明,使用 .preferredColorScheme() 设置浅色和深色配色方案非常容易且响应迅速;但是,对于“系统”选项,我还没有找到任何令人满意的解决方案。

我目前的方法如下:

  1. 在 ContentView 中使用 @Environment(.colorScheme) 获取设备配色方案
  2. 创建自定义视图修饰符以在任何视图上应用相应的配色方案
  3. 使用“MainView”上的修饰符(即应用的真实内容应该存在的地方)在配色方案之间切换

我的想法是在 ContentView 中嵌入 MainView,这样@Environment(.colorScheme) 就不会被任何应用于 MainView 的 colorScheme 所干扰。

但是,它仍然没有按预期工作:设置明暗外观时,一切都按预期工作。但是,当从亮/暗切换到“系统”时,外观的变化只有在重新启动应用程序后才能看到。然而,预期的行为是外观立即改变。

对此有什么想法吗?

以下是相关代码片段:

主视图

import SwiftUI

struct MainView: View {

    @AppStorage("selectedAppearance") var selectedAppearance = 0

    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                selectedAppearance = 1
            }) {
                Text("Light")
            }
            Spacer()
            Button(action: {
                selectedAppearance = 2
            }) {
                Text("Dark")
            }
            Spacer()
            Button(action: {
                selectedAppearance = 0
            }) {
                Text("System")
            }
            Spacer()
        }
    }
}

内容视图

import SwiftUI

struct ContentView: View {

    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        MainView()
            .modifier(ColorSchemeModifier(colorScheme: colorScheme))
    }
}

“实用程序”

import Foundation
import SwiftUI

struct ColorSchemeModifier: ViewModifier {

    @AppStorage("selectedAppearance") var selectedAppearance: Int = 0
    var colorScheme: ColorScheme

    func body(content: Content) -> some View {
        if selectedAppearance == 2 {
            return content.preferredColorScheme(.dark)
        } else if selectedAppearance == 1 {
            return content.preferredColorScheme(.light)
        } else {
            return content.preferredColorScheme(colorScheme)
        }
    }
}

解决方法

我最终使用了以下解决方案,这是对@pgb 给出的答案的轻微改编:

内容视图:

#include <Windows.h>
#include <iostream>

HHOOK hMouseHook;

LRESULT CALLBACK mouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{   
    MOUSEHOOKSTRUCT* pMouseStruct = (MOUSEHOOKSTRUCT*)lParam;
    if (pMouseStruct != NULL)
        printf("X: %d  Y: %d\n",pMouseStruct->pt.x,pMouseStruct->pt.y);
    return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}

int main() {
    hMouseHook = SetWindowsHookEx(WH_MOUSE_LL,mouseProc,0);
    MSG message;
    while (GetMessage(&message,NULL,0)) {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    UnhookWindowsHookEx(hMouseHook);
    return 0;
}

助手类

struct ContentView: View {

    @AppStorage("selectedAppearance") var selectedAppearance = 0
    var utilities = Utilities()

    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                selectedAppearance = 1
            }) {
                Text("Light")
            }
            Spacer()
            Button(action: {
                selectedAppearance = 2
            }) {
                Text("Dark")
            }
            Spacer()
            Button(action: {
                selectedAppearance = 0
            }) {
                Text("System")
            }
            Spacer()
        }
        .onChange(of: selectedAppearance,perform: { value in
            utilities.overrideDisplayMode()
        })
    }
}
,

我认为这在 SwiftUI 中是不可能的。

从文档来看,使用 preferredColorScheme 将影响整个层次结构,从封闭的演示文稿开始:

配色方案适用于最近的封闭演示文稿,例如弹出窗口或窗口。视图可以使用 colorScheme 环境值读取配色方案。

因此,这会影响您的整个视图层次结构,这意味着您不能仅针对某些视图覆盖它。相反,它会“冒泡”并更改整个窗口(或演示文稿上下文)的配色方案。

然而,您可以从 UIKit 更改它,但使用 overrideUserInterfaceStyle,它也是一个支持 .dark.light 和 {{1} }.未指定为系统设置。

这对您的应用有何影响?好吧,我认为您根本不需要视图修饰符。相反,您需要监视 .unspecified 以了解 UserDefaults 键的更改,并通过将 selectedAppearance 更改为适当的值来对其做出反应。

因此,取决于您的应用程序其余部分的结构,在您的 overrideUserInterfaceStyleAppDelegate 中(您也可以使用任何其他对象,但它需要访问呈现的 {{ 1}},因此在重构时请记住这一点),您可以将侦听器连接到 SceneDelegate 以侦听更改。像这样:

UIWindow

然后覆盖 UserDefaults 以监控对默认键的更改:

UserDefaults.standard.addObserver(self,forKeyPath: "selectedAppearance",options: [.new],context: nil)

还有一些需要改进的地方(这个问题没有上下文,但我认为值得一提):

  • 对于外观的有效值,我会使用 observeValue 而不是 override func observeValue(forKeyPath keyPath: String?,of object: Any?,change: [NSKeyValueChangeKey : Any]?,context: UnsafeMutableRawPointer?) { guard let userDefaults = object as? UserDefaults,keyPath == "selectedAppearance" else { return } switch userDefaults.integer(forKey: "selectedAppearance") { case 2: window?.overrideUserInterfaceStyle = .dark case 1: window?.overrideUserInterfaceStyle = .light default: window?.overrideUserInterfaceStyle = .unspecified } }
  • 假设 enum 提供了一个只包含您想要的 3 个值的枚举,我会重用它,因此您不需要 integer 调用中的 UIKit
,

适用于

.preferredColorScheme(selectedAppearance == 1 ? .light : selectedAppearance == 2 ? .dark : nil)

iOS 14.5+: 这就对了。他们让“nil”工作来重置您的首选颜色方案。

iOS14:

唯一的问题是您必须重新加载应用程序才能让系统正常工作。我可以想到一个解决方法 - 当用户选择“系统”时,您首先确定当前的 colorScheme 是什么,将其更改为它,然后才将 selectedAppearance 更改为 0。

  • 用户会立即看到结果,下次启动时它将成为系统主题。

编辑:

这是工作的想法:

struct MainView: View {
    @Binding var colorScheme: ColorScheme
    @AppStorage("selectedAppearance") var selectedAppearance = 0

    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                selectedAppearance = 1
            }) {
                Text("Light")
            }
            Spacer()
            Button(action: {
                selectedAppearance = 2
            }) {
                Text("Dark")
            }
            Spacer()
            Button(action: {
                if colorScheme == .light {
                    selectedAppearance = 1
                }
                else {
                    selectedAppearance = 2
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
                    selectedAppearance = 0
                }
            }) {
                Text("System")
            }
            Spacer()
        }
    }
}

struct ContentView: View {
    @AppStorage("selectedAppearance") var selectedAppearance = 0
    @Environment(\.colorScheme) var colorScheme
    @State var onAppearColorScheme: ColorScheme = .light //only for iOS<14.5

    var body: some View {
        MainView(colorScheme: $onAppearColorScheme)
            .onAppear { onAppearColorScheme = colorScheme } //only for iOS<14.5
            .preferredColorScheme(selectedAppearance == 1 ? .light : selectedAppearance == 2 ? .dark : nil) }
}

有一个小问题 -> 它仅适用于 onAppear 而不是 onChange(不知道为什么)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res