Блок завершения для popViewController
При отклонении диспетчера модального представления с помощью dismissViewController
существует возможность предоставить блок завершения. Существует ли аналогичный эквивалент для popViewController
?
Аргумент завершения довольно удобен. Например, я могу использовать его, чтобы не удалять строку из таблицы, пока модальность не будет отключена, позволяя пользователю увидеть анимацию строки. Когда я возвращаюсь с контроллера толкаемого представления, мне нужна такая же возможность.
Я попытался разместить popViewController
в блоке анимации UIView
, где у меня есть доступ к блоку завершения. Однако это вызывает некоторые нежелательные побочные эффекты при просмотре представления.
Если такой метод недоступен, какие-то обходные пути?
Ответы
Ответ 1
Я знаю, что ответ был принят более двух лет назад, однако этот ответ неполный.
Невозможно сделать то, что вам нужно, чтобы быть готовым к работе
Это технически правильно, потому что API UINavigationController
не предлагает никаких вариантов для этого. Однако, используя каркас CoreAnimation, можно добавить блок завершения в базовую анимацию:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// handle completion here
}];
[self.navigationController popViewControllerAnimated:YES];
[CATransaction commit];
Блок завершения будет вызываться, как только анимация, используемая popViewControllerAnimated:
, будет завершена. Эта функциональность доступна с iOS 4.
Ответ 2
Для версии iOS9 SWIFT - работает как шарм (не тестировался для более ранних версий). На основе этого ответа
extension UINavigationController {
func pushViewController(viewController: UIViewController, animated: Bool, completion: () -> ()) {
pushViewController(viewController, animated: animated)
if let coordinator = transitionCoordinator() where animated {
coordinator.animateAlongsideTransition(nil) { _ in
completion()
}
} else {
completion()
}
}
func popViewController(animated: Bool, completion: () -> ()) {
popViewControllerAnimated(animated)
if let coordinator = transitionCoordinator() where animated {
coordinator.animateAlongsideTransition(nil) { _ in
completion()
}
} else {
completion()
}
}
}
Ответ 3
Я сделал версию Swift
с расширениями с ответом @JorisKluivers.
Это вызовет закрытие завершения после того, как анимация будет выполнена как для push
, так и для pop
.
extension UINavigationController {
func popViewControllerWithHandler(completion: ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popViewControllerAnimated(true)
CATransaction.commit()
}
func pushViewController(viewController: UIViewController, completion: ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.pushViewController(viewController, animated: true)
CATransaction.commit()
}
}
Ответ 4
У меня была такая же проблема. И поскольку мне приходилось использовать его несколько раз и в цепочках блоков завершения, я создал это общее решение в подклассе UINavigationController:
- (void) navigationController:(UINavigationController *) navigationController didShowViewController:(UIViewController *) viewController animated:(BOOL) animated {
if (_completion) {
_completion();
_completion = nil;
}
}
- (UIViewController *) popViewControllerAnimated:(BOOL) animated completion:(void (^)()) completion {
_completion = completion;
return [super popViewControllerAnimated:animated];
}
Полагая
@interface NavigationController : UINavigationController <UINavigationControllerDelegate>
и
@implementation NavigationController {
void (^_completion)();
}
и
- (id) initWithRootViewController:(UIViewController *) rootViewController {
self = [super initWithRootViewController:rootViewController];
if (self) {
self.delegate = self;
}
return self;
}
Ответ 5
Невозможно делать то, что вам нужно из коробки. то есть нет метода с блоком завершения для того, чтобы вытащить контроллер вида из навигационного стека.
Я бы поставил логику в viewDidAppear
. Это будет вызываться, когда представление закончится на экране. Он будет вызываться для всех сценариев появления контроллера представления, но это должно быть хорошо.
Или вы можете использовать метод UINavigationControllerDelegate
navigationController:didShowViewController:animated:
, чтобы сделать аналогичную вещь. Это вызывается, когда контроллер навигации закончил нажатие или нажатие контроллера вида.
Ответ 6
Работа с анимацией или без нее должным образом, а также включает popToRootViewController
:
// updated for Swift 3.0
extension UINavigationController {
private func doAfterAnimatingTransition(animated: Bool, completion: @escaping (() -> Void)) {
if let coordinator = transitionCoordinator, animated {
coordinator.animate(alongsideTransition: nil, completion: { _ in
completion()
})
} else {
DispatchQueue.main.async {
completion()
}
}
}
func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping (() -> Void)) {
pushViewController(viewController, animated: animated)
doAfterAnimatingTransition(animated: animated, completion: completion)
}
func popViewController(animated: Bool, completion: @escaping (() -> Void)) {
popViewController(animated: animated)
doAfterAnimatingTransition(animated: animated, completion: completion)
}
func popToRootViewController(animated: Bool, completion: @escaping (() -> Void)) {
popToRootViewController(animated: animated)
doAfterAnimatingTransition(animated: animated, completion: completion)
}
}
Ответ 7
Swift 3 ответ, благодаря этому ответу: fooobar.com/questions/76380/...
//MARK:UINavigationController Extension
extension UINavigationController {
//Same function as "popViewController", but allow us to know when this function ends
func popViewControllerWithHandler(completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popViewController(animated: true)
CATransaction.commit()
}
func pushViewController(viewController: UIViewController, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.pushViewController(viewController, animated: true)
CATransaction.commit()
}
}
Ответ 8
Блок завершения вызывается после вызова метода viewDidDisappear на представленном контроллере представления. Таким образом, ввод кода в методе viewDidDisappear контроллера popped view должен работать так же, как и блок завершения.
Ответ 9
Я добился именно этого с точностью, используя блок. Я хотел, чтобы мой получатель результатов отображал строку, которая была добавлена модальным представлением, только после того, как она полностью покинула экран, чтобы пользователь мог увидеть изменение. В процессе подготовки к segue, который отвечает за отображение контроллера модального представления, я устанавливаю блок, который я хочу выполнить, когда модаль исчезает. И в модульном контроллере просмотра я переопределяю viewDidDissapear, а затем вызываю блок. Я просто начинаю обновления, когда модаль будет появляться и заканчивать обновления, когда он исчезает, но это потому, что я использую NSFetchedResultsController, но вы можете делать все, что захотите внутри блока.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"addPassword"]){
UINavigationController* nav = (UINavigationController*)segue.destinationViewController;
AddPasswordViewController* v = (AddPasswordViewController*)nav.topViewController;
...
// makes row appear after modal is away.
[self.tableView beginUpdates];
[v setViewDidDissapear:^(BOOL animated) {
[self.tableView endUpdates];
}];
}
}
@interface AddPasswordViewController : UITableViewController<UITextFieldDelegate>
...
@property (nonatomic, copy, nullable) void (^viewDidDissapear)(BOOL animated);
@end
@implementation AddPasswordViewController{
...
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
if(self.viewDidDissapear){
self.viewDidDissapear(animated);
}
}
@end
Ответ 10
Существует подкатегория UINavigationControllerWithCompletionBlock, которая добавляет поддержку блока завершения, когда и толкает, и выскакивает на UINavigationController.
Ответ 11
Только для полноты я создал класс Objective-C, готовый к использованию:
// UINavigationController+CompletionBlock.h
#import <UIKit/UIKit.h>
@interface UINavigationController (CompletionBlock)
- (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion;
@end
// UINavigationController+CompletionBlock.m
#import "UINavigationController+CompletionBlock.h"
@implementation UINavigationController (CompletionBlock)
- (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion {
[CATransaction begin];
[CATransaction setCompletionBlock:^{
completion();
}];
UIViewController *vc = [self popViewControllerAnimated:animated];
[CATransaction commit];
return vc;
}
@end