IOS, как программно определить, когда вызывается контроллер верхнего уровня?
Предположим, что у меня есть стек контроллера контроллера с двумя контроллерами представления: VC2 находится сверху, а VC1 находится внизу. Есть ли код, который я могу включить в VC1, который обнаружит, что VC2 только что выскочил из стека?
Поскольку я пытаюсь обнаружить всплытие VC2 из кода для VC1, кажется, что что-то вроде viewWillAppear или viewDidAppear не будет работать, потому что эти методы каждый раз, когда отображается VC1, в том числе, когда он сначала помещается в стек.
РЕДАКТИРОВАТЬ: мне кажется, что я не совсем понял свой первоначальный вопрос. Здесь то, что я пытаюсь сделать: определить, когда отображается VC1 из-за того, что VC2 выскользнул из верхней части стека. Здесь то, что я НЕ пытаюсь сделать: определить, когда VC1 отображается из-за того, что его вставляют в верхнюю часть стека. Мне нужен способ, который обнаружит первое действие, но НЕ второе действие.
Примечание. Я не особо забочусь о VC2, это может быть любое количество других VC, которые выходят из стека, что меня волнует, когда VC1 снова становится вершиной стека из-за того, что какой-то другой VC начинается выскочил сверху.
Ответы
Ответ 1
В iOS 5 были введены два новых метода обработки именно такого типа ситуаций. Вы ищете -[UIViewController isMovingToParentViewController]
. Из docs:
isMovingToParentViewController
Возвращает значение Boolean, которое указывает что контроллер представления находится в процессе добавления родителя.
- (BOOL)isMovingToParentViewController
Возвращаемое значение
ДА, если контроллер вида появляется, потому что он был добавлен как дочерний элемент контейнера, иначе NO.
Обсуждение
Этот метод возвращает YES только при вызове изнутри следующие методы:
-viewWillAppear:
-viewDidAppear:
В вашем случае вы можете реализовать -viewWillAppear:
следующим образом:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.isMovingToParentViewController == NO)
{
// we're already on the navigation stack
// another controller must have been popped off
}
}
РЕДАКТИРОВАТЬ: Там тонкая смысловая разница, чтобы рассмотреть здесь - вас интересует тот факт, что VC2, в частности, выскочил из стека, или вы хотите получать уведомления каждый раз, когда VC1 отображается как результат какого-либо контроллера? В первом случае делегирование является лучшим решением. Прямая слабая ссылка на VC1 также может работать, если вы никогда не намереваетесь повторно использовать VC2.
EDIT 2: Я сделал пример более явным, инвертируя логику и не возвращаясь раньше.
Ответ 2
isMovingTo/FromParentViewController не будет работать для нажатия и ввода в стек навигационного контроллера.
Здесь надежный способ сделать это (без использования делегата), но это, вероятно, только iOS 7+.
UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey];
if ([[self.navigationController viewControllers] containsObject:fromViewController])
{
//we're being pushed onto the nav controller stack. Make sure to fetch data.
} else {
//Something is being popped and we are being revealed
}
В моем случае использование делегата означало бы, что поведение диспетчера представлений более тесно связано с делегатом, которому принадлежит стек nav, и я хотел бы получить более автономное решение. Это работает.
Ответ 3
Один из способов, по которым вы могли бы подойти, - объявить делегатский протокол для VC2 примерно так:
в VC1.h
@interface VC1 : UIViewController <VC2Delegate> {
...
}
в VC1.m
-(void)showVC2 {
VC2 *vc2 = [[VC2 alloc] init];
vc2.delegate = self;
[self.navigationController pushViewController:vc2 animated:YES];
}
-(void)VC2DidPop {
// Do whatever in response to VC2 being popped off the nav controller
}
в VC2.h
@protocol VC2Delegate <NSObject>
-(void)VC2DidPop;
@end
@interface VC2 : UIViewController {
id<VC2Delegate> delegate;
}
@property (nonatomic, assign) id delegate;
...
@end
VC2.m
-(void)viewDidUnload {
[super viewDidUnload];
[self.delegate VC2DidPop];
}
Здесь есть хорошая статья об основах протоколов и делегатов здесь.
Ответ 4
Вы также можете обнаружить в контроллере представления, который вызывается
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]) {
....
}
}
Ответ 5
Это сработало для меня
UIViewController *fromViewController = [[[self navigationController] transitionCoordinator] viewControllerForKey:UITransitionContextFromViewControllerKey];
if (![[self.navigationController viewControllers] containsObject:fromViewController] && !self.presentedViewController)
{
//Something is being popped and we are being revealed
}
Ответ 6
Что вы конкретно пытаетесь сделать?
Если вы пытаетесь обнаружить, что VC1 будет показан, этот ответ должен вам помочь. Используйте UINavigationControllerDelegate.
Если вы пытаетесь обнаружить, что VC2 будет скрыт, я просто использую viewWillDisappear:
VC2.
Ответ 7
Вы можете добавить наблюдателя для NSNotification специально JUST для вашего VC2.
// pasing the "VC2" here will tell the notification to only listen for notification from
// VC2 rather than every single other objects
[[NSNotitificationCenter defaultCenter] addObserver:self selector:@selector(doSomething:) object:VC2];
Теперь в вашем представлении VC2 исчезнет, вы можете отправить уведомление:
-(void)viewWillDisappear
{
[[NSNotificationCenter defaultCenter] postNotificationNamed:@"notif_dismissingVC2" object:nil];
}
Ответ 8
Да, в VC1 вы можете проверить, всплывает ли VC2. В UINavigationController существует один метод viewControllers, который возвращает массив нажатых контроллеров, которые находятся в стеке (т.е. Которые были нажаты).
Итак, вы повторяете цикл путем сравнения класса. Если VC2, будет иметь совпадение, в противном случае нет.
Ответ 9
swift 3
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.isMovingToParentViewController {
print("View is moving to ParentViewControll")
}
}
Ответ 10
У меня такая же ситуация, но с небольшим конкретным вариантом использования. В моем случае
мы хотели определить, появляется ли VC1/отображается, когда пользователь нажимает кнопку VC2 назад, где VC2 нажимается на navigationController через VC1.
Итак, я использовал help ответ snarshad для настройки по моей потребности. Вот код в VC1 viewDidAppear
в swift 3.
// VC1: ParentViewController
// VC2: ChildViewController
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let transitionCoordinator = navigationController?.transitionCoordinator,
let fromVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionCoordinator.viewController(forKey: UITransitionContextViewControllerKey.to),
fromVC is ChildViewController,
toVC is ParentViewController {
print("Back button pressed on ChildViewController, and as a result ParentViewController appeared")
}
}