objective c - Mostrar UIAlertController de la clase UIView/NSObject
objective-c uialertview (9)
Tengo una working iOS application
Para support iOS8
, estoy reemplazando UIAlertView/UIActionSheet with UIAlertController
.
Problema:
Para visualizar UIAlertController, necesito el método presentViewController de la clase UIViewController.
Pero UIAlertView se muestra desde clases que se inherited
de UIView or NSObject
,
No puedo obtener el método [self presentViewController...]
por razones obvias.
Mi trabajo :
Intenté que rootViewController formara la ventana actual y visualice UIAlertController.
[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController ...]
pero tengo algunos problemas de rotación, como si mi controlador de vista actual no tiene soporte de rotación, rotará si UIAlertController está abierto.
Pregunta:
¿Alguien se enfrentó al mismo problema y tiene una solución segura?
Si es así, por favor dame un ejemplo o dame alguna guía
Parece que actualmente (antes de iOS8) está activando una vista de alerta desde el objeto de visualización. Esa es una práctica bastante mala, ya que en general las alertas deben activarse a partir de acciones y lógica. Y ese código debería vivir en controladores.
Le sugiero que refactorice su código actual para mover la lógica que activa la alerta al controlador correcto, y luego puede actualizar fácilmente a iOS 8 utilizando self
como controlador.
Si, en cambio, llama a la alerta desde un objeto externo, pase el controlador al método que llama a la alerta. En algún lugar aguas arriba debe tener conocimiento del controlador.
Tuve una situación en la que una subvista contiene un botón para descartarla. Presento una alerta para confirmar la acción. Envía un mensaje al delegado, que es el controlador de vista que contiene la subvista, para eliminar la subvista
Originalmente presenté UIAlertView desde una UIView. Refactorizando para UIAlertController, ya que el UIAlertController no puede presentarse como una lata UIAlertView, se me ocurrió lo siguiente (en Swift, traducido fácilmente a ObjC):
Agregue un protocolo a la subvista:
protocol MySubviewDelegate {
// called when user taps subview/delete button
// or, you could call it from a gesture handler, etc.
func displayAlert(alert : UIAlertController)
// called when user confirms delete from the alert controller
func shouldRemoveSubview(sender : AnyObject)
}
Agregue un delegado para la subvista y agregue un controlador para el toque de botón / gesto:
class MySubview : UIView {
var subviewDelegate : MySubviewDelegate!
...
func handleTap(sender : AnyObject) {
// set up the alert controller here
var alert = UIAlertController(title: "Confirm Delete",
message: "This action is permanent. Do you wish to continue?",
preferredStyle: UIAlertControllerStyle.Alert)
// Cancel action
// nil handler means "no action if Cancel button selected"
alert.addAction(UIAlertAction(title: "Cancel",
style: UIAlertActionStyle.Cancel,
handler: nil))
// Confirm action
alert.addAction(UIAlertAction(title: "Confirm",
style: UIAlertActionStyle.Default,
handler: { (action : UIAlertAction!) -> Void in
// call delegate method to perform confirmed action, - i.e. remove
self.subviewDelegate.shouldRemoveSubview(self)
}))
// call delegate method to display alert controller
// send alert object to delegate
self.subviewDelegate.displayAlert(alert)
}
}
Establezca el UIViewController llamante como el delegado de la subvista, por ejemplo, en su método viewDidLoad () e incluya los métodos de protocolo:
class viewController : UIViewController, MySubviewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.subviewDelegate = self
...
}
func displayAlert(alert : UIAlertController) {
presentViewController(alert, animated: true, completion: nil)
}
func shouldRemoveSubview(sender : AnyObject) {
// cast as UIView / MySubview subclass
var subview = sender as MySubview
// remove the subview / perform the desired action
subview.removeFromSuperview()
...
}
...
}
Esto evita la necesidad de encontrar el controlador de vista superior, o pasar referencias para ver los controladores a las subvistas (que no sean una relación objeto / delegado).
La mejor solución para UIView
clases de UIView
está debajo
C objetivo
UIViewController *currentTopVC = [self currentTopViewController];
currentTopVC.presentViewController.........
- (UIViewController *)currentTopViewController
{
UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
while (topVC.presentedViewController)
{
topVC = topVC.presentedViewController;
}
return topVC;
}
Rápido
var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while((topVC!.presentedViewController) != nil){
topVC = topVC!.presentedViewController
}
topVC?.presentViewController........
Mi solución está abajo:
Rápido
class alert {
func msg(message: String, title: String = "")
{
let alertView = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertView, animated: true, completion: nil)
}
}
Aquí está el uso de muestra:
let Alert = alert()
Alert.msg("My alert (without title)")
Alert.msg("This is my alert", title: "Warning!")
En Swift 3:
UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
He tenido este problema. Ver my SO aquí para el código para obtener el controlador de vista superior con el que presentar otro controlador de vista.
Estoy de acuerdo en que, en la mayoría de los casos, es una mala práctica presentar un controlador de vista desde un objeto que no es un controlador de vista, pero a veces es OBLIGATORIO.
Resolví un problema esencialmente similar hoy. Al igual que Jageen , me encontré con una situación en la que quería presentar un UIAlertController pero desde una clase que no sea UIViewController. En mi caso, quería que apareciera una alerta cuando se ejecuta el bloque de falla de una solicitud HTTP.
Esto es lo que usé y, a diferencia de nuestro amigo aquí, funcionó perfectamente para mí.
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(errorAlert, animated: true, completion: nil)
En general, las alertas se deben manejar en el controlador de vista. Aquí hay un ejemplo del código requerido:
Swift 3
private func displayError(message: String) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
alertController.addAction(okayAction)
present(alertController, animated: true, completion: nil)
}
Para la pantalla UIAlertController en NSObject Class, use el siguiente código.
UIAlertController * popup = [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[popup dismissViewControllerAnimated:YES completion:nil];
}];
[popup addAction:cancel];
UIViewController *rootViewController = [[Helper shareInstance] topViewController];
[rootViewController presentViewController:popup animated:YES completion:nil];
// Ponga debajo del método en su clase Global Helper.
- (UIViewController *)topViewController {
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController {
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}