【问题标题】:Is it possible to check whether an identifier exists in a storyboard before instantiating the object?是否可以在实例化对象之前检查故事板中是否存在标识符?
【发布时间】:2012-11-08 01:42:58
【问题描述】:

在我的代码中,我有这一行,但我想知道是否有办法在将 @"SomeController" 与“instantiateViewControllerWithIdentifier”一起使用之前检查它是否存在方法。如果标识符不存在,则应用程序崩溃。

如果没有好的方法,这不是一个大问题,我可以更小心一点,不要让标识符名称变胖,但我希望我能更优雅地处理它。

UIViewController *newTopViewController = [self.storyboard    instantiateViewControllerWithIdentifier:@"SomeController"];

【问题讨论】:

  • 这个想法是故事板是您项目的一部分,以及您的代码。如果标识符不正确,那是代码中的逻辑错误,而不是由于运行时某些不可预见的情况而导致的可恢复故障。

标签: objective-c ios cocoa-touch uistoryboard


【解决方案1】:

正如 Tom 所说,这个问题的最佳解决方案是 try-catch 块:

@try {
        UIViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"identifier"];

    }
    @catch (NSException *exception) {
        UIAlertView *catchView;

        catchView = [[UIAlertView alloc]
                     initWithTitle: NSLocalizedString(@"Error", @"Error")
                     message: NSLocalizedString(@"Identifier not found on SB".", @"Error")
                     delegate: self
                     cancelButtonTitle: NSLocalizedString(@"OK", @"Error") otherButtonTitles: nil];

        [catchView show];
    }

希望对你有帮助!即使答案真的很晚。

【讨论】:

  • 正是这样的事情让我想知道 Apple 是如何变得如此庞大的......我期望一个框架提供一个健全的方法来检查这一点是不是很奇怪?
  • 我同意@Kevin,所以我猜你没疯!或者我们俩都是 XD
  • 我发现了一个 hack,检查我的答案 ;)
【解决方案2】:

您可以在UIStoryboards 上使用valueForKey:UIStoryboards 有一个名为“identifierToNibNameMap”的键,它的值是一个NSDictionary,在该故事板中带有UIViewControllers。这个内部NSDictionary 使用视图控制器的名称作为键,因此您可以使用以下代码实际检查视图控制器是否存在于情节提要中:

if ([[storyboard valueForKey:@"identifierToNibNameMap"] objectForKey:myViewControllerName]) {
    // the view controller exists, instantiate it here
    UIViewController* myViewController = [storyboard instantiateViewControllerWithIdentifier:myViewControllerName];
} else {
    //the view controller doesn't exist, do fallback here
}

注意:众所周知,Apple 会拒绝使用 valueForKey: 查询可可类底层属性的应用程序。这些基础属性可能会在未来的任何时候发生变化,从而在没有警告的情况下破坏应用程序的功能。这些东西没有弃用过程。

【讨论】:

    【解决方案3】:

    @Kevin 的解决方案有效。这是我在代码中使用的 Swift 3 的一段与函数完全相同的代码:

    func instantiateViewController(fromStoryboardName storyboardName: String, withIdentifier identifier: String) -> UIViewController? {
        let mainStoryboard = UIStoryboard(name: storyboardName, bundle: nil)
        if let availableIdentifiers = mainStoryboard.value(forKey: "identifierToNibNameMap") as? [String: Any] {
            if availableIdentifiers[identifier] != nil {
                if let poiInformationViewController = mainStoryboard.instantiateViewController(withIdentifier: identifier) as? UIViewController {
                    return viewController
                }
            }
        }
        return nil
    }
    

    按如下方式使用该函数:

    if let viewController = self.instantiateViewController(fromStoryboardName: "YourStoryboardName", withIdentifier: "YourViewControllerStoryboardID") {
        // Here you are sure your viewController is available in the Storyboard
    } else {
        print("Error: The Storyboard with the name YourStoryboardName or the Storyboard identifier YourViewControllerStoryboardID is not available")
    }
    

    【讨论】:

      【解决方案4】:

      不,没有检查。但是,您不需要这样做。如果标识符不存在,此方法将返回 nil,因此只需使用 NSAssert 进行检查。

      编辑其实这是错的!!这很奇怪......文档的返回值部分与另一部分相矛盾......但答案最终是否定的(没有方法来检查标识符是否存在)

      【讨论】:

        【解决方案5】:

        您可以使用 try-catch 异常处理来包装代码,并决定在发生此类异常时如何做出反应。 我使用这种方法来动态实例化视图控制器,而不必知道它们是在 Storyboard 中还是在 nib 文件中表示的。

        【讨论】:

        • 太糟糕了,这将触发您可能拥有的任何异常断点。
        • 并导致内存泄漏,直到您使用 fobjc-arc-exceptions 或类似的东西。
        【解决方案6】:

        Swift 4.2.

        在下面声明一个扩展。

        extension UIStoryboard {
            func instantiateVC(withIdentifier identifier: String) -> UIViewController? {
                // "identifierToNibNameMap" – dont change it. It is a key for searching IDs 
                if let identifiersList = self.value(forKey: "identifierToNibNameMap") as? [String: Any] {
                    if identifiersList[identifier] != nil {
                        return self.instantiateViewController(withIdentifier: identifier)
                    }
                }
                return nil
            }
        }
        

        在任何地方使用这种方法:

        if let viewController = self.storyboard?.instantiateVC(withIdentifier: "yourControllerID") {
                    // Use viewController here
                    viewController.view.tag = 0; // for example
                }
        

            if let viewController = UIStoryboard(name: "yourStoryboardID", bundle: nil).instantiateVC(withIdentifier: "yourControllerID") {
                // Use viewController here
                viewController.view.tag = 0; // for example
            }
        

        将“yourControllerID”替换为您的控制器 ID。

        【讨论】:

        • Apple 接受这个吗?注意:众所周知,Apple 会拒绝使用 valueForKey: 查询可可类底层属性的应用程序。这些基础属性可能会在未来的任何时候发生变化,从而在没有警告的情况下破坏应用程序的功能。这些东西没有弃用过程。
        猜你喜欢
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-10
        • 1970-01-01
        • 2019-07-23
        • 1970-01-01
        • 2021-02-01
        相关资源
        最近更新 更多