Совместимость UIAlertView / UIAlertController iOS 7 и iOS 8

Я использую Swift для написания приложения, и мне нужно показать предупреждение. Приложение должно быть совместимо с iOS 7 и iOS 8. Поскольку UIAlertView был заменен на UIAlertController, как я могу проверить, доступен ли UIAlertController, не проверяя версию системы? Я слышал, что Apple рекомендует не проверять системную версию устройства, чтобы определить доступность API.

Это то, что я использую для iOS 8, но это вылетает на iOS 7 с "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)

Если я использую UIAlertView для iOS 8, я получаю следующее предупреждение: Warning: Attempt to dismiss from view controller <_UIAlertShimPresentingViewController: 0x7bf72d60> while a presentation or dismiss is in progress!


person Shan    schedule 04.08.2014    source источник


Ответы (14)


Шаблон обнаружения идентичен стилю Objective-C.

Вам необходимо определить, может ли текущая активная среда выполнения создать экземпляр этого класса.

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
}

Не пытайтесь решить это в зависимости от версии ОС. Вам нужно обнаружить возможности НЕ ОС.

ИЗМЕНИТЬ

Исходный детектор для этого ответа NSClassFromString("UIAlertController") не работает при -O оптимизации, поэтому он был изменен на текущую версию, которая работает для сборок Release

ИЗМЕНИТЬ 2

NSClassFromString работает над всеми оптимизациями в Xcode 6.3 / Swift 1.2

person Warren Burton    schedule 17.08.2014
comment
Спасибо. Это отвечает на мой первоначальный вопрос, потому что я читал в других сообщениях, что вам не следует проверять версию ОС для обнаружения способностей. - person Shan; 18.08.2014
comment
@ bay.phillips дружелюбный постер на форумах разработчиков предоставил мне работающий детектор. Я собираюсь подать радар против оригинала - person Warren Burton; 20.12.2014
comment
@WarrenBurton приятно слышать! И да, глупо, что эта проверка работает, когда написана на Objective-C, а затем на нее можно ссылаться в коде Swift. Но так оно и есть в настоящее время, поскольку это известное ограничение языка. - person Bay Phillips; 20.12.2014

Для не-быстрого кода, чистый объект C делает это

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

    }
person Sam B    schedule 27.09.2014
comment
Этот код не будет работать, поскольку UIAlertAction не имеет свойства textFields. Только UIAlertController имеет свойство textFields. - person Matt Morey; 09.12.2014
comment
Это не очень хорошая идея, потому что поведение старых и новых предупреждений разное. Например. старое предупреждение может быть поставлено в очередь для отображения нескольких предупреждений об ошибках, новое не может просто пропускать отображение дополнительных предупреждений при отображении первого. Предупреждение: Попытка представить ‹UIAlertController: 0x7fe17268afd0› на ‹MasterViewController: 0x7fe172734310›, который уже представляет (ноль) - person malhal; 04.02.2016

Меня раздражало, что мне постоянно приходилось записывать обе ситуации, поэтому я написал совместимый UIAlertController, который также работает для iOS 7, поэтому я просто выложил его на GitHub. Я приложил все усилия, чтобы воспроизвести (намного лучшие) методы добавления кнопок и действий UIAlertController. Работает как с Objective-C, так и со Swift. Я публикую это, поскольку я нашел этот вопрос при поиске в Google и решил, что это может быть полезно для других.

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

person Bay Phillips    schedule 03.12.2014
comment
Хорошая идея, я использую вашу библиотеку, и это значительно упрощает поддержку совместимости с предупреждениями. - person Paludis; 07.01.2015
comment
Рад слышать @Paludis. Я хотел бы получить любую обратную связь, а также любой пиар, чтобы сделать его более полнофункциональным! - person Bay Phillips; 08.01.2015

Вы можете решить свою проблему, используя этот код: -

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

и UIKit нужно было пометить как необязательный, а не обязательный.

Кортси: - Оповещение, которое может работать на iOS 7 и iOS 8

person Kundan    schedule 17.08.2014
comment
Считается плохой практикой проверять конкретную версию iOS - лучше проверить, известен ли класс или отвечает ли он на селектор. - person Gonen; 09.05.2015

Swift 2.0

 if #available(iOS 8.0, *) {

 } else {

 }
person William Hu    schedule 17.09.2015

Если это общий код и есть вероятность, что этот код можно будет использовать в расширении iOS 8 (где UIAlertView и UIActionSheet являются ограниченными API-интерфейсами), а также в iOS 7, где UIAlertController не существует, посмотрите: JVAlertController

Это API-совместимый обратный порт UIAlertController для iOS 7, который я обязался сделать код SDK безопасным для использования в расширениях iOS 7 и iOS 8.

person jverdi    schedule 07.03.2015
comment
Похоже, это изящное решение! Спасибо! - person Zaxter; 01.07.2015

Вы можете использовать категорию для решения этой проблемы (хотя вам нужно будет преобразовать ее в 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

Тогда просто позвони

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

Но, если вы не хотите проверять версию системы, просто используйте

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

внутри категории UIView (AlertCompatibility)

person Daniel Alves    schedule 30.09.2014
comment
как я могу вызвать действие в классе вызывающего абонента? - person iPhone Guy; 17.11.2014
comment
Вы можете передать цель действия в качестве другого параметра +showSimpleAlertWithTitle:message:cancelButtonTitle: - person Daniel Alves; 19.11.2014
comment
Сравнивать версию системы для подобных задач не рекомендуется. Как говорится в принятом ответе, вы должны проверять возможности, а не версии. - person Alex Repty; 20.11.2014
comment
Да, вот почему я также дал это альтернативное решение: BOOL lowerThaniOS8 = NSClassFromString( @"UIAlertController" ) == nil; Если вы прочтете весь ответ, вы найдете его внизу =) - person Daniel Alves; 24.11.2014

Если вы используете как iOS 7- UIAlertView, так и iOS 8+ UIAlertController, как описано выше, и хотите, чтобы блоки UIAlertController вызывали ваш делегат UIAlertView (например, MyController) alertView: didDismissWithButtonIndex для продолжения обработки результатов, вот пример того, как это сделать что:

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

...

Здесь используется рекомендация Apple по захвату себя в блоке: Избегайте сильных ссылочных циклов при захвате себя

Конечно, этот метод предполагает, что у вас есть только один UIAlertView в контроллере и, следовательно, вы передаете nil в качестве его значения методу делегата. В противном случае вам потребуется создать экземпляр (и пометить) «поддельный» UIAlertView для передачи в alertView: didDismissWithButtonIndex.

person ScottyB    schedule 12.01.2015

Здесь можно проверить два способа UIAlertView и UIAlertContoller.

Проверка 1. Проверка версии iOS UIAlertController Класс.

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

Проверка 2: отметьте UIAlertController nil, затем версию iOS ниже 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()
    }
person Kirit Modi    schedule 24.05.2016

Если вы хотите быть совместимым с iOS 7, просто не используйте UIAlertController. Просто как тот.

UIAlertView не был заменен, он по-прежнему отлично работает и будет продолжать отлично работать в обозримом будущем.

person Abhi Beckert    schedule 04.08.2014
comment
Но я получаю это предупреждение при запуске на устройстве iOS 8: Предупреждение: Попытка уволить из контроллера представления ‹_UIAlertShimPresentingViewController: 0x7bf72d60› во время презентации или закрытия! - person Shan; 04.08.2014
comment
Так подать отчет об ошибке? iOS 8 - это бета-версия, в ней есть ошибки. - person Abhi Beckert; 11.08.2014
comment
Я (ab) использую UIAlertView для типа двойного ввода пароля, два поля редактирования, которые должны совпадать. Я делаю это, добавляя подпредставления к большему представлению, а затем добавляя это представление в качестве дополнительного представления представления предупреждений. К сожалению, размер представления упорядочен, и второе поле редактирования полностью обрезано, как и кнопки. - person William T. Mallard; 19.09.2014
comment
Я подал радар о том, что subviews больше не возвращаются для UIActionSheet / UIAlertViews. Мне в основном сказали использовать новый UIAlertController, поскольку UIAlert / Action устарел. Не совсем идеально, поскольку вы не можете настроить iOS 7 и ожидать тех же результатов для пользователей, работающих на iOS 8. Я думаю, что единственный способ заставить его работать правильно для пользователей - это определить способность нового стиля и таким образом применить изменения. - person Bill Burgess; 23.09.2014
comment
UIAlertView устарел, если вы посмотрите исходный код, вы обнаружите, что следующий commet UIAlertView устарел. Вместо этого используйте UIAlertController с предпочтительным стилем UIAlertControllerStyleAlert. См. Также: nshipster.com/uialertcontroller - person Eric; 06.10.2014
comment
@Eric он устарел, но только в документации, они намеренно не помечали его как устаревшее в файлах заголовков. Обычно, когда Apple делает это, это означает, что они хотят, чтобы люди ушли от нее, но они будут продолжать поддерживать UIAlertView в течение еще 5 лет или около того. Определенно, еще не время переходить на UIAlertController для большинства приложений. Apple хочет, чтобы разработчики по возможности поддерживали обратную совместимость с iOS 7, а в iOS 7 нет UIAlertController. Не знаю, почему мой комментарий был отвергнут, когда я просто констатировал факт. - person Abhi Beckert; 06.10.2014
comment
У меня проблемы с совместимостью с UIAlertView под iOS8. Иногда я вижу, что только 2/3 левых экрана моего iPad (альбомная ориентация) затемнены. Представление предупреждений выглядит неуместным, как если бы устройство считало его повернутым. Правая часть экрана по-прежнему принимает сенсорные жесты! - person Brad Thomas; 06.01.2015

Вот мое быстрое решение для перетаскивания:

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

Звоните так:

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

Помните, что это только для самых основных предупреждений, вам может потребоваться дополнительный код для расширенных функций.

person Esqarrouth    schedule 21.12.2014

Попробуйте код ниже. Он отлично работает как для iOS 8, так и для более ранней версии.

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];
}
person amitshinik    schedule 24.06.2016
comment
Почему мой ответ отклонен. Если не так, поправьте меня. Спасибо - person amitshinik; 21.07.2016

загрузите класс предупреждений по этой ссылке и легко используйте его для iOS 6, 7 и 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];
person GameLoading    schedule 12.12.2014

В iOS8 есть новый класс UIAlertController, который заменяет UIAlertView и UIActionSheet. Начиная с iOS8, используйте UIAlertController, а для iOS8 и перед использованием UIAlertView и UIActionSheet. Я думаю, что в iOS8 добавлены size classes, которые изменяют UIAlertView направление отображения. См. https://github.com/wangyangcc/FYAlertManage

person yangyang wang    schedule 29.10.2014
comment
Если вы сможете объяснить ответ, он будет полезен другим. - person Nilesh; 29.10.2014