Повесть о двух UIViewControllers - PullRequest
       1

Повесть о двух UIViewControllers

2 голосов
/ 20 октября 2011

Так что я думаю, что схожу с ума.

Я обновился до 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 вызывается, когда это должно быть.И не эта строка делает его неудачным - это все, что относится к self.view!что ???

Что действительно сводит меня с ума, так это то, что у меня есть другой контроллер подклассов, HistoryBrowser, который загружается точно таким же образом ... и он отлично работает.

Итак,Помимо того, что я расстроен, что я не могу понять это, я хотел бы понять:

  1. Что изменилось в XCode 4.2 (из 4.1), что может вызвать это, или это проблема IOS 5?
  2. Что означает эта ошибка, и что я могу с ней сделать?

РЕДАКТИРОВАТЬ:

Таким образом, на основе предложений из ответа Abberantниже, я:

  1. изменил initwithnib просто на init
  2. удалил метод loadView
  3. добавил полное сообщение об ошибке
  4. добавил более актуальноекод метода init

И теперь ссылка на 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

Итак, если я закомментирую эту строку, выполнение продолжается до тех пор, пока не достигнет этой группы строк, где происходит сбой при попытке выполнить executeFetch.

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

Отладка в код для показа [self currentFetchedResultsController]что контроллер выбранных результатов настраивается правильно и без проблем.

Полученная здесь ошибка такая же, как и выше.Числа в первом стеке вызовов вызовов меняются, но в остальном ошибка та же.

Так что я остаюсь, думая, что у меня здесь возникает большая проблема.Я просто не знаю, как отследить это.Как я сказал где-то там, у меня есть другое представление, сконструированное таким же образом, которое прекрасно загружается.

Редактировать:

Так что NJones в своем ответе ниже предложилследуя рекомендациям Apple, поместив соответствующий код в loadView и viewDidLoad.Так я и сделал.Я переместил всю конструкцию представления из init в loadView и запустил fetchedResultsController в viewDidLoad.

Приложение по-прежнему падает на том же месте:

self.tableView.tableHeaderView = searchBar

С той же ошибкой, что и отмеченнаявыше.

Спасибо!

Ответы [ 4 ]

1 голос
/ 22 октября 2011

Так что я нашел ответ, и никто бы не догадался, не увидев намного больше кода.Но, надеюсь, этот ответ поможет другим людям, которые могут столкнуться с тем же.

В том, что стало моим методом 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 использовалась для определения порядка сортировки при запуске моего контроллера fetchedresults.

До IOS 5, когда я устанавливал sc.selectedSegmentIndex, метод, указанный в селекторе, запускался, и все настраивалосьдолжным образом.В IOS 5 это больше не так, и строка var (установка порядка сортировки) не будет установлена.

Интересно то, что при выполнении эта строка:

self.tableView.tableHeaderView = searchBar;

приведет к доступу к fetchedReultsController, и при обращении к нему он будет инициализирован.Когда он пошел на инициализацию, он установил бы порядок сортировки и включил бы нулевое значение для имени поля, отсюда и сообщение об ошибке, которое теперь имеет смысл.

Таким образом, основной причиной проблемы было то, что до IOS 5 установка selectedSegmentIndex в UISegmentedControl запускала действие, связанное с элементом управления.Начиная с IOS 5, это уже не так.

Первоначальная причина проблемы, которую мне помогли решить несколько человек, заключалась в том, что я не следовал процессу загрузки viewController должным образом.Я не использую файлы NIB.Apple говорит, что если вы не используете nib-файл, вам нужно определить loadView, чтобы настроить представление.Если loadView не определен, self.view устанавливается внутри и вызывается viewDidLoad.Причина сбоя приложения при первой ссылке на self.view заключается в том, что у меня была логика в viewDidLoad для настройки fetchedResultsController.Когда я переместил логику установки представления в loadView, ссылки на self.view больше не запускали fetchedresultscontroller.

1 голос
/ 20 октября 2011

Я бы сказал, что вы должны использовать модель загрузки, предложенную Apple. Вот часть кода, сгенерированного в xCode, когда вы создаете новый подкласс UIViewController (сокращенно, чтобы соответствовать):

/*
// 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.

0 голосов
/ 21 октября 2011

Исключение говорит, что вы добавляете ноль в массив.Я думаю, что 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];
}

Я надеваюНе думаю, что это решит проблему, но это должно помочь выследить проблему.

0 голосов
/ 20 октября 2011

Некоторое время мы думали и искали информацию об этом, но это может быть сколько угодно.Возможно, self.view вызывается до loadView где-то, может быть, это потому, что initWithNibName вызывает то, что вам не нужно, может быть, потому что ваш вид меньше экрана / окна, что не рекомендуется, возможно, в новой iOS есть ошибка вэто ... С таким небольшим количеством вашего кода, без возможности отладки и неполным сообщением об ошибке, я не могу понять, что его вызывает.

Однако у меня может быть обходной путь:

Как насчет удаления этого переопределения loadView, чтобы viewController создал свое собственное представление, а затем добавьте свой mainView в это представление по умолчанию?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...