Эта строка:
[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;
В файле .m VC1 метод кнопки отклонения должен вызывать метод делегата
- (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:
может быть тем же методом, что и VC2Pressed:
метод IBAction вашей кнопки. Обратите внимание, что он вызывается из блока завершения, чтобы гарантировать, что VC2 не будет представлен, пока VC1 не будет полностью отклонен.
Теперь вы переходите от VC1-> VCMain-> VC2, поэтому вы, вероятно, захотите анимировать только один из переходов.
Обновить
В своих комментариях вы выражаете удивление по поводу сложности, необходимой для достижения, казалось бы, простой вещи. Уверяю вас, этот шаблон делегирования настолько важен для большей части Objective-C и Какао, и этот пример - самый простой из возможных, и вам действительно стоит приложить усилия, чтобы освоиться с ним.
В компании Apple Программирование View Controller Guide они это , чтобы сказать :
Отклонение представленного контроллера представления
Когда приходит время отклонить представленный контроллер представления, предпочтительный подход состоит в том, чтобы позволить контроллеру представления представления отклонить его. Другими словами, всякий раз, когда это возможно, тот же контроллер представления, который представил контроллер представления, также должен нести ответственность за его отклонение. Хотя существует несколько методов для уведомления контроллера представления представления о том, что его представленный контроллер представления должен быть отклонен, предпочтительным методом является делегирование. Для получения дополнительной информации см. «Использование делегирования для связи с другими контроллерами».
Если вы действительно продумаете, чего хотите достичь и как вы это делаете, вы поймете, что обмен сообщениями с вашим MainViewController для выполнения всей работы является единственным логическим выходом, учитывая, что вы не хотите использовать NavigationController. Если вы действительно используете NavController, вы фактически «делегируете», даже если не явно, navController для выполнения всей работы. Должен быть какой-то объект, который будет отслеживать все, что происходит с вашей навигацией VC, и вам нужен какой-то способ связи с ним, что бы вы ни делали.
На практике совет Apple немного экстремален ... в обычных случаях вам не нужно создавать специального делегата и метода, на которые вы можете положиться [self presentingViewController] dismissViewControllerAnimated:
- это когда в таких случаях, как ваш, вы хотите, чтобы ваше увольнение имело другие последствия для удаленного управления. предметы, за которыми нужно ухаживать.
Вот что вы можете себе представить, работая без хлопот с делегатами ...
- (IBAction)dismiss:(id)sender {
[[self presentingViewController] dismissViewControllerAnimated:YES
completion:^{
[self.presentingViewController performSelector:@selector(presentVC2:)
withObject:nil];
}];
}
После того, как представляющий контроллер попросил нас уволить, у нас есть блок завершения, который вызывает метод в PresentingViewController для вызова VC2. Делегат не требуется. (Большой плюс блоков в том, что они уменьшают потребность в делегатах в этих обстоятельствах). Однако в этом случае есть несколько вещей, которые мешают ...
- в VC1 вы не знаете, что mainVC реализует этот метод
present2
- вы можете столкнуться с трудными для отладки ошибками или сбоями. Делегаты помогут вам избежать этого.
- после того, как VC1 отклонен, на самом деле не нужно выполнять блок завершения ... или нет? Имеет ли self.presentingViewController что-нибудь еще? Вы не знаете (я тоже) ... с делегатом у вас нет этой неопределенности.
- Когда я пытаюсь запустить этот метод, он просто зависает без предупреждений или ошибок.
Так что, пожалуйста ... найдите время, чтобы изучить делегирование!
update2
В своем комментарии вам удалось заставить его работать, используя это в обработчике кнопки увольнения VC2:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Это, конечно, намного проще, но при этом возникает ряд проблем.
Тесная связь.
Вы жестко связываете структуру viewController вместе. Например, если вы вставите новый viewController перед mainVC, ваше требуемое поведение нарушится (вы перейдете к предыдущему). В VC1 вам также нужно было #import VC2. Поэтому у вас довольно много взаимозависимостей, что нарушает цели ООП / MVC.
При использовании делегатов ни VC1, ни VC2 не нужно ничего знать о mainVC или его предшественниках, поэтому мы сохраняем все слабо связанное и модульное.
Память
VC1 никуда не делась, у вас все еще есть два указателя на нее:
presentedViewController
собственность mainVC
- VC2 в
presentingViewController
недвижимость
Вы можете проверить это, зарегистрировавшись, а также просто сделав это из VC2.
[self dismissViewControllerAnimated:YES completion:nil];
Он по-прежнему работает, по-прежнему возвращает вас к VC1.
Мне это кажется утечкой памяти.
Ключ к этому - в предупреждении, которое вы здесь получаете:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
Логика нарушается, поскольку вы пытаетесь отклонить представляющий VC, для которого VC2 является представленным VC. Второе сообщение на самом деле не выполняется - возможно, что-то происходит, но у вас все еще остаются два указателя на объект, от которого, как вы думали, вы избавились. ( править - я проверил это, и это не так уж плохо, оба объекта исчезнут, когда вы вернетесь в mainVC )
Это довольно длинный способ сказать - пожалуйста, используйте делегатов. Если это поможет, я сделал здесь еще одно краткое описание шаблона:
всегда ли передача контроллера в конструктор - плохая практика?
update 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, но это лучшее, что вы получите без небольшого явного делегирования.