Цей рядок:
[self dismissViewControllerAnimated:YES completion:nil];
не надсилає повідомлення собі, насправді надсилає повідомлення своєму представницькому ВК, просячи його звільнити. Коли ви представляєте ВК, ви створюєте взаємозв'язок між представленим ВК і представленим. Отже, ви не повинні знищувати презентуючий ВК під час його презентації (представлений ВК не може відправити це повідомлення про відмову назад…). Оскільки ви насправді не враховуєте це, ви залишаєте програму в розгубленому стані. Дивіться мою відповідь Звільнення представленого контролера
подання, в якій я рекомендую цей метод чіткіше написаний:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
У вашому випадку вам потрібно переконатися, що весь контроль здійснюється в mainVC
. Вам слід використовувати делегата, щоб відправити правильне повідомлення назад до MainViewController з ViewController1, щоб mainVC міг відхилити VC1, а потім подати VC2.
У VC2 VC1 додайте протокол у файл .h над інтерфейсом @:
@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:
може бути таким же методом, як і ваш VC2Pressed:
метод IBAction кнопки. Зверніть увагу, що його викликають із блоку завершення, щоб переконатись, що VC2 не відображається, поки VC1 не буде повністю відхилено.
Зараз ви переходите з VC1-> VCMain-> VC2, тому ви, мабуть, хочете, щоб анімувався лише один із переходів.
оновлення
У своїх коментарях ви висловлюєте здивування складністю, необхідною для досягнення, здавалося б, простої справи. Запевняю вас, цей шаблон делегування є настільки важливим для більшості об’єктивів-C та какао, і цей приклад стосується найпростішого, що ви можете отримати, і вам справді слід докласти зусиль, щоб з ним почуватись комфортно.
У посібнику програмування View Controller від Apple вони мають таке сказати :
Відхилення представленого контролера перегляду
Коли приходить час звільнити представлений контролер подання, найкращим підходом є надання дозволу представницькому контролеру подання його відхилити. Іншими словами, коли це можливо, той самий контролер перегляду, який представив контролер перегляду, також повинен нести відповідальність за його відхилення. Незважаючи на те, що існує декілька методів повідомлення контролера подання представлення про те, що його представлений контролер подання повинен бути відхилений, кращим методом є делегування. Для отримання додаткової інформації див. “Використання делегування для спілкування з іншими контролерами”.
Якщо ви дійсно продумаєте, чого хочете досягти, і як ви це робите, ви зрозумієте, що обмін повідомленнями MainViewController, щоб виконати всю роботу, є єдиним логічним виходом з огляду на те, що ви не хочете використовувати NavigationController. Якщо ви робите використовувати NavController, насправді ви «делегування», навіть якщо не явно, до NavController , щоб зробити всю роботу. Потрібно мати якийсь об’єкт, який відстежує центральне відстеження того, що відбувається з вашою навігацією по ВК, і вам потрібен якийсь спосіб спілкування з ним, що б ви не робили.
На практиці поради 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 щось більше? Ви не знаєте (як і я) ... з делегатом, у вас немає цієї невизначеності.
- Коли я намагаюся запустити цей метод, він просто зависає без попередження та помилок.
Тож, будь ласка ... знайдіть час, щоб навчитися делегуванню!
оновлення2
У своєму коментарі вам вдалося змусити його працювати, використовуючи це в обробнику кнопок звільнення VC2:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
Це, звичайно, набагато простіше, але це залишає перед вами низку питань.
Герметичне з'єднання
Ви міцно з'єднуєте свою структуру viewController разом. Наприклад, якщо ви повинні були вставити новий viewController перед mainVC, ваша необхідна поведінка порушиться (ви перейдете до попереднього). У VC1 вам також довелося # імпортувати VC2. Тому у вас досить багато взаємозалежностей, що порушує цілі ООП / MVC.
Використовуючи делегатів, ні VC1, ні VC2 не повинні знати нічого про mainVC або його попередні дані, тому ми зберігаємо все в вільному поєднанні та модулі.
Пам'ять
VC1 не зникла, ви все ще тримаєте на ній два вказівники:
- mainVC в
presentedViewController
нерухомість
presentingViewController
Властивість VC2
Ви можете перевірити це, увійшовши в журнал, а також просто виконавши це з VC2
[self dismissViewControllerAnimated:YES completion:nil];
Це все ще працює, все ще повертає вас до VC1.
Мені це здається витоком пам’яті.
Підказка цього полягає у попередженні, яке ви отримуєте тут:
[self presentViewController:vc2 animated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
Логіка руйнується, оскільки ви намагаєтеся відмовитись від представленого ВК, яким VC2 є представленим ВК. Друге повідомлення насправді не виконується - ну, можливо, трапляються деякі речі, але ви все одно залишаєте два вказівники на об’єкт, від якого ви думали, що позбулися. ( редагувати - я перевірив це, і це не так погано, обидва об'єкти зникають, коли ви повертаєтесь до 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, але це найкраще, що ви отримаєте без жодного явного делегування.