要求 UISplitViewController 为细节视图控制器延迟加载多个视图控制器

Posted

技术标签:

【中文标题】要求 UISplitViewController 为细节视图控制器延迟加载多个视图控制器【英文标题】:Require UISplitViewController to lazy load multiple view controllers for detail view controller 【发布时间】:2011-02-16 09:07:58 【问题描述】:

好吧,标题可能不清楚。我已经从 Apple 的 MultipleDetailView 示例代码中删除了这个。每次用户从弹出的表中选择一行时, detailViewController 都会再次分配 FirstDetailViewController 和 SecondDetailViewController。我不想一遍又一遍地分配和初始化视图控制器,我想将现有的和已经分配和初始化的视图控制器分配给选择行时的 detailViewController。我已经修改了拆分视图模板而不是示例代码来实现我所需要的。程序代码:

这是 AppDelegate.h 文件:

@interface iPadHelloWorldAppDelegate : NSObject <UIApplicationDelegate> 

    UIWindow *window;

    UISplitViewController *splitViewController;

    MasterViewController *masterViewController;
    DetailViewController *detailViewController;
    SecondDetailViewController *secondDetailViewController;

这是 AppDelegate.m 文件:

 masterViewController = [[MasterViewController alloc] init];
 UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
 detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:nil];
 secondDetailViewController = [[SecondDetailViewController alloc] initWithNibName:@"SecondDetailView" bundle:nil];
 splitViewController = [[UISplitViewController alloc] init];
 splitViewController.viewControllers = [NSArray arrayWithObjects:navigationController, detailViewController, nil];
    splitViewController.delegate = detailViewController;
    // Add the split view controller's view to the window and display.
    [window addSubview:splitViewController.view];
    [window makeKeyAndVisible];

这是 MasterViewController.m:

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    NSUInteger row = indexPath.row;
    [self.appDelegate.splitViewController viewWillDisappear:YES];
    self.tempArrays = [NSMutableArray arrayWithArray:self.appDelegate.splitViewController.viewControllers];
    [self.tempArrays removeLastObject];
    if (row == 0) 
        [self.tempArrays addObject:self.appDelegate.detailViewController];
        self.appDelegate.splitViewController.delegate = self.appDelegate.detailViewController;
    
    if (row == 1) 
        [self.tempArrays addObject:self.appDelegate.secondDetailViewController];
        self.appDelegate.splitViewController.delegate = self.appDelegate.secondDetailViewController;
    
    self.appDelegate.splitViewController.viewControllers = self.tempArrays;
    [self.appDelegate.splitViewController viewWillAppear:YES];

这是 DetailViewController.m:

#pragma mark -
#pragma mark Split view support

- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc 

    barButtonItem.title = @"Master List";
    [navigationBar.topItem setLeftBarButtonItem:barButtonItem animated:NO];
    self.popoverController = pc;


// Called when the view is shown again in the split view, invalidating the button and popover controller.
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem 

    [navigationBar.topItem setLeftBarButtonItem:nil animated:NO];
    self.popoverController = nil;

我可以延迟加载视图控制器,但是当我点击弹出框的栏按钮并跳转到第二个视图控制器时,第二个视图控制器不会显示弹出框。当我跳回第一个详细视图控制器时,会显示弹出框。

基本上,here 是一个类似的问题。但是那里的投递箱的链接不起作用。

【问题讨论】:

【参考方案1】:

既然你要求我开枪,那就这样吧。从我们这里的观点来看,SplitViewController 相当有问题。如果您不完全按照 Apple 在示例代码中的做法,我们会遇到很多问题。

首先,我建议您重新获取示例代码并从头开始,因为您似乎修改了很多。

至于您的问题:在您的委托和 MainWindow.xib 中,您设置了您的 SplitViewController。最重要的是不要按照您的方式设置 viewControllers 数组。

我遇到的问题是,如果我覆盖 RootViewController,它会弄乱 SplitViewController 并产生与您遇到的类似的错误。

尝试只设置一次 RootViewController(TableViewController),并且永远不要在 viewControllers 属性中覆盖它。不过,这对于 DetailViewController 来说似乎没问题。

其次,您的代码应该放在其他地方,而不是 RootViewController。这应该仅适用于 tableView 数据源和内容。

试试这个并在这里反馈,我会尽快跟进。

祝你好运。

编辑:代码添加 - 在您的 RootViewController 中执行此操作:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
// Navigation logic may go here. Create and push another view controller.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) 


DetailViewController *dvC = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];

// take the original view controller from the splitviewcontroller as root
// appDelegateiPad defined like this in my appdelegate:
// #define appDelegateiPad ((AppDelegate_iPad *)[[UIApplication sharedApplication] delegate]) 
NSArray *viewControllers = [[NSArray alloc] initWithObjects:[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:0], dvC, nil];
        appDelegateiPad.splitViewController.viewControllers = viewControllers;
//careful with this, set it to whatever your delegate is in your case           
appDelegateiPad.splitViewController.delegate = dvC;
        [viewControllers release];  


//this is my version
//i have the popoverController property in my detailviewcontroller. this is where my splitviewcontroller delegate methods are. you need to set the popovercontroller property in the class where your splitviewcontroller delegate methos are
    dvC.popoverController = [[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:1] popoverController];     

     

【讨论】:

@Icky:嘿,谢谢你的回答,是的,splitviewcontroller 确实有问题。我按照你说的做了。我在我的委托和 MainWindow.xib 中设置了 splitView。但是如何设置视图控制器数组?我正在努力解决这个问题 :) 无论如何感谢您的帮助。 @Icky:顺便说一句,我只是想再次简化我的问题。当在根视图控制器中选择一行并且再次选择同一行时,我想加载一个新的视图控制器,我不应该再次为视图控制器分配,因为它已经分配了一次。 Apple 的示例代码不允许这样做。 分配一个新的视图控制器是可以的,这取决于你需要什么。检查我上面的代码 - 它适用于我...... @Icky:对不起,我可能不太清楚。我不想再分配了。如果之前已经分配过视图控制器,则基本上是延迟加载。 你没有设置 popovercontroller。你的新视图控制器应该有一个 popoverController 属性,一旦你替换它,就需要将它设置为当前的 popoverController。类似于:dvC.popoverController = [[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:1] popoverController];【参考方案2】:

尝试在这个对象的 viewDidLoad 中分配 viewControllers。给两个视图控制器一个标题并将它们放在一个数组中。如果需要,您可以将这些标题用于两个单元格文本标签。 在 didSelectRowAtIndexPath 中,您可以获得所选行的正确视图控制器

UIViewController <SubstitutableDetailViewController> *detailViewController = [theArray objectAtIndex:indexPath.row];

编辑:

@interface SomeClass : NSObject 
  NSArray *controllerArray;

@end
@implementation SomeClass

- (void) viewDidLoad 
  controllerArray = [[NSArray alloc] initWithObject://yourControllers//,nil];
  [super viewDidLoad]


- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section 

    // Two sections, one for each detail view controller.
    return 2;


- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

  //Create Cell

  UIViewCOntroller *controller = [controllerArray objectAtIndex:indexPath.row];
  cell.textLabel.text = controller.title;

  return cell;


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
  [detailViewController autorelease]; 
  detailViewController = [[controllerArray objectAtIndex:indexPath.row] retain];


@end

【讨论】:

这是一个解决方案(延迟加载)。但是每次选择一行时,都会执行一个 if 语句。不够用。请参阅我的编辑以了解我会选择的解决方案。 嘿,我试过了...我收到了这个错误 - 无法从没有窗口的视图中显示弹出框。 这段代码在你的 UISPlitViewController 中吗?错误(代码)在哪里? 我想如果你可以看一下示例程序MultipleDetailViews的代码会更容易。它在 RootViewController.m 中。我正在使用示例中完全相同的代码,并按照您的建议进行了更改。它在 UISplitVIewController 中。 嘿,垫子,我仍然不知道如何解决这个问题。我已经用我在程序中修改的一些内容更新了问题。【参考方案3】:

不必创建详细视图控制器的新实例或更新拆分视图控制器中的视图控制器。

试试这个。

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

        UIApplication *application = [UIApplication sharedApplication];
        AppDelegate *appDelegate = (AppDelegate*) [application delegate];
        appDelegate.detailViewController.label.text = @"Detail view controller updated";

    

PS:请拖一个标签到详细信息进行测试。

【讨论】:

以上是关于要求 UISplitViewController 为细节视图控制器延迟加载多个视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

UISplitViewController 一致分隔符

UISplitViewController - 并排或纵向叠加

在 UIViewControllers 和 UISplitViewController 之间导航 [关闭]

将 UIToolBar 放在 UISplitViewController 上方?

UISplitViewController + UISearchController 显示搜索细节

iPad 上的 UITraitCollection 和 UISplitViewController