Ответ 1
Эта строка:
[self dismissViewControllerAnimated:YES completion:nil];
не отправляет сообщение самому себе, он фактически отправляет сообщение своему представляющему VC, прося его отклонить. Когда вы представляете VC, вы создаете связь между представленным VC и представленным. Поэтому вы не должны уничтожать представленный VC во время его представления (представленный VC не может отправить сообщение об увольнении назад...). Поскольку вы не учитываете это, вы оставляете приложение в запутанном состоянии. См. Мой ответ Отклонение контроллера представления данных в котором я рекомендую этот метод более четко написано:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
В вашем случае вам нужно убедиться, что все управление выполнено в mainVC
. Вы должны использовать делегата для отправки правильного сообщения обратно в MainViewController из ViewController1, так что mainVC может отклонить VC1, а затем представить VC2.
В VC2 VC1 добавьте протокол в ваш .h файл над @interface:
@protocol ViewController1Protocol <NSObject>
- (void)dismissAndPresentVC2;
@end
и ниже в том же файле в разделе @interface объявите свойство для хранения указателя делегата:
@property (nonatomic,weak) id <ViewController1Protocol> delegate;
В файле VC1.m метод кнопки-приглашения должен вызвать метод делегата
- (IBAction)buttonPressedFromVC1:(UIButton *)sender {
[self.delegate dissmissAndPresentVC2]
}
Теперь в mainVC установите его как делегат VC1 при создании VC1:
- (IBAction)present1:(id)sender {
ViewController1* vc = [[ViewController1 alloc] initWithNibName:@"ViewController1" bundle:nil];
vc.delegate = self;
[self present:vc];
}
и реализовать метод делегирования:
- (void)dismissAndPresent2 {
[self dismissViewControllerAnimated:NO completion:^{
[self present2:nil];
}];
}
present2:
может быть тем же методом, что и ваш метод IBAction VC2Pressed:
. Обратите внимание, что он вызывается из блока завершения, чтобы гарантировать, что VC2 не будет представлен, пока VC1 не будет полностью уволен.
Теперь вы переходите из VC1- > VCMain- > VC2, поэтому вам, возможно, понадобится только один из переходов для анимации.
обновление
В ваших комментариях вы выражаете удивление по поводу сложности, необходимой для достижения, казалось бы, простой вещи. Уверяю вас, эта диаграмма делегирования настолько важна для большинства Objective-C и Cocoa, и этот пример касается самого простого, что вы можете получить, что вы действительно должны приложить усилия, чтобы получить с ним удобство.
В Руководстве по программированию контроллера Apple View есть чтобы сказать:
Отклонение контроллера представления данных
Когда приходит время отклонить представленный контроллер представлений, предпочтительный подход - позволить диспетчеру представления представления отклонить его. Другими словами, по возможности, тот же контроллер представления, который представил контроллер представления, должен также нести ответственность за его увольнение. Несмотря на то, что существует несколько методов уведомления диспетчера представления представления о том, что его представленный контроллер представления должен быть отклонен, предпочтительным методом является делегирование. Дополнительные сведения см. В разделе "Использование делегирования для связи с другими контроллерами".
Если вы действительно продумаете то, что хотите достичь, и как вы собираетесь это делать, вы поймете, что обмен сообщениями с вашим MainViewController для выполнения всей работы - это единственный логический выход из-за того, что вы не хотите использовать навигационный контроллер. Если вы используете NavController, вы фактически делегируете, даже если не явно, на navController для выполнения всей работы. Там должен быть какой-то объект, который держит центральную часть того, что происходит с вашей навигацией по VC, и вам нужен какой-то способ общения с ним, что бы вы ни делали.
На практике советы Apple немного экстремальны... в обычных случаях вам не нужно делать выделенный делегат и метод, вы можете положиться на [self presentingViewController] dismissViewControllerAnimated:
- это, когда в таких случаях, как вы, хотите, чтобы вы отклонили иметь другие эффекты на удаленных объектах, которые вам нужно позаботиться.
Вот что вы можете себе представить, чтобы работать без всякой проблемы с делегатом...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
После запроса диспетчера представления отклонить нас, у нас есть блок завершения, который вызывает метод в представленииViewController для вызова VC2. Никакой делегат не нужен. (Большая точка продажи блоков заключается в том, что они уменьшают потребность в делегатах в этих обстоятельствах). Однако в этом случае есть несколько вещей, мешающих...
- в VC1 вы не знаете, что mainVC реализует метод
present2
- вы можете в конечном итоге с трудными для отладки ошибок или сбоев. Делегаты помогут вам избежать этого. - после того, как VC1 будет уволен, на самом деле это не так, чтобы выполнить блок завершения... или это так? Может ли self.presentingViewController что-нибудь значить? Вы не знаете (я тоже)... с делегатом, у вас нет этой неопределенности.
- Когда я пытаюсь запустить этот метод, он просто зависает без предупреждения или ошибок.
Так что, пожалуйста... найдите время, чтобы изучить делегацию!
Update2
В вашем комментарии вам удалось заставить его работать, используя это в обработчике кнопки отклонения VC2:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Это, конечно, намного проще, но это оставляет вам ряд проблем.
Плотная муфта
Вы прочно соединяете структуру viewController. Например, если вы должны вставить новый viewController перед mainVC, ваше искомое поведение сломается (вы перейдете к предыдущему). В VC1 вам также пришлось #import VC2. Поэтому у вас довольно много взаимозависимостей, что нарушает цели OOP/MVC.
Используя делегатов, ни VC1, ни VC2 не должны ничего знать о mainVC или его антецедентах, поэтому мы сохраняем все слабосвязанные и модульные.
памяти
VC1 не ушел, у вас все еще есть два указателя:
- mainVC
presentedViewController
свойство - Свойство VC2
presentingViewController
Вы можете проверить это путем ведения журнала, а также просто сделать это из VC2
[self dismissViewControllerAnimated:YES completion:nil];
Он по-прежнему работает, все еще возвращает вас в VC1.
Мне кажется, что это утечка памяти.
Ключ к этому заключается в предупреждении, которое вы получаете здесь:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
// Attempt to dismiss from view controller <VC1: 0x715e460>
// while a presentation or dismiss is in progress!
Логика ломается, поскольку вы пытаетесь отклонить представленный VC, VC2 которого является представленным VC. Второе сообщение действительно не выполняется. Возможно, что-то происходит, но вы все еще остаетесь с двумя указателями на объект, о котором вы думали, что избавились. (edit - я проверил это, и это не так уж плохо, оба объекта исчезают, когда вы возвращаетесь к mainVC)
Это довольно длинный способ сказать - пожалуйста, используйте делегатов. Если это поможет, я сделал еще одно краткое описание шаблона здесь:
Проводят ли контроллер в конструторе всегда плохую практику?
обновление 3
Если вы действительно хотите избежать делегатов, это может быть лучшим выходом:
В VC1:
[self presentViewController:VC2
animated:YES
completion:nil];
Но не отвергайте ничего... как мы выяснили, на самом деле этого не происходит.
В VC2:
[self.presentingViewController.presentingViewController
dismissViewControllerAnimated:YES
completion:nil];
Как мы (знаем), мы не отклонили VC1, мы можем вернуться через VC1 в MainVC. MainVC отклоняет VC1. Поскольку VC1 ушел, он представил VC2 с ним, поэтому вы вернулись в MainVC в чистом состоянии.
Он все еще сильно связан, так как VC1 должен знать о VC2, а VC2 должен знать, что он был получен через MainVC- > VC1, но это лучшее, что вы получите без частичного делегирования.