关于Objective C:是否可以在实例化对象之前检查情节提要中是否存在标识符?

Is it possible to check whether an identifier exists in a storyboard before instantiating the object?

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

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

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


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

1
2
3
4
5
6
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:查询可可类的基础属性的应用程序。这些基础属性可能会在将来的任何时间更改,从而在没有警告的情况下中断应用程序功能。这些东西没有淘汰的过程。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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];
    }

希望对您有所帮助!即使答案确实很晚。


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

1
2
3
4
5
6
7
8
9
10
11
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
}

按如下所示使用此功能:

1
2
3
4
5
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")
}

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

EDIT其实这是错误的!太奇怪了...文档的返回值部分与另一部分矛盾...但是答案仍然是最终的(没有方法来检查标识符是否存在)


迅速4.2。

在下面声明一个扩展名。

1
2
3
4
5
6
7
8
9
10
11
extension UIStoryboard {
    func instantiateVC(withIdentifier identifier: String) -> UIViewController? {
        //"identifierToNibNameMap" a€" 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
    }
}

在任何地方都可以使用这种方法:

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

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

将" yourControllerID"替换为控制器的ID。


您可以使用try-catch异常处理package代码,并确定在发生此类异常时如何做出反应。
我使用此方法动态实例化视图控制器,而不必知道它们是否在情节提要或nib文件中表示。