【问题标题】:View Controller not loading via instantiateViewController function even with correct identifier即使使用正确的标识符,视图控制器也不会通过 instanceViewController 函数加载
【发布时间】:2021-04-23 00:35:54
【问题描述】:

目标:在通过 main.storyboard 中的故事板引用加载的单独故事板中,在充当初始视图控制器的 pageViewController 中,我想通过函数 .instantiateViewController(标识符:)。

问题:我试图实例化为常量的最后一个 viewController 没有加载。错误 - *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load the scene view controller for identifier 'FinalVC''"

此故事板中的所有其他 viewController 都可以正常加载。最后一个视图控制器有一个正确的自定义类链接和一个唯一的故事板标识符。

调试:我创建了一个断点,在此视图控制器被实例化并在调试控制台中注意到所有其他视图控制器对象加载为“BillyCues.repeatViewController + 唯一标识号”,而最后一个 vc加载为“UIViewController + 0x000000000000000”。就好像这个 vc 不是 app bundle 的一部分或没有被正确引用,但是当我在目录中搜索时它就在那里。

Debugging console screen

我尝试过的方法不起作用

  • 检查另一个 vc 是否具有相同的标识符
  • 清理构建文件夹
  • 在身份检查器中选中“使用 Storyboard ID”
  • let finalVC = storyBoard.instantiateViewController(identifier: "FinalVC") as! FinalViewController
  • 重启 Xcode
  • 使用相同的自定义类创建具有不同故事板标识符的全新视图控制器
  • 从最后一个 vc 中的按钮和标签中删除了所有连接
  • 确保 main.storyboard 中的所有故事板引用都链接了正确的故事板

结论:我所有的谷歌搜索都导致其他开发人员遇到关于 NIB 或 tableviews 不一定是视图控制器的错误。如果我的 vc 具有正确的自定义类和唯一标识符,则不应发生错误。如果有人可以提供指导,我将不胜感激;我傻眼了。

我希望我在适当的结构中寻求帮助,但如果需要更多代码或屏幕截图,请告诉我。

PageViewController 代码

    import UIKit
    
    class LauncherViewController: UIPageViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.setViewControllers([viewControllerList[0]], direction: .forward, animated: false, completion: nil)
    
            // Do any additional setup after loading the view.
        }
        
        private var viewControllerList: [UIViewController] = {
            let storyBoard = UIStoryboard.cueCreation
            let firstVC = storyBoard.instantiateViewController(identifier: "CueNameVC")
            let secondVC = storyBoard.instantiateViewController(identifier: "DueDateVC")
            let thirdVC = storyBoard.instantiateViewController(identifier: "IconVC")
            let fourthVC = storyBoard.instantiateViewController(identifier: "IconColorVC")
            let fifthVC = storyBoard.instantiateViewController(identifier: "RepeatVC")
            let finalVC = storyBoard.instantiateViewController(identifier: "FinalVC") as! FinalViewController
            
            
            return [firstVC, secondVC, thirdVC, fourthVC, fifthVC, finalVC]
        }()
        
        var selectedReminderBill: CueObject?
        
        public var currentIndex = 0
        static var cueName: String = ""
        static var cueDate: Date = Date()
        static var cueIcon: Data = Data()
        static var iconColor:String = "14CC7F"
        static var repeatMonthly: Bool = false
       
        // Navigation button functions below to move to the next or previous page
        func pushNext() {
            if currentIndex + 1 < viewControllerList.count {
                self.setViewControllers([self.viewControllerList[self.currentIndex + 1]], direction: .forward, animated: true, completion: nil)
                currentIndex += 1
            }
        }
        
        func pullBack() {
            print(currentIndex)
            if currentIndex - 1 < viewControllerList.count {
                self.setViewControllers([self.viewControllerList[self.currentIndex-1]], direction: .reverse, animated: true, completion: nil)
                currentIndex -= 1
            }   
        }
    }

FinalViewController 代码

    import UIKit
    import UserNotifications
    import RealmSwift
    
    class FinalViewController: UIViewController {
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(true)
            cueName.text = LauncherViewController.cueName
            dueDate.text = CueLogic.convertPaymentDateToString(for: LauncherViewController.cueDate)
            iconBackgroundView.backgroundColor = colorLogic.colorWithHexString(hexString: LauncherViewController.iconColor)
            cueIcon.image = UIImage(data: LauncherViewController.cueIcon)
            repeatsMonthly.text = repeatMonthlyToString
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            cueName.layer.cornerRadius = 15
            cueName.clipsToBounds = true
            
            iconBackgroundView.layer.cornerRadius = 20
            iconBackgroundView.clipsToBounds = true
            
            dueDate.layer.cornerRadius = 15
            dueDate.clipsToBounds = true
            
            repeatsMonthly.layer.cornerRadius = 15
            repeatsMonthly.clipsToBounds = true
            
            backButton.layer.cornerRadius = 15
            backButton.clipsToBounds = true
            
            saveButton.layer.cornerRadius = 15
            saveButton.clipsToBounds = true
            
            // Do any additional setup after loading the view.
        }
    
        let colorLogic = ColorLogic()
        let realm = try! Realm()
        weak var delegate: HomeScreenDelegate?
        var launcher = LauncherViewController()
    
        
        var repeatMonthlyToString: String  {
            get {
                if LauncherViewController.repeatMonthly == true {
                    return "Repeats Monthly: Yes"
                } else {
                    return "Repeats Monthly: No"
                }
            }
        }
        
        @IBOutlet var cueName: UILabel!
        
        @IBOutlet var dueDate: UILabel!
        @IBOutlet var saveButton: UIButton!
        
        @IBOutlet var backButton: UIButton!
        
        @IBOutlet var iconBackgroundView: UIView!
        @IBOutlet var cueIcon: UIImageView!
        
        @IBOutlet var repeatsMonthly: UILabel!
        
        @IBAction func dismissButtonTapped(_ sender: Any) {
            self.dismiss(animated: true, completion: nil)
        }
        
        @IBAction func backButtonTapped(_ sender: Any) {
            if let pageController = parent as? LauncherViewController {
                pageController.pullBack()
            }
        }
        
        @IBAction func saveButtonTapped(_ sender: Any) {
            // Request authorization from the user to allow notifications
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: {success, error in
                if success {
                    // schedule test
                    
                } else if let error = error {
                    print("error occured \(error)")
                    
                }
            })
            
            let newCue = CueObject()
            let launcherVC = LauncherViewController.self
            
            newCue.name = launcherVC.cueName
            newCue.paymentDate = launcherVC.cueDate
            newCue.icon = launcherVC.cueIcon
            newCue.iconColor = launcherVC.iconColor
            newCue.repeatsMonthly = launcherVC.repeatMonthly
            
            NotificationLogic.scheduleLocalAlertForBill(named: newCue.name, due: newCue.paymentDate, repeatsMonthly: newCue.repeatsMonthly)
            
            saveToDB(for: newCue)
    
            delegate?.loadCuesFromRealm()
            self.dismiss(animated: true, completion: nil)
        }
        
        func saveToDB(for cue: CueObject) {
            do {
                try realm.write({
                    realm.add(cue)
                })
            } catch {
                print("Error - \(error)")
            }
        }
    }
    
    protocol HomeScreenDelegate: AnyObject {
        func loadCuesFromRealm()
    }

我在另一个 viewController 中写的扩展

extension UIStoryboard {
    static let onboarding = UIStoryboard(name: "Onboarding", bundle: nil)
    static let main = UIStoryboard(name: "Main", bundle: nil)
    static let cueCreation = UIStoryboard(name:"CueCreation", bundle: nil)
    
}

Identity Inspector

Main Storyboard References

【问题讨论】:

  • 您是否尝试复制/粘贴 VC 标识符?会不会有隐形角色?也许取消选中/选中“从目标继承模块”。
  • 我只是手动写入并取消选中“从目标继承模块” - 仍然收到相同的错误。

标签: ios swift


【解决方案1】:

作为清理的一部分,我会做一些事情来开始调试实际问题。在故事板扩展中,我宁愿使用静态函数来引用视图控制器。

extension UIStoryboard { 
    class func createFinalVC() -> FinalViewController? { 
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FinalVC") as? FinalViewController)
    }
}

为了实现它,我会在视图控制器中使用 FinalViewController:

private func createCreateFinalVC() -> FinalViewController? { 
    return UIStoryboard.createFinalVC()
}

最后将其推入视图中,

if let finalVC = createCreateFinalVC() {
    yourNavController?.pushViewController(finalVC, animated: true)
}

【讨论】:

  • 我在你的帖子中做了所有的事情,我能够让 viewController 出现。我没有带有这些 viewControllers 的导航控制器,所以我不得不使用 self.present(finalVC, animated: true, completion: nil) 而不是 self.present(finalVC, animated: true, completion: nil) 根据您的建议,有两件事是真的 1:没有返回 Nil,因此 VC 实际上在那里,2:标识符 I正在使用。为了调试,你的想法是什么?如果我知道 VC 在那里并且不是零,我的下一步应该是什么。这是否意味着我错误地定位了某些东西
  • 嘿,展示 VC 是您的目标,对吧?你没有做到吗?
  • 是的。我想到了。导致崩溃的代码行是 FinalViewController 中的 var launcher = LauncherViewController()。不知道为什么.. 感谢您的回复;因为它,我学会了如何使用可选项进行调试。
【解决方案2】:

解决方案

我开始了一个排除过程,并开始注释掉我的 FinalVC 类中的所有代码。我了解到这行代码var launcher = LauncherViewController() 正在触发崩溃。

鉴于我有限的初学者知识,我不知道为什么这会导致崩溃;我只能假设 Xcode 试图用相同的标识符号或类似的东西初始化两个 LauncherViewControllers。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-05
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-13
    相关资源
    最近更新 更多