强制视图提前加载子视图的简洁方法

Clean way to force view to load subviews early

提问人:Mark Amery 提问时间:6/24/2013 最后编辑:CommunityMark Amery 更新时间:2/25/2019 访问量:11685

问:

最近我写了一些代码,我试图在我刚刚实例化的 a 上引用一个 outlet,并在显示 .它不起作用,因为 的视图尚未加载其子视图,包括我的出口引用的子视图,因此该属性只是给了我一个空指针。UIViewController[storyboard instantiateViewControllerWithIdentifier]ViewControllerViewController

在(经过一些挣扎)在调试器中追踪我的问题原因之后,我用谷歌搜索了一下,并通过这样的答案了解到,我可以通过调用 getter 使视图加载其子视图而不显示。之后,我可以毫无问题地访问我的插座。myViewController.view

不过,这是一个明显的黑客攻击,Xcode - 非常正确地 - 不喜欢它,并愤怒地抗议这个警告:

未使用的属性访问结果 - 不应将 getter 用于副作用

有没有一种非黑客的替代方法可以做到这一点,而不涉及滥用 getter?或者,此方案是否有规范/惯用模式,涉及动态添加要在加载子视图后立即调用的处理程序?.view

还是标准解决方案只是替换为关闭 Xcode 的警告,然后忍受黑客攻击?myViewController.view[myViewController view]

iOS UIVieviceController

评论

0赞 Glenn Maynard 4/8/2015
您可以将任何 obj.property 更改为 [obj property] 以停止该警告。ObjC getter 和没有参数的选择器之间没有区别。

答:

-2赞 3 revsMark Amery #1

您可以调用以显式加载视图,而不是滥用 getter。如果有必要,getter 实际上会在调用时调用。[myViewController loadView].view.viewloadView

这仍然不是一个非常好的解决方案,因为 UIView 文档loadView

切勿直接调用此方法

评论

0赞 Mark Amery 6/25/2013
感谢 zbMax 的这个答案;他把它作为评论发布,但这是对这个问题的完整回答。
0赞 peterflynn 9/5/2018
正如现在的文档告诉你的那样,这样做的安全方法是调用——调用该 getter 将在幕后以正确的方式进行调用。[myViewController view]loadView
1赞 jbbenni 8/11/2013 #2

如果我理解正确的话,我认为还有另一个相当标准的解决方案:将出口修改/配置代码移动到(最近实例化的 VC)的方法中。viewDidLoad

这个问题也讨论了这个话题。

这需要一些重组,但如果你传入的 VC 处理自己的配置,它可能会在 MVC 方面给你一个“更干净”的设计,并且它会避免 “你永远不应该直接调用此方法” 的限制。loadView

评论

0赞 Mark Amery 8/31/2013
哎呀。是的,这就是我一直以来应该这样做的方式。
0赞 Mark Amery 8/31/2013
尽管我读过关于是否可以多次调用的相互矛盾的说法 - 因此在 ivar 中使用它来确保初始化代码只执行一次(如有必要)可能是值得的。viewDidLoadBOOL isInitializedviewDidLoad
6赞 Swany 10/14/2015 #3

我同意应该避免强制加载视图,但我遇到了一种情况,它似乎是解决问题的唯一合理解决方案(弹出包含尚未调用的 UISearchController 的 UINavigationController 会导致讨厌的控制台说警告)。

我所做的是使用新的 iOS9 API loadViewIfNeeded,并且在 iOS9 之前使用 viewController.view.alpha = 1.0。当然,在此代码上方的良好注释将防止您(或其他人)以后认为不需要此代码。

Apple 现在提供此 API 这一事实表明,有时可能需要它。

43赞 Rudolf Adamkovič 7/21/2016 #4

在 iOS 9 或更高版本上,可以使用:

viewController.loadViewIfNeeded()

文档:https://developer.apple.com/reference/uikit/uiviewcontroller/1621446-loadviewifneeded

评论

0赞 Mark Amery 7/21/2016
这是最直接回答所问问题并获得复选标记的答案,但请注意,在许多情况下,它可能被误导了 - 考虑将您需要加载视图的任何逻辑移动到 viewDidLoad
0赞 Rudolf Adamkovič 7/21/2016
@MarkAmery 它可能也可能不。 :)
2赞 Anton Tropashko 10/4/2016 #5

合并了 ios9 之前部署目标的 Rudolph/Swany 答案

    if #available(iOS 9.0, *) {
        loadViewIfNeeded()
    }
    else {
        // _ = self.view works but some Swift compiler genius could optimize what seems like a noop out
        // hence this perversion from this recipe http://stackoverflow.com/questions/17279604/clean-way-to-force-view-to-load-subviews-early
        view.alpha = 1
    }

评论

0赞 Mark A. Donohoe 4/23/2019
我不认为这会被优化出来,因为虽然结果是未使用的,但不能假设它是非操作的,因为这不会给你任何关于吸食者内部做什么的信息,所以我认为这是一个安全的假设,它永远不会通过优化被删除。
0赞 Anton Tropashko 4/26/2019
也许你是对的,但我已经开始期待 Swift 编译器贡献者最坏的情况
3赞 Stanislau Baranouski 9/18/2018 #6

不确定这种方式有多干净,但它仍然工作正常:

_ = vc.view

UPD:为了方便起见,您可以像下面这样声明扩展:

extension UIViewController {
    func preloadView() {
        let _ = view
    }
}

您可以通过以下 URL 阅读说明:https://www.natashatherobot.com/ios-testing-view-controllers-swift/