В моем представленииDidAppear, как я узнаю, когда он разматывается ребенком?
Когда мой ребенок выполняет сеанс размотки, мой диспетчер viewDidAppear получает вызов.
В этом методе (и только этот метод мне нужно знать, было ли это от размотки или нет)
Примечание. Ребенок разворачивается к самому первому контроллеру представления, поэтому это контроллер представления промежуточного, а не истинный корень.
Ответы
Ответ 1
Вы должны иметь возможность использовать следующее для обнаружения в каждом контроллере, если воздействие на контроллер просмотра было вызвано нажатием/представлением или в результате того, что он был выставлен в результате поп/увольнение/размотка.
Это может быть или может быть достаточно для ваших нужд.
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Handle controller being exposed from push/present or pop/dismiss
if (self.isMovingToParentViewController || self.isBeingPresented){
// Controller is being pushed on or presented.
}
else{
// Controller is being shown as result of pop/dismiss/unwind.
}
}
Если вы хотите знать, что viewDidAppear
был вызван из-за того, что разворачиваемый сеанс отличается от обычного приглашения pop/off, вам нужно добавить код, чтобы обнаружить, что произошло размот. Для этого вы можете сделать следующее:
Для любого промежуточного контроллера, который вы хотите полностью отключить, добавьте свойство формы:
/** BOOL property which when TRUE indicates an unwind occured. */
@property BOOL unwindSeguePerformed;
Затем переопределите метод разматывания segue canPerformUnwindSegueAction:fromViewController:withSender:
следующим образом:
- (BOOL)canPerformUnwindSegueAction:(SEL)action
fromViewController:(UIViewController *)fromViewController
withSender:(id)sender{
// Set the flag indicating an unwind segue was requested and then return
// that we are not interested in performing the unwind action.
self.unwindSeguePerformed = TRUE;
// We are not interested in performing it, so return NO. The system will
// then continue to look backwards through the view controllers for the
// controller that will handle it.
return NO;
}
Теперь у вас есть флаг для обнаружения разматывания и средство для обнаружения разматывания перед тем, как оно произойдет. Затем настройте метод viewDidAppear
, чтобы включить этот флаг.
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Handle controller being exposed from push/present or pop/dismiss
// or an unwind
if (self.isMovingToParentViewController || self.isBeingPresented){
// Controller is being pushed on or presented.
// Initialize the unwind segue tracking flag.
self.unwindSeguePerformed = FALSE;
}
else if (self.unwindSeguePerformed){
// Controller is being shown as a result of an unwind segue
}
else{
// Controller is being shown as result of pop/dismiss.
}
}
Надеюсь, это соответствует вашим требованиям.
В документах по обращению с цепочкой размотки segue см.: https://developer.apple.com/library/ios/technotes/tn2298/_index.html
Ответ 2
Вот простая категория на UIViewController, которую вы можете использовать для отслеживания того, находится ли ваш представленный контроллер представлений в разгар сеанса размотки. Полагаю, это может быть покрашено больше, но я считаю, что это много работает для вашего дела.
Чтобы использовать его, вам необходимо зарегистрировать развязку segue из вашего метода разматывания на контроллере представления назначения:
- (IBAction) prepareForUnwind:(UIStoryboardSegue *)segue
{
[self ts_registerUnwindSegue: segue];
}
Что это. С вашего промежуточного контроллера просмотра вы можете проверить, находитесь ли вы в разгаре segue:
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
BOOL unwinding = [self ts_isUnwinding];
NSLog( @"%@:%@, unwinding: %@", self.title, NSStringFromSelector(_cmd), unwinding ? @"YES" : @"NO" );
}
Нет необходимости ничего чистить; segue саморегистрарируется, когда закончится.
Здесь полная категория:
@interface UIViewController (unwinding)
- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue;
- (BOOL) ts_isUnwinding;
@end
static NSMapTable* g_viewControllerSegues;
@implementation UIViewController (unwinding)
- (void) ts_registerUnwindSegue: (UIStoryboardSegue*) segue
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g_viewControllerSegues = [NSMapTable weakToWeakObjectsMapTable];
});
for ( UIViewController* vc = segue.sourceViewController ; vc != nil ; vc = vc.presentingViewController )
{
[g_viewControllerSegues setObject: segue forKey: vc];
}
}
- (BOOL) ts_isUnwinding
{
return [g_viewControllerSegues objectForKey: [self ts_topMostParentViewController]] != nil;
}
- (UIViewController *)ts_topMostParentViewController {
UIViewController *viewController = self;
while (viewController.parentViewController) {
viewController = viewController.parentViewController;
}
return viewController;
}
@end
Ответ 3
Ваш вопрос был действительно интересен мне, потому что я никогда раньше не использовал IB и segues (не судите меня по этому поводу) и хотел узнать что-то новое. Как вы описали в своих комментариях:
viewDidAppear будет вызываться на B
, когда C
перематывается на A
Итак, я придумал легкое решение для этого:
protocol ViewControllerSingletonDelegate: class {
func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController)
}
class ViewControllerSingleton {
static let sharedInstance = ViewControllerSingleton()
private var delegates: [ViewControllerSingletonDelegate] = []
func addDelegate(delegate: ViewControllerSingletonDelegate) {
if !self.containsDelegate(delegate) {
self.delegates.append(delegate)
}
}
func removeDelegate(delegate: ViewControllerSingletonDelegate) {
/* implement any other function by your self :) */
}
func containsDelegate(delegate: ViewControllerSingletonDelegate) -> Bool {
for aDelegate in self.delegates {
if aDelegate === delegate { return true }
}
return false
}
func forwardToDelegate(closure: (delegate: ViewControllerSingletonDelegate) -> Void) {
for aDelegate in self.delegates { closure(delegate: aDelegate) }
}
}
class SomeViewController: UIViewController, ViewControllerSingletonDelegate {
let viewControllerSingleton = ViewControllerSingleton.sharedInstance
func someFunction() { // some function where you'll set the delegate
self.viewControllerSingleton.addDelegate(self)
}
/* I assume you have something like this in your code */
@IBAction func unwindToSomeOtherController(unwindSegue: UIStoryboardSegue) {
self.viewControllerSingleton.forwardToDelegate { (delegate) -> Void in
delegate.viewControllerWillUnwind(unwindSegue.sourceViewController, toViewController: unwindSegue.destinationViewController)
}
/* do something here */
}
// MARK: - ViewControllerSingletonDelegate
func viewControllerWillUnwind(viewcontroller: UIViewController, toViewController: UIViewController) {
/* do something with the callback */
/* set some flag for example inside your view controller so your viewDidAppear will know what to do */
}
}
Вы также можете изменить функцию обратного вызова, чтобы вернуть что-то еще, например, идентификатор контроллера, а не сам контроллер.
Я делаю все программно, поэтому, пожалуйста, не судите меня за это.;)
Если этот фрагмент кода вам не поможет, мне все равно понравится какая-то обратная связь.
Ответ 4
Предположим, что segue-навигация - ViewController → FirstViewController → SecondViewController. Откажитесь от SecondViewController
до ViewController
. Вы можете добавить в промежуточном FirstViewController
следующий код для обнаружения операций размотки.
import UIKit
class FirstViewController: UIViewController {
var unwindAction:Bool = false
override func viewDidAppear(animated: Bool) {
if unwindAction {
println("Unwind action")
unwindAction = false
}
}
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController? {
self.unwindAction = true
return super.viewControllerForUnwindSegueAction(action, fromViewController: fromViewController, withSender: sender)
}
}
ИЗМЕНИТЬ
Подумав об этом, я решил, что решение этого зависит от той сложности, с которой вы имеете дело. Что именно вы делаете, когда делаете развязку? Решения, приведенные здесь, являются жизнеспособными, и они работают - только если вы хотите определить, является ли это незаменимым действием. Что делать, если вы хотите передать данные между точкой, где происходит размотка с корнем? Что делать, если есть сложный набор препаратов, которые вы хотите сделать в одном из промежуточных контроллеров? Что делать, если вы хотите сделать это?
В таких сложных сценариях я сразу же исключил бы отмену методов разматывания контроллера вида. Выполнение таких операций будет работать, но оно не будет чистым. Метод будет делать то, что он не должен делать. Запах? Этот код запах.
Что делать, если каким-то образом контроллер представления может сообщить о следующем контроллере представления в иерархии события? Еще лучше, как мы это сделаем без плотной связи этих двух?
Протокол.
Определите определение протокола:
protocol UnwindResponding {
prepareForUnwindSegue(segue:UISegue , formViewController:UIViewController, withImportantInfo info:[String,AnyObject])
}
Используя протокол, вы сохраните связь между объектами - иерархией диспетчеров вида в этом случае - явным. В момент возникновения конкретного события вы делегируете вызов следующему контроллеру в иерархии, информируя о событии конкретного события в другом контроллере представления. Вот пример:
override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
if let unwindResponder = self.presentingViewController as? UnwindResponding where segue.identifier = "unwindSegue" {
unwindResponder.prepareForUnwindSegue(segue:UISegue, fromViewController:self,info:info)
}
}
В контроллере промежуточного представления вы можете сделать что-то вроде:
extension IntermediaryViewController : UnwindResponding {
prepareForUnwindSegue(segue:UISegue , fromViewController:UIViewController, withImportantInfo info:[String,AnyObject]) {
if let unwindResponder = self.presentingViewController {
unwindResponder.prepareForUnwindSegue(segue,fromViewController:fromViewController, info:info)
}
unwindSegue = true
}
}
Конечно, вы не захотите этого делать, если хотите просто обнаружить разматывание. Возможно, вы это сделаете, вы никогда не узнаете, что произойдет в будущем. Никогда не бойтесь, чтобы ваш код был чистым.
Ответ 5
Добавить метод в контроллер родительского представления
@IBAction func unwindToParent(unwindSegue: UIStoryboardSegue) {
if let childViewController = unwindSegue.sourceViewController as? ChildViewController {
println("unwinding from child")
}
}
В качестве примера, если разворот segue связан с кнопкой, в канале раскадровки ваша кнопка к нему открывает выход контроллера
![введите описание изображения здесь]()
Он предложит ссылку на метод unwindToParent
![введите описание изображения здесь]()
Затем каждый раз, когда выполняется размотка segue, метод unwindToParent
будет называться
Ответ 6
для всех, кто ищет это,
в swift вы можете использовать функцию переопределения "разматывать", это вызывается, когда ViewController находится на пути размотки segue, что означает перенастройку ViewController.
Пример:
override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
}