【问题标题】:When code is in loadView: Could not execute support code to read Objective-C class data in the process当代码在 loadView 中时:无法执行支持代码来读取进程中的 Objective-C 类数据
【发布时间】:2020-11-06 11:15:37
【问题描述】:

假设我以编程方式在导航控制器中创建了一个 tableView。删除故事板文件及其参考后。我将所有 UI 初始化和约束代码放入loadView(),如下代码。

用真机运行,但是table view没有出现,很快就弹出这个警告。

如果我将这些代码放在viewDidLoad 中,一切正常。那么,我该如何追踪这个问题呢?我搜索了一些类似的线程,但没有解决我的问题。

警告:无法执行支持代码来读取 Objective-C 类数据 进行中。这可能会降低类型信息的质量 可用。

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    let table = UITableView()
    var pictures = [Picture]()
    let defaults = UserDefaults.standard

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // if put code in loadView() into here, everything works fine.
        loadData()
    }
    
    override func loadView() {
        title = "My Pictures"
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))
        
        view.addSubview(table)
        table.delegate = self
        table.dataSource = self
        table.register(PictureCell.self, forCellReuseIdentifier: "picture")
        table.translatesAutoresizingMaskIntoConstraints = false
        table.rowHeight = 120
        
        NSLayoutConstraint.activate([
            table.topAnchor.constraint(equalTo: view.topAnchor),
            table.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            table.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            table.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
    }
    
    func loadData() {
        DispatchQueue.global().async { [weak self] in
            if let savedPcitures = self?.defaults.object(forKey: "pictures") as? Data {
                let jsonDecoder = JSONDecoder()
                do {
                    self?.pictures = try jsonDecoder.decode([Picture].self, from: savedPcitures)
                } catch {
                    print("Failed to load pictures.")
                }
            }
            DispatchQueue.main.async {
                self?.table.reloadData()
            }
        }
    }
    
    @objc func addPicture() {
        let picker = UIImagePickerController()
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            picker.sourceType = .camera
        } else {
            fatalError("Camera is not available, please use real device")
        }
        picker.allowsEditing = true
        picker.delegate = self
        present(picker, animated: true)
    }

...
    

【问题讨论】:

  • 那你为什么不放viewDidLoad呢?您想知道为什么会发生这种情况或解决您的问题吗?

标签: ios swift iphone


【解决方案1】:

如果您覆盖 loadView(),则由您来创建和设置控制器的“根”视图。

如果您将其添加到您的代码中:

override func loadView() {
    title = "My Pictures"
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))

    // add these three lines
    let newView = UIView()
    newView.backgroundColor = .white
    view = newView
    
    view.addSubview(table)
    // ... rest of your original code

您应该能够导航到您的控制器。

也就是说 --- 除非您有特定原因来覆盖 loadView(),否则您可能不应该这样做。您示例中的所有代码通常都放在viewDidLoad() 中。


编辑 - 澄清一下...

我认为 Apple 的文档有点模棱两可...

据我了解,任何仅通过代码创建的 UIViewController 后代的视图控制器(UIViewControllerUITableViewControllerUICollectionViewController 等)都会生成自己的“根”视图 除非我们覆盖loadView(),在这种情况下,由我们来实例化并提供根视图。

那么,我们为什么要提供这种视图呢?我们可能希望提供 custom UIView 子类。例如,如果我有一个“渐变”视图,它将其基层类设置为CAGradientLayer - 所以我不必添加和管理子层。我可以覆盖 loadView() 并将控制器的根视图设置为渐变视图。

我在 GitHub 上提供了一个完整的示例。 only 项目中的 Storyboard 是必需的 LaunchScreen --- 其他一切仅通过代码完成。

从导航控制器中的 3 个按钮开始:

  • 第一个按钮推送到带有标签的简单UIViewController
  • 第二个按钮推送到UITableViewController,其中选择一行推送到“详细信息”UIViewController
  • 第三个按钮推送到带有标签和渐变背景的简单UIViewController

第三个示例是唯一一个我覆盖loadView() 以使用我的自定义渐变视图的示例。所有其他人处理viewDidLoad()中的所有设置

这是项目的链接:https://github.com/DonMag/CodeOnly

【讨论】:

  • 谢谢,我需要给view一个值,因为它默认为nil。当我手动创建视图(没有 IB)时,我应该在这里使用loadView()。一个问题,我可以设置view = table 将tableView 设置为根视图,这是一种正确的方法吗?
  • @ChuckZHB - 请参阅对我的答案的编辑以进行澄清以及指向示例项目的链接。
  • 很好的例子,我已经完成了。显然,我们可以使用viewDidLoadloadView 这两种方式来设置视图。而使用loadView 的最佳实践是当我们需要使用一些自定义UIView 作为根视图时。与此同时,我找到了一篇关于这个主题的好文章。 roopc.net/posts/2015/loadview-vs-viewdidload
  • @ChuckZHB - 再想一想... "如果使用 IB ...你不能覆盖 loadView()" -- 所以,如果我开始使用 loadView()对于 VC 初始化,然后决定在 Storyboard / IB 环境中使用这些类——或者如果其他人想使用我的代码——将需要进行更改。由于根视图无论如何都会自动创建,我个人认为使用自定义视图类除非使用viewDidLoad() 更安全。我不是 Apple 工程师,所以我的理解可能是错误的,但我还没有看到任何其他文档或讨论来说服我。
  • 是的,没有对错。据我了解,我们应该使用它们flexibel。使用 IB 或纯编码方式创建 UI,或者根据您的条件将它们混合起来。通过以编程方式创建 UI/视图让我感到自信,并且我可以了解一些潜在的东西。因此,我更喜欢使用编码方式。
【解决方案2】:

正如上面DonMag所说,我需要在这里给view一个值,因为它默认为nil(不使用界面生成器)。

我发现这里有一个参考可以解释何时使用viewDidLoadloadView,在我的例子中,我使用第二种方式以编程方式创建所有视图。

参考:https://stackoverflow.com/a/3423960/10158398

当您从 NIB 加载视图并想要进一步执行时 启动后自定义,使用 viewDidLoad。

如果您想以编程方式创建视图(不使用接口 Builder),使用 loadView。

更新:

根据 DonMag 的示例。很明显,我们可以使用这两种方式来设置视图,在viewDidLoadloadView 中。而使用loadView 的最佳实践是当我们需要使用一些自定义UIView 作为根视图时。与此同时,我找到了一篇关于这个主题的好文章。这是一个旧的,虽然我认为概念不会改变。

http://roopc.net/posts/2015/loadview-vs-viewdidload/

总而言之,我们可以在 loadView 或 viewDidLoad,只要我们知道什么时候调用了哪个方法。 要记住的最重要的事情是,如果您覆盖 loadView, 您应该设置视图属性,而如果您覆盖 viewDidLoad, 您应该只阅读应该已经存在的视图属性 设置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2016-05-31
    相关资源
    最近更新 更多