【问题标题】:Force view controller to reload to refresh UIAppearance changes强制视图控制器重新加载以刷新 UIAppearance 更改
【发布时间】:2014-01-02 01:28:55
【问题描述】:

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

Apple 有一个UIAppearance 协议来处理这类事情(所以我可以设置所有按钮的文本颜色等。 他们的文件说:

注意:当视图进入窗口时,iOS 会应用外观更改,它不会更改已经在窗口中的视图的外观。要更改当前位于窗口中的视图的外观,请从视图层次结构中删除该视图,然后将其放回原处。

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

【问题讨论】:

  • 从视图层次结构中删除视图 - 唯一的方法是 [view removeFromSuperView] 或者如果视图被推送,那么您可以弹出视图并再次推送它而不使用动画

标签: ios objective-c interface-builder


【解决方案1】:

尝试使用这个sn-p:

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 更改应用主题后,它对我来说非常适合

【讨论】:

  • 这也适用于我,但看起来真的像一个“大锤破解坚果”的解决方案。我认为我最好避免 UIAppearance 如果这是它所需要的 - 我的使用会比偶尔的主题更改更频繁。
  • 是的,UIAppearance 如果没有更改/偶尔的 UI 更改。如果您需要频繁更新 UI,则需要编写自己的主题代码。
  • 成功了!内存效率高吗?
  • 这不是最好的,也不是最差的。但是我没有找到其他方法来实现它
【解决方案2】:

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

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

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

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 作为解决方法。

【讨论】:

  • 感谢您发布此信息。可能永远不会弄清楚为什么我的 UIToolbar 在主题更改后会从键盘上消失。
  • 我认为 UIApplication.shared.keyWindow 应该是主要答案,因为大多数应用程序应该只包含一个窗口。为什么有人会在 iOS 应用程序中创建额外的窗口?废话。
  • @m8labs 您无需创建额外的窗口 - 只要在 UI 中显示键盘,iOS 就会自动为您创建第二个窗口。这是您在操作 UI 层次结构时需要了解的 iOS 操作系统的实现技术。
【解决方案3】:

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

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

为我工作。

【讨论】:

    【解决方案4】:

    对于斯威夫特:

    let windows = UIApplication.sharedApplication().windows
    for window in windows {
        for view in window.subviews {
            view.removeFromSuperview()
            window.addSubview(view)
        }
    }
    

    【讨论】:

      【解决方案5】:

      对于 Swift 3.0.2:

      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()
      }
      

      【讨论】:

        【解决方案6】:

        这是一个 Swift 5 单线:

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

        【讨论】:

        • 它工作正常,但会影响键盘行为(打开键盘时崩溃),我建议看下面的答案,它适用于所有场景
        【解决方案7】:

        试试

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

        【讨论】:

        • 谢谢。如果我想从我的视图控制器类中执行此操作,我应该在什么上调用 addSubView? self 下没有该名称的方法,我无法将其添加回 self.view。
        【解决方案8】:

        对于 Swift 4:

        let windows = UIApplication.shared.windows
        for window in windows {
            for view in window.subviews {
                view.removeFromSuperview()
                window.addSubview(view)
            }
        }
        

        【讨论】:

        • 出于某种原因,这对我来说不起作用。我必须再次致电:UINavigationBar.appearance().titleTextAttributes = ...UIBarButtonItem.appearance().setTitleTextAttributes(..., for: .normal) - 令人惊讶的是,只有导航栏标题外观发生变化,而不是栏按钮项?!他们保持不变?这是在 iOS 11 上
        • @AhmedKhedr,是的,但我认为这不是你的问题。我认为问题在于您的外观配置。您可以显示您的代码,我会看一下,这是我的示例: UILabel.appearance(whenContainedInInstancesOf: [UITableViewCell.self]).textColor = .white let windows = UIApplication.shared.windows for window in windows { for在 window.subviews { view.removeFromSuperview() window.addSubview(view) } } 中查看
        • @AhmedKhedr 我的代码在其中的机器人中运行良好。尝试将 func appearances() 放在 func setupProxies() 中,看看它是否工作
        • UIBarButtonItems 包括后退按钮需要重置,否则它们只会在下一个绘图周期中重绘 - 这是在 iOS11 中
        【解决方案9】:

        大多数答案非常适合将语言从 LTR 更改为 RTL 但有时 标签栏导航标题导航栏标题 不会被翻译。我用下面的代码解决了这个问题

        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()
        }
        

        【讨论】:

          【解决方案10】:

          目标c

          self.view.window.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;

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

          【讨论】:

          • 嗨 :) ,您能否添加更多解释以支持您的回答。谢谢。
          • 嗨!当然。如果我想在所有视图控制器中更改 overrideUserInterfaceStyle,我会使用此代码。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-07
          • 2013-03-31
          • 1970-01-01
          相关资源
          最近更新 更多