web-dev-qa-db-fra.com

Compatibilité UIAlertView/UIAlertController iOS 7 et iOS 8

J'utilise Swift pour écrire une application et je dois afficher une alerte. L'application doit être compatible iOS 7 et iOS 8. Puisque UIAlertView a été remplacé par UIAlertController, comment puis-je vérifier si UIAlertController est disponible sans vérifier la version du système? J'ai entendu dire qu'Apple recommandait de ne pas vérifier la version du système de l'appareil afin de déterminer la disponibilité d'une API.

C’est ce que j’utilise pour iOS 8, mais cela se bloque sur iOS 7 avec "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 j'utilise UIAlertView pour iOS 8, je reçois cet avertissement: Warning: Attempt to dismiss from view controller <_UIAlertShimPresentingViewController: 0x7bf72d60> while a presentation or dismiss is in progress!

56
Shan

Le modèle de détection est identique au style Objective-C. 

Vous devez détecter si le runtime actif en cours est capable d'instancier cette classe.

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
}

N'essayez pas de résoudre ce problème en vous basant sur la version du système d'exploitation. Vous devez détecter des capacitésPASOS.

MODIFIER 

Le détecteur d'origine pour cette réponse NSClassFromString("UIAlertController") échoue sous l'optimisation -O et a donc été remplacé par la version actuelle qui fonctionne pour les versions Release

EDIT 2

NSClassFromString fonctionne avec toutes les optimisations dans Xcode 6.3/Swift 1.2

76
Warren Burton

Pour le code non-Swift, objectif-C pur le fait 

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];

    }
29
Sam B

J'étais ennuyé de devoir écrire dans les deux situations, j'ai donc écrit un contrôleur UIAlert compatible qui fonctionne également pour iOS 7; J'ai fait de mon mieux pour reproduire les méthodes (bien meilleures) d'ajout de boutons et d'actions de UIAlertController. Fonctionne à la fois avec Objective-C et Swift. Je poste ceci car j'ai trouvé cette question lors d'une recherche sur Google et j'ai pensé que cela pourrait être utile pour les autres.

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

9
Bay Phillips

Vous pouvez résoudre votre problème en utilisant ce code: -

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)
        }

et UIKit devait être marqué comme facultatif plutôt que obligatoire.

Courtsey: - Alerte pouvant fonctionner sur iOS 7 et iOS 8

8
Shivaay

Swift 2.0 

 if #available(iOS 8.0, *) {

 } else {

 }
6
William Hu

S'il s'agit de code partagé et qu'il est possible que le code puisse être utilisé dans une extension iOS 8 (où UIAlertView et UIActionSheet sont des API restreintes), ainsi que dans iOS 7, où UIAlertController n'existe pas, consultez: JVAlertController

Il s’agit d’un port arrière d’UIAlertController pour iOS 7 compatible avec les API, que j’ai entrepris de rendre le code SDK sûr pour une utilisation dans les extensions iOS 7 et iOS 8.

4
jverdi

Vous pouvez utiliser une catégorie pour résoudre ce problème (bien que vous deviez le convertir en 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

Ensuite, il suffit d'appeler

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

Mais si vous ne voulez pas vérifier la version du système, utilisez simplement

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

dans la catégorie UIView (AlertCompatibility)

2
Daniel Alves

Si vous utilisez à la fois iOS 7- UIAlertView et iOS 8+ UIAlertController comme décrit ci-dessus et que vous voulez que votre méthode UIAlertController appelle le délégué de votre UIAlertView (par exemple, MyController) alertView: didDismissWithButtonIndex pour continuer à traiter les résultats, voici un exemple de procédure cette:

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];
            }
    ];

...

Ceci utilise la recommandation d’Apple pour la capture de auto dans un bloc: Évitez les cycles de référence élevés lors de la capture d’auto

Bien sûr, cette méthode suppose que vous n’ayez qu’un seul UIAlertView dans le contrôleur et que vous transmettez par conséquent la valeur nil à la méthode déléguée. Sinon, vous devez instancier (et marquer) un "faux" UIAlertView à transmettre à alertView: didDismissWithButtonIndex.

2
ScottyB

Ici pour vérifier deux façons de UIAlertView et UIAlertContoller .

Vérification 1: Vérification de la version iOS 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()
    }

Vérification 2: vérifiez UIAlertController nil, puis la version iOS inférieure à 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()
    }
1
Kirit Modi

Si vous souhaitez être compatible avec iOS 7, n'utilisez pas UIAlertController. Aussi simple que cela.

UIAlertView n'a pas été remplacé, il fonctionne toujours parfaitement et continuera de fonctionner parfaitement dans un avenir proche.

0
Abhi Beckert

Voici ma solution Swift drag and drop:

//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()
    }
}

Appelle comme ça:

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

Attention, cela ne concerne que les alertes les plus élémentaires. Vous aurez peut-être besoin de code supplémentaire pour les éléments avancés.

0
Esqarrouth