Cómo identificar el controlador de vista anterior en la pila de navegación

78

Tengo 2 separados navigationcontrollers, uno con RootViewControllerA y el otro con RootViewControllerB.

Puedo presionar ViewControllerC en la pila de navegación de A o B.

Pregunta: Cuando estoy en ViewControllerC, ¿cómo puedo saber si estoy en la pila que pertenece a A o B?

Zhen
fuente
7
¿Por qué no simplemente crear una propiedad previousViewController en C que configura cuando presiona C? ¿O le da a C la información que necesita de A o B cuando lo presiona?
Terry Wilcox
1
@TerryWilcox, porque las relaciones podrían ser muy complicadas hoy en día
Vyacheslav

Respuestas:

107

Se podría utilizar el UINavigationController's viewControllersde propiedad:

@property(nonatomic, copy) NSArray *viewControllers

Discusión: El controlador de vista raíz está en el índice 0 en la matriz, el controlador de vista posterior está en el índice n-2 y el controlador superior está en el índice n-1, donde n es el número de elementos en la matriz.

https://developer.apple.com/documentation/uikit/uinavigationcontroller

Puede usar eso para probar si el controlador de vista raíz (el que está en el índice de matriz 0) es el controlador de vista A o B.

jburns20
fuente
¿Cómo puedo obtener esto como una cadena en SWIFT? (Soy nuevo en
Swift
@cyberboy tan fácil: convar viewControllers: [UIViewController]
NerdyTherapist
1
El controlador de vista anterior estará enviewControllers.last
Burak
@Burak viewControllers.lasttiene un controlador de vista actual, para obtener el controlador de vista anterior, se debe usarviewControllers[viewControllers.count-2]
Ganpat
102

Aquí está la implementación de la respuesta aceptada:

- (UIViewController *)backViewController
{
    NSInteger numberOfViewControllers = self.navigationController.viewControllers.count;

    if (numberOfViewControllers < 2)
        return nil;
    else
        return [self.navigationController.viewControllers objectAtIndex:numberOfViewControllers - 2];
}
Jorge
fuente
20
Recomiendo agregar esto como una categoría enUIViewController
MaxGabriel
28
- (UIViewController *)backViewController
{
    NSInteger myIndex = [self.navigationController.viewControllers indexOfObject:self];

    if ( myIndex != 0 && myIndex != NSNotFound ) {
        return [self.navigationController.viewControllers objectAtIndex:myIndex-1];
    } else {
        return nil;
    }
}
Chris
fuente
11

Una implementación más general de la respuesta aceptada:

- (UIViewController *)backViewController {
    NSArray * stack = self.navigationController.viewControllers;

    for (int i=stack.count-1; i > 0; --i)
        if (stack[i] == self)
            return stack[i-1];

    return nil;
}

Esto devolverá el "controlador de vista posterior" correcto independientemente de dónde esté la clase actual en la pila de navegación.

tjklemz
fuente
9

Implementación rápida del código tjklemz como extensión:

extension UIViewController {

    func backViewController() -> UIViewController? {        
        if let stack = self.navigationController?.viewControllers {
            for(var i=stack.count-1;i>0;--i) {
                if(stack[i] == self) {
                    return stack[i-1]
                }
            }
        }
        return nil
    }
}
burrito de hierba
fuente
He publicado una versión modificada de este código que tiene compatibilidad con Swift 3 a continuación como una nueva respuesta.
Cin316
9

Aquí hay una versión modificada del código Swift de Prabhu Beeman que lo adapta para admitir Swift 3:

extension UIViewController {
    func backViewController() -> UIViewController? {
        if let stack = self.navigationController?.viewControllers {
            for i in (1..<stack.count).reverse() {
                if(stack[i] == self) {
                    return stack[i-1]
                }
            }
        }
        return nil
    }
}
Cin316
fuente
un par de notas: reverse () ahora está invertido () y self.navigationController? .viewControllers nunca contiene el controlador de vista "self", por lo que return stack.last debería ser suficiente
Kappe
3

Acceda al n-2elemento de la viewControllerspropiedad para acceder al controlador de vista principal.

Una vez que tenga esa instancia, puede verificar su tipo registrando lo que sale de la NSStringFromClass()función. O puede mantener alguna static constcadena de identificación en los controladores A y B, y una función getter que imprima la cadena.

Alex Reynolds
fuente
cualquier forma simple de obtener el índice actual de un controlador de vista o los codificaré dependiendo de mi estructura jerárquica.
HpTerm
3

Como UINavigationControllerextensión:

extension UINavigationController {

    func previousViewController() -> UIViewController? {
        guard viewControllers.count > 1 else {
            return nil
        }
        return viewControllers[viewControllers.count - 2]
    }

}
Adam Johns
fuente
2

Implementación rápida del código @tjklemz:

var backViewController : UIViewController? {

        var stack = self.navigationController!.viewControllers as Array

        for (var i = stack.count-1 ; i > 0; --i) {
            if (stack[i] as UIViewController == self) {
                return stack[i-1] as? UIViewController
            }

        }
        return nil
    }
cdf1982
fuente
¿Cómo puedo obtener esto como una cadena? (Soy nuevo en
Swift
@cyberboy ¡Hola! No estoy seguro de entender tu pregunta. ¿Desea obtener la descripción de backViewController como una cadena? Si es así, podría considerarlo println("backViewController is: \(backViewController.description)"); esto le dará una descripción como "UINavigationController: 0x7fcea1d286b0", que no es muy clara pero al menos le permite identificar el objeto. Como dije, no estoy seguro de lo que necesita exactamente ...
cdf1982
1

Utilice el navigationControllermétodo para recuperarlo. Consulte la documentación en el sitio de Apple .

navigationController Un padre o un antepasado que es un controlador de navegación. (solo lectura)

@property (no atómico, solo lectura, retener) UINavigationController * navigationController

Discusión Solo devuelve un controlador de navegación si el controlador de vista está en su pila. Esta propiedad es nula si no se puede encontrar un controlador de navegación.

Disponibilidad Disponible en iOS 2.0 y posterior.

Percepción
fuente
1

Porque a los caballos muertos les gusta ser golpeados :)

- (UIViewController *)previousViewController
{
    NSArray *vcStack = self.navigationController.viewControllers;
    NSInteger selfIdx = [vcStack indexOfObject:self];
    if (vcStack.count < 2 || selfIdx == NSNotFound) { return nil; }
    return (UIViewController *)[vcStack objectAtIndex:selfIdx - 1];
}
Andrés
fuente
1

Una impementación Swift más sucinta:

extension UIViewController {
    var previousViewController: UIViewController? {
        guard let nav = self.navigationController,
              let myIdx = nav.viewControllers.index(of: self) else {
            return nil
        }
        return myIdx == 0 ? nil : nav.viewControllers[myIdx-1]
    }
}
John Scalo
fuente
0

Encontrar el controlador de vista anterior es simple.

En tu caso, es decir, estás en C y necesitas B, [self.navigationController.viewControllers lastObject]es lo que quieres.

Para A, dado que A es el controlador de vista raíz, puede simplemente reemplazar lastObjectcon firstObjectpara obtener.

chih-chun chan
fuente
0

Implementación para Swift 2.2: agregue esto en una UIViewControllerextensión. Seguro en el sentido de que volverá nilsi el viewcontroller es rootvc o no en un navigationcontroller.

var previousViewController: UIViewController? {
  guard let viewControllers = navigationController?.viewControllers else {
    return nil
  }
  var previous: UIViewController?
  for vc in viewControllers{
    if vc == self {
      break
    }
    previous = vc
  }
  return previous
}
Blake Lockley
fuente
0

Con Swift usando guardia.

extension UIViewController {

    func getPreviousViewController() -> UIViewController? {
        guard let _ = self.navigationController else {
            return nil
        }

        guard let viewControllers = self.navigationController?.viewControllers else {
            return nil
        }

        guard viewControllers.count >= 2 else {
            return nil
        }        
        return viewControllers[viewControllers.count - 2]
    }
}
baquiax
fuente
0

Mi extensión, Swift 3

extension UIViewController {
    var previousViewController: UIViewController? {
        guard let controllers = navigationController?.viewControllers, controllers.count > 1 else { return nil }
        switch controllers.count {
        case 2: return controllers.first
        default: return controllers.dropLast(2).first
        }
    }
}
Dmytro Nasyrov
fuente
0

para swift 3 puedes hacer esto:

var backtoViewController:UIViewController!
for viewController in (self.navigationController?.viewControllers)!.reversed() {
    if viewController is NameOfMyDestinationViewController {
            backtoViewController = viewController
    }
}
self.navigationController!.popToViewController(backtoViewController, animated: true)

Solo necesita reemplazar "NameOfMyDestinationViewController" por viewController que desea devolver.

Gandhi Mena
fuente