Эта строка:
[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, но это лучшее, что вы получите без небольшого явного делегирования.
Пример на Swift , изображающий объяснение литейного завода выше и документацию Apple:
ViewController.swift
import UIKit protocol ViewControllerProtocol { func dismissViewController1AndPresentViewController2() } class ViewController: UIViewController, ViewControllerProtocol { @IBAction func goToViewController1BtnPressed(sender: UIButton) { let vc1: ViewController1 = self.storyboard?.instantiateViewControllerWithIdentifier("VC1") as ViewController1 vc1.delegate = self vc1.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal self.presentViewController(vc1, animated: true, completion: nil) } func dismissViewController1AndPresentViewController2() { self.dismissViewControllerAnimated(false, completion: { () -> Void in let vc2: ViewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as ViewController2 self.presentViewController(vc2, animated: true, completion: nil) }) } }
ViewController1.swift
import UIKit class ViewController1: UIViewController { var delegate: protocol<ViewControllerProtocol>! @IBAction func goToViewController2(sender: UIButton) { self.delegate.dismissViewController1AndPresentViewController2() } }
ViewController2.swift
import UIKit class ViewController2: UIViewController { }
ViewController.swift
import UIKit protocol ViewControllerProtocol { func popViewController1AndPushViewController2() } class ViewController: UIViewController, ViewControllerProtocol { @IBAction func goToViewController1BtnPressed(sender: UIButton) { let vc1: ViewController1 = self.storyboard?.instantiateViewControllerWithIdentifier("VC1") as ViewController1 vc1.delegate = self self.navigationController?.pushViewController(vc1, animated: true) } func popViewController1AndPushViewController2() { self.navigationController?.popViewControllerAnimated(false) let vc2: ViewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as ViewController2 self.navigationController?.pushViewController(vc2, animated: true) } }
ViewController1.swift
import UIKit class ViewController1: UIViewController { var delegate: protocol<ViewControllerProtocol>! @IBAction func goToViewController2(sender: UIButton) { self.delegate.popViewController1AndPushViewController2() } }
ViewController2.swift
import UIKit class ViewController2: UIViewController { }
источник
ViewController
класс mainVC правильно?Я думаю, вы неправильно поняли некоторые основные концепции контроллеров модального представления iOS. Когда вы отклоняете VC1, все представленные контроллеры представления VC1 также отклоняются. Apple предназначила для контроллеров модальных представлений работать с накоплением - в вашем случае VC2 представлен VC1. Вы отклоняете VC1, как только представляете VC2 из VC1, так что это полный беспорядок. Чтобы добиться того, чего вы хотите, buttonPressedFromVC1 должен иметь mainVC, представляющий VC2 сразу после того, как VC1 закрывается. И я думаю, что этого можно добиться без делегатов. Что-то вроде:
UIViewController presentingVC = [self presentingViewController]; [self dismissViewControllerAnimated:YES completion: ^{ [presentingVC presentViewController:vc2 animated:YES completion:nil]; }];
Обратите внимание, что self.presentingViewController хранится в какой-то другой переменной, потому что после того, как vc1 закрывается, вы не должны делать на него никаких ссылок.
источник
[self dismiss...]
происходит после[self present...]
завершения? Дело не в том, что происходит что-то асинхронноеРаду Симионеску - классная работа! и ниже Ваше решение для любителей Swift:
@IBAction func showSecondControlerAndCloseCurrentOne(sender: UIButton) { let secondViewController = storyboard?.instantiateViewControllerWithIdentifier("ConrollerStoryboardID") as UIViewControllerClass // change it as You need it var presentingVC = self.presentingViewController self.dismissViewControllerAnimated(false, completion: { () -> Void in presentingVC!.presentViewController(secondViewController, animated: true, completion: nil) }) }
источник
Я хотел вот этого:
MapVC - это карта в полноэкранном режиме.
Когда я нажимаю кнопку, над картой открывается PopupVC (не в полноэкранном режиме).
Когда я нажимаю кнопку в PopupVC, она возвращается в MapVC, а затем я хочу выполнить viewDidAppear.
Я сделал это:
MapVC.m: в действии кнопки программный переход и установка делегата
- (void) buttonMapAction{ PopupVC *popvc = [self.storyboard instantiateViewControllerWithIdentifier:@"popup"]; popvc.delegate = self; [self presentViewController:popvc animated:YES completion:nil]; } - (void)dismissAndPresentMap { [self dismissViewControllerAnimated:NO completion:^{ NSLog(@"dismissAndPresentMap"); //When returns of the other view I call viewDidAppear but you can call to other functions [self viewDidAppear:YES]; }]; }
PopupVC.h: перед @interface добавьте протокол
@protocol PopupVCProtocol <NSObject> - (void)dismissAndPresentMap; @end
после @interface новое свойство
@property (nonatomic,weak) id <PopupVCProtocol> delegate;
PopupVC.m:
- (void) buttonPopupAction{ //jump to dismissAndPresentMap on Map view [self.delegate dismissAndPresentMap]; }
источник
Я решил проблему, используя UINavigationController при презентации. В MainVC при представлении VC1
let vc1 = VC1() let navigationVC = UINavigationController(rootViewController: vc1) self.present(navigationVC, animated: true, completion: nil)
В VC1, когда я хочу показать VC2 и одновременно закрыть VC1 (только одна анимация), я могу использовать анимацию push с помощью
let vc2 = VC2() self.navigationController?.setViewControllers([vc2], animated: true)
А в VC2, закрывая контроллер представления, мы, как обычно, можем использовать:
self.dismiss(animated: true, completion: nil)
источник