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

时间:2011-04-12 01:30:52

标签: objective-c cocoa-touch uiviewcontroller cocoa-design-patterns

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

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

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

这是我没有得到的部分。有时候我的代码在每个子类中执行相同的事情,因为我需要为每个设备加载不同的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];
}

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

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


更新

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

1 个答案:

答案 0 :(得分: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,如果大于或等于,则从UIDevice对象获取实际值3.2。

- (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];
}