57

我正在使用 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)

如果我使用 iOS 8 的 UIAlertView,我会收到以下警告:Warning: Attempt to dismiss from view controller <_UIAlertShimPresentingViewController: 0x7bf72d60> while a presentation or dismiss is in progress!

4

14 回答 14

77

检测模式与 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因此已更改为适用于发布版本的当前版本

编辑 2

NSClassFromString正在 Xcode 6.3/Swift 1.2 中进行所有优化

于 2014-08-17T11:57:40.027 回答
29

对于非 swift 代码,纯 Objective-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];

    }
于 2014-09-27T11:54:08.643 回答
9

我很生气,因为我不得不写出这两种情况,所以我写了一个兼容的 UIAlertController,它也适用于 iOS 7,所以我把它扔到了 GitHub 上。我尽力复制了(更好的)添加 UIAlertController 按钮和操作的方法。适用于 Objective-C 和 Swift。我发布这个是因为我在谷歌上搜索时发现了这个问题,并认为它可能对其他人有帮助。

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

于 2014-12-03T16:27:57.673 回答
8

您可以使用此代码解决您的问题:-

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 必须标记为可选而不是必需。

Courtsey :-可以在 iOS 7 和 iOS 8 上运行的警报

于 2014-08-17T11:35:49.317 回答
6

斯威夫特 2.0

 if #available(iOS 8.0, *) {

 } else {

 }
于 2015-09-17T02:18:03.353 回答
4

如果这是共享代码,并且该代码有可能用于 iOS 8 扩展(其中 UIAlertView 和 UIActionSheet 是受限 API)以及不存在 UIAlertController 的 iOS 7,请查看:JVAlertController

它是 UIAlertController 到 iOS 7 的 API 兼容的后向端口,我承诺让 SDK 代码在 iOS 7 和 iOS 8 扩展中都能安全使用。

于 2015-03-07T04:37:27.927 回答
2

您可以使用一个类别来解决这个问题(尽管您需要将其转换为 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)

于 2014-09-30T17:45:19.190 回答
2

如果您如上所述同时使用 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 对在块中捕获自我的建议: Avoid Strong Reference Cycles when Capturing self

当然,此方法假设您在控制器中只有一个 UIAlertView,因此将 nil 作为其值传递给委托方法。否则,您需要实例化(并标记)“假”UIAlertView 以传递给 alertView:didDismissWithButtonIndex。

于 2015-01-12T02:04:58.623 回答
1

这里要检查UIAlertViewUIAlertContoller的两种方式。

检查 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:检查UIAlertControllernil 然后 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()
    }
于 2016-05-24T09:16:21.300 回答
0

如果你想兼容 iOS 7,就不要使用UIAlertController. 就那么简单。

UIAlertView没有被替换,它仍然可以完美运行,并将在可预见的未来继续完美运行。

于 2014-08-04T03:19:15.413 回答
0

这是我的拖放快速解决方案:

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

请注意,这仅适用于最基本的警报,您可能需要额外的代码来处理高级内容。

于 2014-12-21T09:04:03.467 回答
-1

试试下面的代码。它适用于 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];
}
于 2016-06-24T06:28:50.320 回答
-2

在 iOS8 中,有一个新的类UIAlertController取代了UIAlertViewUIActionSheet。从 iOS8 开始,使用 UIAlertController,对于 iOS8 和之前使用 UIAlertView 和 UIActionSheet。我认为 iOS8 添加size classes了改变UIAlertView显示方向的功能。见:https ://github.com/wangyangcc/FYAlertManage

于 2014-10-29T07:00:23.137 回答
-2

从此链接下载警报类 并将其轻松用于 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];
于 2014-12-12T20:03:47.820 回答