Compatibilidad con UIAlertView / UIAlertController iOS 7 e iOS 8

Estoy usando Swift para escribir una aplicación y necesito mostrar una alerta. La aplicación debe ser compatible con iOS 7 e iOS 8. Como UIAlertView ha sido reemplazado por UIAlertController , ¿cómo puedo verificar si el UIAlertController está disponible sin verificar la versión del sistema? He estado escuchando que Apple recomienda que no revisemos la versión del sistema del dispositivo para determinar la disponibilidad de una API.

Esto es lo que estoy usando para iOS 8, pero esto falla en iOS 7 con ” dyld: Symbol not found: _OBJC_CLASS_$_UIAlertAction “:

 let alert = UIAlertController(title: "Error", message: message, preferredStyle: .Alert) let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil) alert.addAction(cancelAction) presentViewController(alert, animated: true, completion: nil) 

Si utilizo UIAlertView para iOS 8, recibo esta advertencia: Warning: Attempt to dismiss from view controller while a presentation or dismiss is in progress!

El patrón de detección es idéntico al estilo Objective-C.

Debe detectar si el tiempo de ejecución activo actual tiene la capacidad de instanciar esta clase

 if objc_getClass("UIAlertController") != nil { println("UIAlertController can be instantiated") //make and use a UIAlertController } else { println("UIAlertController can NOT be instantiated") //make and use a UIAlertView } 

No intente resolver esto según la versión del sistema operativo. Necesita detectar habilidades NO SO.

EDITAR

El detector original para esta respuesta NSClassFromString("UIAlertController") falla en virtud de la optimización -O por lo que se ha cambiado a la versión actual que funciona para las versiones de Release.

EDIT 2

NSClassFromString funciona en todas las optimizaciones en Xcode 6.3 / Swift 1.2

Para código no rápido, objective puro-C hacer esto

 if ([UIAlertController class]) { // use UIAlertController UIAlertController *alert= [UIAlertController alertControllerWithTitle:@"Enter Folder Name" message:@"Keep it short and sweet" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){ //Do Some action here UITextField *textField = alert.textFields[0]; NSLog(@"text was %@", textField.text); }]; UIAlertAction* cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { NSLog(@"cancel btn"); [alert dismissViewControllerAnimated:YES completion:nil]; }]; [alert addAction:ok]; [alert addAction:cancel]; [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { textField.placeholder = @"folder name"; textField.keyboardType = UIKeyboardTypeDefault; }]; [self presentViewController:alert animated:YES completion:nil]; } else { // use UIAlertView UIAlertView* dialog = [[UIAlertView alloc] initWithTitle:@"Enter Folder Name" message:@"Keep it short and sweet" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; dialog.alertViewStyle = UIAlertViewStylePlainTextInput; dialog.tag = 400; [dialog show]; } 

Me molestaba tener que escribir ambas situaciones, así que escribí un UIAlertController compatible que también funciona para iOS 7, así que lo tiré en GitHub. Hice todo lo posible para replicar los métodos (mucho mejores) de agregar botones y acciones del UIAlertController. Funciona con Objective-C y Swift. Estoy publicando esto porque encontré esta pregunta cuando busqué en Google y pensé que podría ser útil para otros.

https://github.com/BayPhillips/compatible-alert-controller

Puedes resolver tu problema usando este código:

 var device : UIDevice = UIDevice.currentDevice()!; var systemVersion = device.systemVersion; var iosVerion : Float = systemVersion.bridgeToObjectiveC().floatValue; if(iosVerion < 8.0) { let alert = UIAlertView() alert.title = "Noop" alert.message = "Nothing to verify" alert.addButtonWithTitle("Click") alert.show() }else{ var alert : UIAlertController = UIAlertController(title: "Noop", message: "Nothing to verify", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "Click", style:.Default, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) } 

y UIKit tenía que marcarse Opcional en lugar de Obligatorio.

Courtsey: - Alerta que puede funcionar en iOS 7 e iOS 8

Swift 2.0

  if #available(iOS 8.0, *) { } else { } 

Si se trata de un código compartido, y existe la posibilidad de que el código se pueda utilizar en una extensión de iOS 8 (donde UIAlertView y UIActionSheet son API restringidas), así como en iOS 7, donde UIAlertController no existe, échale un vistazo a: JVAlertController

Es un puerto de respaldo compatible con API de UIAlertController para iOS 7, que me comprometí a hacer que el código SDK fuera seguro para su uso en las extensiones iOS 7 e iOS 8.

Puede usar una categoría para resolver eso (aunque deberá convertirlo a Swift):

 @implementation UIView( AlertCompatibility ) +( void )showSimpleAlertWithTitle:( NSString * )title message:( NSString * )message cancelButtonTitle:( NSString * )cancelButtonTitle { if( [[UIDevice currentDevice] isSystemVersionLowerThan: @"8"] ) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle: title message: message delegate: nil cancelButtonTitle: cancelButtonTitle otherButtonTitles: nil]; [alert show]; } else { // nil titles break alert interface on iOS 8.0, so we'll be using empty strings UIAlertController *alert = [UIAlertController alertControllerWithTitle: title == nil ? @"": title message: message preferredStyle: UIAlertControllerStyleAlert]; UIAlertAction *defaultAction = [UIAlertAction actionWithTitle: cancelButtonTitle style: UIAlertActionStyleDefault handler: nil]; [alert addAction: defaultAction]; UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; [rootViewController presentViewController: alert animated: YES completion: nil]; } } @end @implementation UIDevice( SystemVersion ) -( BOOL )isSystemVersionLowerThan:( NSString * )versionToCompareWith { if( versionToCompareWith.length == 0 ) return NO; NSString *deviceSystemVersion = [self systemVersion]; NSArray *systemVersionComponents = [deviceSystemVersion componentsSeparatedByString: @"."]; uint16_t deviceMajor = 0; uint16_t deviceMinor = 0; uint16_t deviceBugfix = 0; NSUInteger nDeviceComponents = systemVersionComponents.count; if( nDeviceComponents > 0 ) deviceMajor = [( NSString * )systemVersionComponents[0] intValue]; if( nDeviceComponents > 1 ) deviceMinor = [( NSString * )systemVersionComponents[1] intValue]; if( nDeviceComponents > 2 ) deviceBugfix = [( NSString * )systemVersionComponents[2] intValue]; NSArray *versionToCompareWithComponents = [versionToCompareWith componentsSeparatedByString: @"."]; uint16_t versionToCompareWithMajor = 0; uint16_t versionToCompareWithMinor = 0; uint16_t versionToCompareWithBugfix = 0; NSUInteger nVersionToCompareWithComponents = versionToCompareWithComponents.count; if( nVersionToCompareWithComponents > 0 ) versionToCompareWithMajor = [( NSString * )versionToCompareWithComponents[0] intValue]; if( nVersionToCompareWithComponents > 1 ) versionToCompareWithMinor = [( NSString * )versionToCompareWithComponents[1] intValue]; if( nVersionToCompareWithComponents > 2 ) versionToCompareWithBugfix = [( NSString * )versionToCompareWithComponents[2] intValue]; return ( deviceMajor < versionToCompareWithMajor ) || (( deviceMajor == versionToCompareWithMajor ) && ( deviceMinor < versionToCompareWithMinor )) || (( deviceMajor == versionToCompareWithMajor ) && ( deviceMinor == versionToCompareWithMinor ) && ( deviceBugfix < versionToCompareWithBugfix )); } @end 

Entonces simplemente llama

 [UIView showSimpleAlertWithTitle: @"Error" message: message cancelButtonTitle: @"OK"]; 

Pero, si no desea verificar la versión del sistema, solo use

 BOOL lowerThaniOS8 = NSClassFromString( @"UIAlertController" ) == nil; 

dentro de la categoría UIView (AlertCompatibility)

Si usa iOS 7 u UIAlertView e iOS 8+ UIAlertController como se describió anteriormente y desea que sus bloques UIAlertController llamen al delegado de UIAlertView (por ejemplo MyController) método alertView: didDismissWithButtonIndex para continuar procesando los resultados, aquí hay un ejemplo de cómo hacerlo ese:

 if ([UIAlertController class]) { MyController * __weak mySelf = self; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:alertTitle message:alertMessage preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:alertCancel style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [mySelf alertView:nil didDismissWithButtonIndex:0]; } ]; ... 

Esto usa la recomendación de Apple para capturar uno mismo en un bloque: evitar los ciclos de referencia fuertes al capturar uno mismo

Por supuesto, este método supone que solo tiene un UIAlertView en el controlador y, por lo tanto, pasa nil como valor al método de delegado. De lo contrario, necesitarías instanciar (y etiquetar) un UIAlertView “falso” para pasar a alertView: didDismissWithButtonIndex.

Aquí para verificar dos formas de UIAlertView y UIAlertContoller .

Comprobación 1: Verificación de iOS verifique UIAlertController Class.

  if #available(iOS 8.0, *) { // UIALertController let alert = UIAlertController(title: "Alert", message: "Alert after 8.0", preferredStyle: .Alert) let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil) alert.addAction(cancelAction) presentViewController(alert, animated: true, completion: nil) } else { // UIALertView UIAlertView(title: "Alert", message: "Alert below iOS V 8.0", delegate: nil, cancelButtonTitle: "OK").show() } 

Comprobación 2: compruebe el control UIAlertController nil y luego la versión iOS debajo de 8.0.

  if objc_getClass("UIAlertController") != nil { // UIALertController let alert = UIAlertController(title: "Alert", message: "Alert after 8.0", preferredStyle: .Alert) let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil) alert.addAction(cancelAction) presentViewController(alert, animated: true, completion: nil) } else { // UIALertView UIAlertView(title: "Alert", message: "Alert below iOS V 8.0", delegate: nil, cancelButtonTitle: "OK").show() } 

Si quieres ser compatible con iOS 7, simplemente no uses UIAlertController . Simple como eso.

UIAlertView no se ha reemplazado, todavía funciona perfectamente y seguirá funcionando perfectamente en el futuro previsible.

Aquí está mi solución rápida de arrastrar y soltar:

 //Alerts change in iOS8, this method is to cover iOS7 devices func CozAlert(title: String, message: String, action: String, sender: UIViewController){ if respondsToSelector("UIAlertController"){ var alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: action, style: UIAlertActionStyle.Default, handler:nil)) sender.presentViewController(alert, animated: true, completion: nil) } else { var alert = UIAlertView(title: title, message: message, delegate: sender, cancelButtonTitle:action) alert.show() } } 

Llamar así:

 CozAlert("reportTitle", message: "reportText", action: "reportButton", sender: self) 

Tenga en cuenta que esto es solo para las alertas más básicas, es posible que necesite código adicional para cosas avanzadas.

Pruebe el siguiente código. Funciona bien para iOS 8 y versiones inferiores.

 if (IS_OS_8_OR_LATER) { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { }]; [alertVC addAction:cancelAction]; [[[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController] presentViewController:alertVC animated:YES completion:^{ }]; } else{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:msg delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; [alert show]; } 

descargue la clase de alerta desde este enlace y úselo fácilmente para iOS 6, 7 y 8

 //Old code **UIAlertView** *alert=[[**UIAlertView** alloc]initWithTitle:@"FreeWare" message:@"Welcome to Common class" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil]; //New code **MyAlertView** *alert=[[**MyAlertView** alloc]initWithTitle:@"FreeWare" message:@"Welcome to Common class" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil]; 

En iOS8, hay una nueva clase UIAlertController que reemplaza UIAlertView y UIActionSheet . Desde iOS8 en adelante, use UIAlertController, y para iOS8 y antes use UIAlertView y UIActionSheet. Creo que iOS8 agregó size classes que cambian la dirección de visualización de UIAlertView . Ver: https://github.com/wangyangcc/FYAlertManage