Выполнить push-переход после раскрутки

Я работаю над приложением камеры, в котором виды камеры отображаются модально. После того, как я закончу обрезку. Я выполняю переход к MainPageViewController. (Пожалуйста, посмотрите на скриншот)

раскадровка

Моя функция раскрутки внутри MainPageViewController выглядит следующим образом;

@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
    self.performSegueWithIdentifier("Categories", sender: self)
}

где категории — это идентификатор перехода от MainPageViewController к CategoriesTableViewController.

Программа входит в функцию unwindToMainMenu, но не выполняет push-переход. Есть идеи, как это исправить?

Примечание. Я нашел тот же вопрос, но в ответе предлагается изменить структуру раскадровки.


person Berkan Ercan    schedule 15.12.2014    source источник
comment
правильно ли это раскручивание до MainPageViewController ?   -  person Anil Varghese    schedule 15.12.2014
comment
Я могу выполнить раскрутку правильно, но я хочу выполнить push-переход сразу после раскрутки до MainPageViewController   -  person Berkan Ercan    schedule 15.12.2014
comment
Вы можете поймать конец перехода. См. этот вопрос: stackoverflow.com/questions/18310396/   -  person Jean-Baptiste Yunès    schedule 15.12.2014


Ответы (7)


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

Примечание: это работает только с iOS 9+, так как только пользовательские переходы поддерживают имена классов до iOS9, и вы не можете объявить выходной переход как пользовательский переход в раскадровках.

1. Подкласс UIStoryboardSegue с UIStoryboardSegueWithCompletion

class UIStoryboardSegueWithCompletion: UIStoryboardSegue {
    var completion: (() -> Void)?

    override func perform() {
        super.perform()
        if let completion = completion {
            completion()
        }
    }
}

2. Установите UIStoryBoardSegueWithCompletion в качестве класса для перехода к выходу.

примечание: действие для этого перехода должно быть unwindToMainMenu, чтобы соответствовать исходному вопросу

«Выберите Добавить пользовательский класс

3. Обновите раскрутку @IBAction, чтобы выполнить код в обработчике завершения.

@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
    if let segue = segue as? UIStoryboardSegueWithCompletion {
        segue.completion = { 
            self.performSegueWithIdentifier("Categories", sender: self) 
        }
    }
}

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

person wyu    schedule 02.06.2016
comment
Я мог заставить это работать надежно, только заключив завершение в координатор перехода: destinationViewController.transitionCoordinator()?.animateAlongsideTransition(nil, completion: { _ in completion() }) - person Bringo; 29.06.2016
comment
@Bringo, это интересно ... Если вы устанавливаете координатора перехода, то, возможно, вы можете просто напрямую установить блок завершения координатора перехода из действия раскрутки, вместо того, чтобы проходить пользовательский переход. - person wyu; 05.07.2016
comment
хорошо, вы не можете, поскольку во время вызова перехода на раскрутку нет активного перехода, поэтому координатор перехода равен нулю (т.е. переход происходит после возврата вызова перехода на раскрутку) - person wyu; 05.07.2016
comment
Это вызывает утечку памяти в моем приложении! :( - person Thomás Pereira; 31.05.2019
comment
вы, вероятно, хотите добавить слабое «я» в завершение перехода. Дайте мне знать, устранит ли это вашу утечку памяти, и я смогу обновить ответ @TomCalmon - person wyu; 06.06.2019
comment
Нет, это не решило проблему. В итоге я установил несколько флагов и выполнил код в viewDidDisappear(animated:). - person Thomás Pereira; 06.06.2019

Я хочу предоставить свое собственное решение этой проблемы на данный момент. Любые дополнительные ответы всегда приветствуются.

Я поместил логическую переменную и функцию viewDidAppear в MainPageViewController.

var fromCamera = false

override func viewDidAppear(animated: Bool) {
    if fromCamera {
        self.performSegueWithIdentifier("categorySelection", sender: self)
        self.fromCamera = false
    }
}

Я установил fromCamera в true, прежде чем выполнить раскрутку перехода от CropViewController. Таким образом, я выполняю переход к экрану категории только в том случае, если выполняется переход от кадрированного вида.

person Berkan Ercan    schedule 16.12.2014
comment
Более надежное решение. Но, к сожалению, мне приходится прибегать к хакерским решениям при раскручивании из модально представленного представления, поскольку viewDidAppear в этом случае не вызывается. - person mohonish; 30.03.2016

Продвигая этот ответ (у меня был только код Objective-C)

Подкласс UIStoryBoardSegue

#import <UIKit/UIKit.h>

@interface MyStoryboardSegue : UIStoryboardSegue

/**
 This block is called after completion of animations scheduled by @p self.
 */
@property (nonatomic, copy) void(^completion)();

@end

И вызовите этот блок завершения после завершения анимации.

@implementation MyStoryboardSegue

- (void)perform {
  [super perform];
  if (self.completion != nil) {
    [self.destinationViewController.transitionCoordinator
     animateAlongsideTransition:nil
     completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
       if (![context isCancelled]) {
         self.completion();
       }
     }];
  }
}

@end
person Ayush Goel    schedule 05.07.2016

Продолжая предыдущие два ответа, здесь есть немного больше подробностей о целевой версии c (у меня тоже был только код Objective-C)

  1. Подкласс UIStoryboardSegue с UIStoryboardSegueWithCompletion

    class UIStoryboardSegueWithCompletion: UIStoryboardSegue { завершение var: (() -> Void)?

    override func perform() {
        super.perform()
        if let completion = completion {
            completion()
        }
    }
    

    }

UIStoryboardSegueWithCompletion.h

#import <UIKit/UIKit.h>

@interface MyStoryboardSegue : UIStoryboardSegueWithCompletion

@property (nonatomic, copy) void(^completion)();

@end

UIStoryboardSegueWithCompletion.m

#import "UIStoryboardSegueWithCompletion.h"

@implementation UIStoryboardSegueWithCompletion

- (void)perform {
  [super perform];
  if (self.completion != nil) {
    [self.destinationViewController.transitionCoordinator
     animateAlongsideTransition:nil
     completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
       if (![context isCancelled]) {
         self.completion();
       }
     }];
  }
}
@end
  1. Установите UIStoryBoardSegueWithCompletion в качестве класса для перехода к выходу.

примечание: действие для этого перехода должно быть unwindToMainMenu, чтобы соответствовать исходному вопросу [изображение, показывающее интерфейс перехода] [1] [изображение, показывающее интерфейс перехода 2] [2]

Выберите выход из раскадровки Добавить собственный класс

-(IBAction)unwindToMainMenu(UIStoryboardSegue *)segue {
    if([segue isKindOfClass:[UIStoryboardSegueWithCompletion class]]){
        UIStoryboardSegueWithCompletion *segtemp = segue;// local prevents warning
        segtemp.completion = ^{
            NSLog(@"segue completion");
            [self performSegueWithIdentifier:@"Categories" sender:self];
        };
    }
}

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

person moride    schedule 24.03.2017

Я предполагаю, что PerformSegue не срабатывает, потому что раскрутка еще не завершена. Единственное, что я могу придумать на данный момент, это отложить вызов PerformSegue с помощью dispatch_after. Хотя мне это кажется очень "хакерским".

@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
    dispatch_after(1, dispatch_get_main_queue()) { () -> Void in
        self.performSegueWithIdentifier("Categories", sender: self)
    }
}
person Ron Fessler    schedule 15.12.2014
comment
Я также нахожу способ, который также может быть хакерским; Я устанавливаю логическую переменную в значение true внутри функции раскрутки и внутри функции viewDidAppear, я вызываю функцию PerformForSegue, если это логическое значение истинно. Что вы думаете об этом решении? - person Berkan Ercan; 15.12.2014
comment
Думаю, это хорошее решение. dispatch_after из-за переменного времени ожидания могут возникнуть проблемы. Использование логического значения и viewDidAppear гарантирует, что perfromSegue будет выполнен, когда представление будет готово. - person Ron Fessler; 15.12.2014

Метод выхода из перехода IBAction происходит до того, как завершится фактическая раскрутка перехода. У меня была такая же проблема, и я решил ее таким образом (если вы не возражаете против моего перефразирования вашего кода). Это позволяет избежать дополнительного времени и анимации, полагаясь на ViewDidAppear.

@IBAction func unwindToMainMenu(segue: UIStoryboardSegue) {
   let categoriesTable = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("CategoryTableViewController")
   self.navigationController?.viewControllers.append(categoriesTable)
   self.navigationController?.showViewController(categoriesTable, sender: self)
}

Надеюсь, это будет полезно для всех, кто сталкивается с этим и просто хочет мгновенного перехода!

person Cole    schedule 24.09.2015

Обновлен ответ @moride для Swift 5. Координатор перехода теперь является необязательным, поэтому в этом случае мы немедленно запускаем завершение.

class UIStoryboardSegueWithCompletion: UIStoryboardSegue {
    var completion: (() -> Void)?

    override func perform() {
        super.perform()

        guard let completion = completion else { return }
        guard let coordinator = destination.transitionCoordinator else {
            completion()
            return
        }

        coordinator.animate(alongsideTransition: nil) { context in
            guard !context.isCancelled else { return }
            completion()
        }
    }
}
person John Rogers    schedule 07.04.2020