两个UIViewControllers的故事

时间:2011-10-20 02:31:46

标签: objective-c ios xcode uiviewcontroller

所以我觉得我疯了。

我已更新到XCode 4.2,所以我有IOS 5 SDK。

我有一个应用程序在升级之前一直很好用。现在,我有一个奇怪的问题。它不适用于我的IOS 5 iPad 2,也不适用于IOS 5模拟器。在4.3模拟器中正常工作。

出于这个问题的目的,我有两个基于UIViewController的类。他们不使用NIB文件。一个名为HistoryBrowser,效果很好。另一个,NoteBrowseViewController,沿着相同的行构建,不会。

来自NoteBrowseView.Controller.h:

@interface NoteBrowseViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate, UIActionSheetDelegate,        UISearchDisplayDelegate, UITabBarDelegate, MKMapViewDelegate> {
  UITableView* tableView;
  ... buncha other vars ...
}
@property (retain, nonatomic) UITableView* tableView;
... buncha other properties ...

来自NoteBrowseViewController.m:

@synthesize tableView

- (id)initWithEditing:(BOOL)inEditingMode inserting:(BOOL)inserting {
self=[super init];
if (self) {
    self.isInserting=inserting;
    self.isEditing=inEditingMode;
    self.viewIsMap=NO;
    self.insertType=defInsertTypeLink;

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0],@"viewSortOrder",
                                 [NSNumber numberWithInt:1],@"priorityView",
                                 [NSNumber numberWithBool:NO],@"simpleView",
                                 nil];

    [defaults registerDefaults:appDefaults];

    self.viewIsSimple=[[NSUserDefaults standardUserDefaults]boolForKey:@"simpleView"];
}
return self;
}

-(void)loadView {
self.view=[[UIView alloc]initWithFrame:[[UIScreen mainScreen] applicationFrame]];

UIButton* aButton;
UIImage* buttonImage;
UIBarButtonItem* spacer=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

UISegmentedControl* sc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:@"Alpha",@"Edit", @"View", nil]];
[sc addTarget:self action:@selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[sc setWidth:55.0 forSegmentAtIndex:0];
[sc setWidth:55.0 forSegmentAtIndex:1];
[sc setWidth:55.0 forSegmentAtIndex:2];
sc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:@"viewSortOrder"];
sc.segmentedControlStyle=UISegmentedControlStyleBar;

UISegmentedControl* prisc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:@"Open",@"All", nil]];
[prisc addTarget:self action:@selector(prioritySegmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[prisc setWidth:55.0 forSegmentAtIndex:0];
[prisc setWidth:55.0 forSegmentAtIndex:1];
prisc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:@"priorityView"];
prisc.segmentedControlStyle=UISegmentedControlStyleBar;

UIBarButtonItem* segmentedButton=[[UIBarButtonItem alloc]initWithCustomView:sc];
UIBarButtonItem* prioritySegmentedButton=[[UIBarButtonItem alloc]initWithCustomView:prisc];

buttonImage=[UIImage imageNamed:@"13-plus.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* addButton=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(addButton:) forControlEvents:UIControlEventTouchUpInside];

buttonImage=[UIImage imageNamed:@"187-pencil.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* editButton=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(editButton:) forControlEvents:UIControlEventTouchUpInside];

buttonImage=[UIImage imageNamed:@"21-circle-east.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* simplify=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(simplify:) forControlEvents:UIControlEventTouchUpInside];

buttonImage=[UIImage imageNamed:@"25-circle-west.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* complexify=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(complexify:) forControlEvents:UIControlEventTouchUpInside];

buttonImage=[UIImage imageNamed:@"243-globe.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* map=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(mapify:) forControlEvents:UIControlEventTouchUpInside];

buttonImage=[UIImage imageNamed:@"162-receipt.png"];
aButton=[UIButton buttonWithType:UIButtonTypeCustom];
[aButton setImage:buttonImage forState:UIControlStateNormal];
aButton.frame=CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
UIBarButtonItem* notes=[[UIBarButtonItem alloc]initWithCustomView:aButton];
[aButton addTarget:self action:@selector(showNotes:) forControlEvents:UIControlEventTouchUpInside];

UIBarButtonItem* cancelButton=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(cancelButtonPressed:)];
self.doneToolbar=[NSArray arrayWithObjects:spacer, cancelButton, nil];

self.toolbar=[[[UIToolbar alloc]init]autorelease];

if(self.isEditing) {
    self.complexToolbar=[NSArray arrayWithObjects:simplify, spacer, segmentedButton, prioritySegmentedButton, spacer, cancelButton, nil];
    self.simpleToolbar=[NSArray arrayWithObjects:complexify, spacer, cancelButton, nil];
}
else {
    self.complexToolbar=[NSArray arrayWithObjects:simplify, map, spacer, segmentedButton, prioritySegmentedButton, spacer, addButton, editButton, cancelButton, nil];
    self.simpleToolbar=[NSArray arrayWithObjects:complexify, map, spacer, addButton, editButton, cancelButton, nil];
}
self.mapToolbar=[NSArray arrayWithObjects:notes, spacer, prioritySegmentedButton, spacer, cancelButton, nil];

if (self.viewIsSimple) {
    [self.toolbar setItems:self.simpleToolbar animated:YES];
}
else {
    [self.toolbar setItems:self.complexToolbar animated:YES];
}

self.toolbar.autoresizingMask=UIViewAutoresizingFlexibleWidth;        
[self.toolbar sizeToFit];

CGFloat toolbarHeight = [self.toolbar frame].size.height;
CGRect rootViewBounds=self.view.bounds;
CGFloat rootViewHeight = CGRectGetHeight(rootViewBounds);
CGFloat rootViewWidth = CGRectGetWidth(rootViewBounds);
[self.toolbar setFrame:CGRectMake(0,0, rootViewWidth, toolbarHeight)];

self.mapView=[[[MKMapView alloc]initWithFrame:CGRectMake(0, toolbarHeight, rootViewWidth, rootViewHeight-(self.isInserting?49:0)-toolbarHeight)]autorelease];
self.mapView.delegate=self;
self.mapView.zoomEnabled=YES;
self.mapView.scrollEnabled=YES;
self.mapView.autoresizingMask=UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

self.tableView=[[UITableView alloc]initWithFrame:CGRectMake(0, toolbarHeight, rootViewWidth, rootViewHeight-(self.isInserting?49:0)-toolbarHeight) 
                                           style:UITableViewStylePlain];
self.tableView.delegate=self;
self.tableView.dataSource=self;
self.tableView.autoresizingMask=UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

self.view.autoresizesSubviews = YES; 
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 44, rootViewWidth, 44.0)];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.tableView.tableHeaderView = searchBar;

self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;

[self.view addSubview:self.tableView];
[self.view addSubview:self.toolbar];

if (self.isInserting) {
    UITabBarItem* item1=[[[UITabBarItem alloc]initWithTitle:@"Link" image:nil tag:defInsertTypeLink]autorelease];
    UITabBarItem* item2=[[[UITabBarItem alloc]initWithTitle:@"Contents Later" image:nil tag:defInsertTypeContentsLater]autorelease];
    UITabBarItem* item3=[[[UITabBarItem alloc]initWithTitle:@"Contents Now" image:nil tag:defInsertTypeContentsNow]autorelease];
    UITabBar* tabbar=[[UITabBar alloc]initWithFrame:CGRectMake(0, rootViewHeight-49, rootViewWidth, 49)];
    tabbar.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
    [tabbar setDelegate:self];
    [tabbar setItems:[NSArray arrayWithObjects:item1, item2, item3, nil] animated:YES];
    [tabbar setSelectedItem:item1];
    [self.view addSubview:tabbar];
}

if(self.viewIsSimple) {
    self.contentSizeForViewInPopover = CGSizeMake(200.0, 625.0);         
}
else {
    self.contentSizeForViewInPopover = CGSizeMake(450.0, 625.0); 
}
self.view.autoresizesSubviews = YES; 
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.note=nil;

[sc release];
[prisc release];
[addButton release];
[prioritySegmentedButton release];
[cancelButton release];
[segmentedButton release];
[spacer release];
[editButton release];
[map release];
[simplify release];
[complexify release];
}

最后,一个NoteBrowseViewController从另一个视图控制器实例化:

self.noteBrowseViewController=[[NoteBrowseViewController alloc]initWithEditing:NO inserting:NO];
self.noteBrowseViewController.delegate=self;
self.popoverController = [[UIPopoverController alloc]initWithContentViewController:self.noteBrowseViewController];
self.popoverController.delegate = self;
[self.popoverController presentPopoverFromRect:((UIButton*)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

所以这就是发生的事情。如果我在调试中运行它,并继续执行,只要执行到达此行:

CGRect rootViewBounds=self.view.bounds;

程序因以下错误而崩溃:

2011-10-20 11:42:02.703 ActionNote3[12332:15803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]:  object cannot be nil'
*** First throw call stack:
(0x1eb0052 0x2a60d0a 0x1e9d36e 0x1e9e220 0x1968cb6 0x5153b 0x50e11 0x50879 0x52e4a 0xdcc64e 0x4bcb3 0x72d1 0x1eb1ec9 0xd095c2 0xd0955a 0xdaeb76 0xdaf03f 0xdae2fe 0xd2ea30 0xd2ec56 0xd15384 0xd08aa9 0x20c7fa9 0x1e841c5 0x1de9022 0x1de790a 0x1de6db4 0x1de6ccb 0x20c6879 0x20c693e 0xd06a9b 0x2dbd 0x2d35)
terminate called throwing an exception

通过在调试期间观察事物,我知道正在设置self.view,并且应该在调用loadView时调用它。并不是这条线让它失败了 - 它是任何涉及自我的东西!什么???

让我疯狂的是我有另一个子视图控制器,HistoryBrowser,以完全相同的方式加载......并且它工作正常。

所以,除了感到沮丧,我无法弄清楚这一点,我想明白:

  1. XCode 4.2(来自4.1)中的更改会导致此问题,或者是什么 这是一个IOS 5问题?
  2. 这个错误意味着什么,我该怎么办? 关于它?
  3. 修改

    因此根据Abberant在下面的回答中的建议,我:

    1. 将initwithnib更改为init
    2. 删除了loadView方法
    3. 添加了完整的错误消息
    4. 向init方法添加了更多相关代码
    5. 现在,引用self.view可以正常工作。但事情变得更加陌生。

      执行使它超过对self.view的第一次引用而没有错误。但是,它现在停止在:

      self.tableView.tableHeaderView = searchBar;
      

      在init方法中。

      收到的错误消息与我以前收到的错误消息非常类似:

      2011-10-20 12:34:04.818 ActionNote3[13487:15803] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
      *** First throw call stack:
      (0x1eaf052 0x2a5fd0a 0x1e9c36e 0x1e9d220 0x1967cb6 0x50b4b 0x50421 0x4fe89 0x4d757 0x6641 0x1eb0ec9 0xd085c2 0xd0855a 0xdadb76 0xdae03f 0xdad2fe 0xd2da30 0xd2dc56 0xd14384 0xd07aa9 0x20c6fa9 0x1e831c5 0x1de8022 0x1de690a 0x1de5db4 0x1de5ccb 0x20c5879 0x20c593e 0xd05a9b 0x212d 0x20a5)
      terminate called throwing an exception
      

      因此,如果我对该行进行注释,则执行将继续执行,直到它到达这一行,并在尝试执行performFetch时失败。

          NSError *error = nil;
          if (![[self currentFetchedResultsController] performFetch:&error]) {
              NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
              abort();
          }
      

      调试[self currentFetchedResultsController]的代码显示正在正确设置获取的结果控制器而没有问题。

      此处收到的错误与上述相同。第一个throw调用堆栈中的数字会发生变化,但错误是相同的。

      所以我想我在这里遇到了更大的问题。我根本不知道如何追踪它。正如我在某处所说的那样,我有另一种观点,以相同的方式构建,可以很好地加载。

      修改

      所以NJones在下面的回答中建议遵循Apple的指导方针,将相应的代码放在loadView和viewDidLoad中。所以我做了。我将所有视图构造移出init并进入loadView,并在viewDidLoad中启动了fetchedResultsController。

      该应用程序仍在同一位置崩溃:

      self.tableView.tableHeaderView = searchBar
      

      出现与上述相同的错误。

      谢谢!

4 个答案:

答案 0 :(得分:1)

我会说你应该使用苹果建议的加载模型。当你创建一个新的UIViewController子类(删节以适应)时,这是在xCode中生成的一些代码:

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
-(void)loadView{
}
*/

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
-(void)viewDidLoad{
    [super viewDidLoad];
}
*/

基于此,由于您是以编程方式创建视图,我会说您的init方法中的大部分内容都属于loadView方法。

答案 1 :(得分:1)

所以我找到了答案,并且如果没有看到更多代码,那么任何人都不会猜到这一点。但是,希望这个答案能够帮助其他可能遇到同样问题的人。

在我的loadView方法中(但最初是在我的init方法中,仍然在上面的代码中),我有以下几行:

UISegmentedControl* sc=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:@"Alpha",@"Edit", @"View", nil]];
[sc addTarget:self action:@selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
[sc setWidth:55.0 forSegmentAtIndex:0];
[sc setWidth:55.0 forSegmentAtIndex:1];
[sc setWidth:55.0 forSegmentAtIndex:2];
sc.selectedSegmentIndex=[[NSUserDefaults standardUserDefaults] integerForKey:@"viewSortOrder"];
sc.segmentedControlStyle=UISegmentedControlStyleBar;

segmentedControlValueChanged:将设置字符串var(并在用户默认值中存储所选按钮的索引)。字符串var后来用于在启动fetchedresultscontroller时确定排序顺序。

在IOS 5之前,当我设置sc.selectedSegmentIndex时,选择器中标识的方法将被触发,并且所有内容都将被正确设置。在IOS 5中,不再是这种情况,并且不会设置字符串var(设置排序顺序)。

有趣的是,在执行此行时:

self.tableView.tableHeaderView = searchBar;

会导致访问fetchedReultsController,并且通过访问它,它将被初始化。当它被初始化时,它将设置排序顺序并为字段名称包含空值,因此错误消息现在是有意义的。

因此,问题的根本原因是在IOS 5之前,在UISegmentedControl上设置selectedSegmentIndex会触发与控件关联的操作。从IOS 5开始,现在不再是这种情况了。

问题的原因,有几个人帮我解决了,就是我没有正确地遵循viewController加载过程。我没有使用NIB文件。 Apple说如果你不使用nib文件,你必须定义loadView来设置视图。如果未定义loadView,则在内部设置self.view,并调用viewDidLoad。应用程序在self.view的第一个引用崩溃的原因是,我在viewDidLoad中有逻辑来设置fetchedResultsController。当我将视图设置逻辑移动到loadView时,对self.view的引用不再启动fetchedresultscontroller。

答案 2 :(得分:0)

暂时考虑并寻找有关此信息的信息,但它可能是任何数量的东西。也许self.view在loadView之前被调用,也许是因为initWithNibName触发了你不想要的东西,也许是因为你的视图小于屏幕/窗口,这是不建议的,也许新的iOS有一个bug它...由于你的代码很少,没有办法调试和不完整的错误信息,我看不出是什么导致它。

但是,我可能有一个解决方法:

如何删除loadView覆盖以便viewController创建自己的视图,然后将mainView添加到该默认视图?

答案 3 :(得分:0)

例外情况表示您正在为数组添加nil。我认为tableViewHeader可以被认为是objectAtIndex:0,因为它是行数组的第一个。试着把这一行放在它上面:

NSLog(@"searchBar = %@",searchBar;
self.tableView.tableHeaderView = searchBar;

编辑(1): 然后试试这些:

NSLog(@"self = %@",self);
NSLog(@"self.tableView = %@",self.tableView);
NSLog(@"self.tableView.tableHeaderView = %@",self.tableView.tableHeaderView);
self.tableView.tableHeaderView = searchBar;

编辑(2):

if ([self.tableView respondsToSelector:@selector(setTableHeaderView:)]){
    NSLog(@"tableView does respond");
    [self.tableView setTableHeaderView:nil];
}

我认为这不会解决问题,但它应该有助于解决问题。