关于ios:Force view controller to reload to refresh UIAppearance changes

Force view controller to reload to refresh UIAppearance changes

我已经搜索了很长时间,但找不到答案。我正在开发一个 iOS 应用程序,并且有一个模式设置页面,该页面出现在点击一个按钮并返回一个 segue。
我想实现的选项之一是配色方案设置。我真的想避免手动更改页面上每个元素的颜色。

Apple 为这类事情提供了 UIAppearance 协议(因此我可以设置所有按钮的文本颜色等。
他们的文档说:

Note: iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.

我的问题是如何做到这一点。我试过调用 viewWillAppear 和 setNeedsDisplay 没有运气。


尝试使用这个片段:

1
2
3
4
5
6
7
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *window in windows) {
    for (UIView *view in window.subviews) {
        [view removeFromSuperview];
        [window addSubview:view];
    }
}

http://snipplr.com/view/75259/refresh-uiappearance-after-application-loaded/

使用 UIAppearance 更改应用程序主题后,它对我来说非常适合


请注意,上面的答案会对您的系统键盘行为产生不利影响。

事实证明,每当显示键盘时,iOS 都会在后台创建一个带有 UITextEffectsWindow 类的新系统窗口。如果您删除它,您的键盘行为可能会受到负面影响。例如,输入附件视图将与键盘分离并且不可见,除了导航控制器中的短暂闪烁。

您可以通过使用附加检查来解决此问题,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for window in UIApplication.shared.windows {
    // Whenever a system keyboard is shown, a special internal window is created in application
    // window list of type UITextEffectsWindow. This kind of window cannot be safely removed without
    // having an adverse effect on keyboard behavior. For example, an input accessory view is
    // disconnected from the keyboard. Therefore, a check for this class is needed. In case this class
    // that is indernal is removed from the iOS SDK in future, there is a"fallback" class check on
    // NSString class that always fails.
    if !window.isKind(of: NSClassFromString("UITextEffectsWindow") ?? NSString.classForCoder()) {
        window.subviews.forEach {
            $0.removeFromSuperview()
            window.addSubview($0)
        }
    }
}

请注意,UITextEffectsWindow 是内部的,将来可能会更改。这就是为什么我不使用 ! 解包变量,而是提供一个后备负 NSString 类(没有类型的窗口属于 NSString 类)。

注意:对于简单的应用程序,您可能可以使用 UIApplication.shared.keyWindow 作为解决方法。


具体来说,要获取当前视图及其父视图,请尝试:

1
2
3
4
UIView *currentview = self.window.rootViewController.view;
UIView *superview = currentview.superview;
[currentview removeFromSuperview];
[superview addSubview:currentview];

为我工作。


对于斯威夫特:

1
2
3
4
5
6
7
let windows = UIApplication.sharedApplication().windows
for window in windows {
    for view in window.subviews {
        view.removeFromSuperview()
        window.addSubview(view)
    }
}

对于 Swift 3.0.2:

1
2
3
4
5
6
7
8
for window in UIApplication.shared.windows {
    for view in window.subviews {
        view.removeFromSuperview()
        window.addSubview(view)
    }
    // update the status bar if you change the appearance of it.
    window.rootViewController?.setNeedsStatusBarAppearanceUpdate()
}

这是一个 Swift 5 单线:

1
UIApplication.shared.windows.forEach { $0.subviews.forEach { $0.removeFromSuperview(); self.window?.addSubview($0) }}


试试

1
2
[self.yourView removeFromSuperView];
[self addSubView:yourView];


对于 swift 4:

1
2
3
4
5
6
7
let windows = UIApplication.shared.windows
for window in windows {
    for view in window.subviews {
        view.removeFromSuperview()
        window.addSubview(view)
    }
}


大多数答案非常适合将语言从 LTR 更改为 RTL,但有时标签栏导航标题和导航栏标题不会被翻译。我用以下代码

解决了这个问题

1
2
3
4
5
6
if let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window {
    window.rootViewController = TabNavigationController()
    let tab = window.rootViewController as? UITabBarController
    tab?.selectedIndex = 3
    window.makeKeyAndVisible()
}

objective C

self.view.window.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;

如果我想在所有视图控制器中更改 overrideUserInterfaceStyle,我会使用此代码