it-swarm.com.ru

Блок завершения для popViewController

При отклонении модального контроллера представления, использующего dismissViewController, есть возможность предоставить блок завершения. Есть ли аналогичный эквивалент для popViewController?

Аргумент завершения довольно удобен. Например, я могу использовать его для удержания удаления строки из табличного представления, пока модальное изображение не будет выключено, что позволит пользователю увидеть анимацию строки. При возврате из контроллера с задним просмотром, я хотел бы получить такую ​​же возможность.

Я попытался поместить popViewController в блок анимации UIView, где у меня есть доступ к блоку завершения. Тем не менее, это создает некоторые нежелательные побочные эффекты в представлении.

Если такого метода нет, каковы обходные пути?

89
Ben Packard

Я знаю, что ответ был принят более двух лет назад, однако этот ответ неполон.

Там нет способа сделать то, что вы хотите из коробки

Это технически правильно, потому что API UINavigationController не предлагает никаких опций для этого. Однако с помощью инфраструктуры CoreAnimation можно добавить блок завершения к базовой анимации:

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    // handle completion here
}];

[self.navigationController popViewControllerAnimated:YES];

[CATransaction commit];

Блок завершения будет вызван, как только закончится анимация, используемая popViewControllerAnimated:. Эта функциональность была доступна с iOS 4.

169
Joris Kluivers

Для 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()
        }
    }
}
41
HotJard

Я сделал Swift версию с расширениями с @JorisKluivers answer.

Это вызовет завершение завершения после завершения анимации для 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()
    }
}
27
Arbitur

Я была такая же проблема. И поскольку мне приходилось использовать его несколько раз и в цепочках блоков завершения, я создал это общее решение в подклассе 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;
}
16
Jos Jong

Там нет никакого способа сделать то, что вы хотите из коробки. то есть не существует метода с блоком завершения для извлечения контроллера представления из стека навигации.

Что бы я сделал, это поместил логику в viewDidAppear. Это будет вызвано, когда представление закончится, появляющееся на экране. Это будет вызвано для всех различных сценариев появления контроллера представления, но это должно быть хорошо.

Или вы можете использовать метод UINavigationControllerDelegatenavigationController:didShowViewController:animated:, чтобы сделать то же самое. Это вызвано, когда контроллер навигации закончил выдвигать или выдвигать контроллер представления.

15
mattjgalloway

Работа с анимацией или без нее, а также включает 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)
  }
}
9
rshev

На основании ответа @ HotJard, когда вам нужно всего лишь несколько строк кода. Быстро и просто.

Swift 4 :

_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
    doWantIWantAfterContollerHasPopped()
}
6
Vitalii

Ответ Swift 3, благодаря этому ответу: https://stackoverflow.com/a/28232570/3412567

    //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()
    }
}
5
Benobab

Блок завершения вызывается после вызова метода viewDidDisappear в представленном контроллере представления, так что помещение кода в метод viewDidDisappear выталкиваемого контроллера представления должно работать так же, как блок завершения.

4
rdelmar

Для 2018 года ...

если у вас есть это ...

    navigationController?.popViewController(animated: false)
    NeXTSTEP()

и вы хотите добавить завершение ...

    CATransaction.begin()
    navigationController?.popViewController(animated: true)
    CATransaction.setCompletionBlock({ [weak self] in
       self?.NeXTSTEP() })
    CATransaction.commit()

это так просто.

3
Fattie

Swift 4.1

extension UINavigationController {
func pushToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.pushViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popViewController(animated: true)
    CATransaction.commit()
}

func popToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popToRootViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToRootViewController(animated: animated)
    CATransaction.commit()
}
}
3
iOS_Developer

Существует модуль под названием UINavigationControllerWithCompletionBlock , который добавляет поддержку блока завершения при нажатии и извлечении UINavigationController.

2
duncanc4

Я добился именно этого с точностью, используя блок. Я хотел, чтобы мой извлеченный контроллер результатов показывал строку, которая была добавлена ​​модальным представлением, только после того, как он полностью покинул экран, чтобы пользователь мог видеть происходящее изменение. В подготовке к переходу, который отвечает за отображение контроллера модального вида, я установил блок, который хочу выполнить, когда модальный режим исчезнет. И в контроллере модального представления я переопределяю 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
1
malhal

Используйте следующее расширение в вашем коде: (Swift 4)

import UIKit

extension UINavigationController {

    func popViewController(animated: Bool = true, completion: @escaping () -> Void) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        popViewController(animated: animated)
        CATransaction.commit()
    }

    func pushViewController(_ viewController: UIViewController, animated: Bool = true, completion: @escaping () -> Void) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        pushViewController(viewController, animated: animated)
        CATransaction.commit()
    }
}
1
Rigoberto Sáenz Imbacuán

Для полноты картины я подготовил категорию 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
1
Diego Freniche

Версия Swift 4 с необязательным параметром viewController для перехода к конкретному.

extension UINavigationController {
    func pushViewController(viewController: UIViewController, animated: 
        Bool, completion: @escaping () -> ()) {

        pushViewController(viewController, animated: animated)

        if let coordinator = transitionCoordinator, animated {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
}

func popViewController(viewController: UIViewController? = nil, 
    animated: Bool, completion: @escaping () -> ()) {
        if let viewController = viewController {
            popToViewController(viewController, animated: animated)
        } else {
            popViewController(animated: animated)
        }

        if let coordinator = transitionCoordinator, animated {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}
1
TejAces

Исправлена ​​версия Swift 4, основанная на этот ответ .

extension UINavigationController {
    func pushViewController(_ viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) {
        self.pushViewController(viewController, animated: animated)
        self.callCompletion(animated: animated, completion: completion)
    }

    func popViewController(animated: Bool, completion: @escaping () -> Void) -> UIViewController? {
        let viewController = self.popViewController(animated: animated)
        self.callCompletion(animated: animated, completion: completion)
        return viewController
    }

    private func callCompletion(animated: Bool, completion: @escaping () -> Void) {
        if animated, let coordinator = self.transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}
0
d4Rk