在通用应用程序中实例化设备特定视图控制器的正确模式是啥?

Posted

技术标签:

【中文标题】在通用应用程序中实例化设备特定视图控制器的正确模式是啥?【英文标题】:What is the right pattern for instantiating a device specific view controller in a Universal App?在通用应用程序中实例化设备特定视图控制器的正确模式是什么? 【发布时间】:2011-04-12 01:30:52 【问题描述】:

我是 Objective-C 的新手,所以请耐心等待。我从 Xcode4 中的 Universal App 模板开始构建我的应用程序。有一个约定,模板让您开始使用我试图坚持的约定。对于每个 View Controller,每个设备类型都有一个主文件和一个子类。例如:

Project/
    ExampleViewController.(h|m)
    - iPhone/
      - ExampleViewController_iPhone.(h|m|xib)
    - iPad/
      - ExampleViewController_iPad.(h|m|xib)

在大多数情况下,这非常方便。大多数逻辑都在超类中,子类负责任何特定于设备的实现。

这是我不明白的部分。有时我有代码在每个子类中执行 same 的事情,只是因为我需要为每个设备加载不同的 xib。例如:

ExampleViewController_iPhone

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    Content *selectedContent = (Content *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
    ContentDetailViewController_iPhone *detailViewController = [[ContentDetailViewController_iPhone alloc] init];
    detailViewController.content = selectedContent;
    detailViewController.managedObjectContext = self.managedObjectContext;

    [self.navigationController pushViewController:detailViewController animated:YES];
    [detailViewController release];

ExampleViewController_iPad

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    Content *selectedContent = (Content *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
    ContentDetailViewController_iPad *detailViewController = [[ContentDetailViewController_iPad alloc] init];
    detailViewController.content = selectedContent;
    detailViewController.managedObjectContext = self.managedObjectContext;

    [self.navigationController pushViewController:detailViewController animated:YES];
    [detailViewController release];

... 请注意,在第二种情况下,唯一不同的是它正在加载视图控制器的_iPad 版本。这是必要的,因为 iPadiPhone 视图控制器附加到单独的设备特定 nib。

这样做的“正确”模式是什么?


更新

我发现this answer 关于使用设备修饰符加载单独的 xib,这似乎在我不需要为一个设备指定特定子类的情况下会有所帮助,但如果我需要实例化一个用于设备特定功能的视图控制器的特定 _iPhone_iPad 实例。

【问题讨论】:

【参考方案1】:

有两种简单的方法可以解决您的问题。放在你的超类中时,两者都可以工作。

第一种方法之所以有效,是因为您有两个不同的类,而创建哪个类取决于所使用的设备。如果您不使用不同的类,它不起作用,因为没有特定于设备的代码。它涉及要求对象的类来决定它是哪个类。由于对象的类将是特定于设备的类,即使在超类要求时,您也可以查看创建了哪个类并采取相应措施。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    Content *selectedContent = (Content *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
    Class classToUse;
    if([self class] == [ExampleViewController_iPad class]) 
        // We are running on the iPad
        classToUse = [ContentDetailViewController_iPad class];
     else 
        // We must be running on either the iPhone or iPod touch
        classToUse = [ContentDetailViewController_iPhone class];
    
    // Just use superclass for typing here, since we aren't doing anything device specific
    ContentDetailViewController *detailViewController = [[classToUse alloc] init];
    detailViewController.content = selectedContent;
    detailViewController.managedObjectContext = self.managedObjectContext;

    [self.navigationController pushViewController:detailViewController animated:YES];
    [detailViewController release];

第二种方法适用于程序中的任何位置。 Apple 在 ios 3.2 中向 UIDevice 类添加了一个属性,称为“用户界面习语”。当前有两个可能的值:UIUserInterfaceIdiomPadUIUserInterfaceIdiomPhone。由于这些在 3.2 之前的版本中不存在,Apple 还添加了一个宏,如果版本小于 3.2,它将返回 UIUserInterfaceIdiomPhone,如果大于或等于 3.2,则从 UIDevice 对象获取实际值。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    Content *selectedContent = (Content *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
    Class classToUse;
    // If you aren't supporting versions prior to 3.2, you can use [UIDevice currentDevice].userInterfaceIdiom instead to save a couple of cycles
    if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) 
        // We are running on the iPad
        classToUse = [ContentDetailViewController_iPad class];
     else 
        // We must be running on either the iPhone or iPod touch
        classToUse = [ContentDetailViewController_iPhone class];
    
    // Just use superclass for typing here, since we aren't doing anything device specific
    ContentDetailViewController *detailViewController = [[classToUse alloc] init];
    detailViewController.content = selectedContent;
    detailViewController.managedObjectContext = self.managedObjectContext;

    [self.navigationController pushViewController:detailViewController animated:YES];
    [detailViewController release];

【讨论】:

+1 一个简单、优雅且解释清楚的解决方案。正是我需要的。我没有意识到您可以存储类引用以供以后在 Objective C 中使用,因此您的示例特别有用。谢谢!

以上是关于在通用应用程序中实例化设备特定视图控制器的正确模式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

特定 javascript 实例化模式的正确打字稿定义是啥

在特定视图控制器中处理 PushNotifcations

如何使用选项卡和导航控制器实例化视图控制器以进行深度链接?

如何首先在容器页面视图控制器中实例化任何视图?

支持 iOS 库中的特定设备方向 - 一处更改

如何从 OS X 上的框架实例化 NSViewController(即获取正确的包)