ios - raya - uso del guion corto
¿Cómo obtener viewController visible desde el delegado de la aplicación cuando se utiliza el guión gráfico? (13)
Tengo algunos viewControllers
y no uso NavigationController
. ¿Cómo puedo obtener el controlador de vista visible en los métodos de delegación de aplicaciones (por ejemplo, applicationWillResignActive
)?
Sé cómo hacerlo desde NSNotification
, pero creo que es el camino equivocado.
Aquí hay un enfoque recursivo, orientado al protocolo en Swift. Puede extenderse a tipos personalizados, pero cualquier tipo de subclase UIViewController debería funcionar con el siguiente código.
public protocol ViewControllerContainer {
var topMostViewController: UIViewController? { get }
}
extension UIViewController: ViewControllerContainer {
public var topMostViewController: UIViewController? {
if let presentedView = presentedViewController {
return recurseViewController(presentedView)
}
return childViewControllers.last.map(recurseViewController)
}
}
extension UITabBarController {
public override var topMostViewController: UIViewController? {
return selectedViewController.map(recurseViewController)
}
}
extension UINavigationController {
public override var topMostViewController: UIViewController? {
return viewControllers.last.map(recurseViewController)
}
}
extension UIWindow: ViewControllerContainer {
public var topMostViewController: UIViewController? {
return rootViewController.map(recurseViewController)
}
}
func recurseViewController(viewController: UIViewController) -> UIViewController {
return viewController.topMostViewController.map(recurseViewController) ?? viewController
}
Aquí hay una implementación Swift 2.3 de la respuesta de @ ProgrammierTier como una extensión a un UIViewController
extension UIViewController {
var visibleViewController: UIViewController? {
if presentedViewController == nil {
return self
}
if let presented = presentedViewController {
if presented.isKindOfClass(UINavigationController) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last
}
if presented.isKindOfClass(UITabBarController) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController
}
return presented.visibleViewController
}
return nil
}
}
Para obtenerlo desde applicationWillResignActive
func applicationWillResignActive(application: UIApplication) {
let visibleVC = application.keyWindow?.rootViewController?.visibleViewController
}
Aquí hay una respuesta en Swift 4 que es muy similar a la respuesta aceptada pero tiene algunas mejoras:
- Iterativa en lugar de recursiva.
- Va todo el camino hace la pila de navegación.
- Más sintaxis "swifty".
- Variable estática que se puede colocar en cualquier lugar (no solo en AppDelegate).
No se bloquea en casos impares, es decir, cuando un controlador de la barra de pestañas no tiene un controlador de vista seleccionado.
static var visibleViewController: UIViewController? { var currentVc = UIApplication.shared.keyWindow?.rootViewController while let presentedVc = currentVc?.presentedViewController { if let navVc = (presentedVc as? UINavigationController)?.viewControllers.last { currentVc = navVc } else if let tabVc = (presentedVc as? UITabBarController)?.selectedViewController { currentVc = tabVc } else { currentVc = presentedVc } } return currentVc }
Aquí hay una solución rápida inspirada en la respuesta de @krcjr89. La respuesta aceptada no baja por completo en la navegación . Por ejemplo, si tiene un controlador de navegación integrado en el controlador de la barra de pestañas, no podrá acceder al controlador de vista visible sino al controlador de navegación.
Lo hice una extensión de UIApplication como @Christian, ya que esto tiene más sentido.
extension UIApplication {
var visibleViewController: UIViewController? {
return getVisibleViewController(nil)
}
private func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
let rootVC = rootViewController ?? UIApplication.shared.keyWindow?.rootViewController
if rootVC!.isKind(of: UINavigationController.self) {
let navigationController = rootVC as! UINavigationController
return getVisibleViewController(navigationController.viewControllers.last!)
}
if rootVC!.isKind(of: UITabBarController.self) {
let tabBarController = rootVC as! UITabBarController
return getVisibleViewController(tabBarController.selectedViewController!)
}
if let presentedVC = rootVC?.presentedViewController {
return getVisibleViewController(presentedVC)
}
return rootVC
}
}
En mi caso, tengo el controlador Tabbar y luego el controlador de navegación para cada pestaña, espero que ayude a alguien
UIViewController *loginViewController=self.window.rootViewController;
UITabBarController *controller=loginViewController.tabBarController;
UIViewController *CurrentController = controller.selectedViewController.childViewControllers.lastObject;
Esto debería hacerlo por ti:
- (void)applicationWillResignActive:(UIApplication *)application
{
UIViewController *vc = [self visibleViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil)
{
return rootViewController;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self visibleViewController:lastViewController];
}
if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [self visibleViewController:selectedViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self visibleViewController:presentedViewController];
}
La respuesta de @ aviatorken89 funcionó bien para mí. Tuve que traducirlo a Swift, para cualquiera que empiece con Swift:
Actualizado para Swift 3:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
Respuesta antigua:
func applicationWillResignActive(application: UIApplication) {
let currentViewController = getVisibleViewController(nil)
}
func getVisibleViewController(var rootViewController: UIViewController?) -> UIViewController? {
if rootViewController == nil {
rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
}
if rootViewController?.presentedViewController == nil {
return rootViewController
}
if let presented = rootViewController?.presentedViewController {
if presented.isKindOfClass(UINavigationController) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKindOfClass(UITabBarController) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
Las principales recomendaciones aquí funcionarán bien en muchos escenarios para obtener la solución de "mejor conjetura" , pero con unos pocos ajustes menores, podemos obtener una solución más completa que no se base en la implementación de la jerarquía de vistas de su aplicación.
1) La jerarquía de vistas de Cocoa Touch permite que varios niños estén presentes y sean visibles a la vez, por lo que debemos solicitar los controladores de vista visibles actuales (plural) y manejar los resultados en consecuencia
2) UINavigationController
s y UITabBarController
s se usan comúnmente en aplicaciones iOS, pero no son el único tipo de controladores de vista de contenedor. UIKit también proporciona el UIPageViewController
, UISplitViewController
, y le permite escribir sus propios controladores de vista de contenedor personalizados.
3) Probablemente queremos ignorar los modales de ventanas emergentes y los tipos específicos de controladores de vista como UIAlertController
o un controlador de vista infantil integrado.
private func visibleViewControllers() -> [UIViewController] {
guard let root = window?.rootViewController else { return [] }
return visibleLeaves(from: root, excluding: [UIAlertController.self])
}
private func visibleLeaves(from parent: UIViewController, excluding excludedTypes: [UIViewController.Type] = []) -> [UIViewController] {
let isExcluded: (UIViewController) -> Bool = { vc in
excludedTypes.contains(where: { vc.isKind(of: $0) }) || vc.modalPresentationStyle == .popover
}
if let presented = parent.presentedViewController, !isExcluded(presented) {
return self.visibleLeaves(from: presented, excluding: excludedTypes)
}
let visibleChildren = parent.childViewControllers.filter {
$0.isViewLoaded && $0.view.window != nil
}
let visibleLeaves = visibleChildren.flatMap {
return self.visibleLeaves(from: $0, excluding: excludedTypes)
}
if visibleLeaves.count > 0 {
return visibleLeaves
} else if !isExcluded(parent) {
return [parent]
} else {
return []
}
}
Lo implementamos como una extensión de UIApplication:
import UIKit
extension UIApplication {
var visibleViewController: UIViewController? {
guard let rootViewController = keyWindow?.rootViewController else {
return nil
}
return getVisibleViewController(rootViewController)
}
private func getVisibleViewController(_ rootViewController: UIViewController) -> UIViewController? {
if let presentedViewController = rootViewController.presentedViewController {
return getVisibleViewController(presentedViewController)
}
if let navigationController = rootViewController as? UINavigationController {
return navigationController.visibleViewController
}
if let tabBarController = rootViewController as? UITabBarController {
return tabBarController.selectedViewController
}
return rootViewController
}
}
Si el controlador de vista raíz de su aplicación es un UINavigationController, puede usar esto:
UIViewController *currentControllerName = ((UINavigationController*)appDelegate.window.rootViewController).visibleViewController;
y si está utilizando UITabBarController, puede usar esto:
UIViewController *currentControllerName = ((UITabBarController*)appDelegate.window.rootViewController).selectedViewController;
Si está utilizando IQKeyboardManager tienen una extensión allí
- (UIViewController *) currentViewController;
para que puedas hacer
application.keyWindow?.currentViewController? // <- there you go
así que agrega esto a tu archivo pod
pod ''IQKeyboardManager''
then pod update and you are away!
espero que esto ayude
Una versión modificada de una respuesta anterior usando la categoría UIViewController en ObjC:
UIViewController + VisibleViewController.h
#import <UIKit/UIKit.h>
@interface UIViewController (VisibleViewController)
- (UIViewController *)visibleViewController;
@end
UIViewController + VisibleViewController.m
#import "UIViewController+VisibleViewController.h"
@implementation UIViewController (VisibleViewController)
- (UIViewController *)visibleViewController {
if (self.presentedViewController == nil) {
return self;
}
if ([self.presentedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [lastViewController visibleViewController];
}
if ([self.presentedViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBarController = (UITabBarController *)self.presentedViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [selectedViewController visibleViewController];
}
UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;
return [presentedViewController visibleViewController];
}
@end
AppDelegate.m
#import "UIViewController+VisibleViewController.h"
- (UIViewController *) applicationVisibleViewController {
return [self.window.rootViewController visibleViewController];
}
modificado de troop231
+ (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
if ([rootViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)rootViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self visibleViewController:lastViewController];
}
if ([rootViewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [self visibleViewController:selectedViewController];
}
if (rootViewController.presentedViewController != nil)
{
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self visibleViewController:presentedViewController];
}
return rootViewController;
}