UIAlertView/UIAlertController iOS 7 and iOS 8 compatibility
IosSwiftUialertviewUialertcontrollerIos Problem Overview
I am using Swift to write an app and I need to show an alert. The app must be iOS 7 and iOS 8 compatible. Since UIAlertView
has been replaced with UIAlertController
, how can I check if the UIAlertController
is available without checking the system version? I have been hearing that Apple recommends that we should not check the system version of the device in order to determine the availability of an API.
This is what I am using for iOS 8 but this crashes on iOS 7 with "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)
If I use the UIAlertView for iOS 8, I get this warning: Warning: Attempt to dismiss from view controller <_UIAlertShimPresentingViewController: 0x7bf72d60> while a presentation or dismiss is in progress!
Ios Solutions
Solution 1 - Ios
The detection pattern is identical to the Objective-C style.
You need to detect whether the current active runtime has the ability to instantiate this class
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
}
Don't try and work out this based on the OS version. You need to detect abilities NOT OS.
EDIT
The original detector for this answer NSClassFromString("UIAlertController")
fails under -O
optimisation so its been changed to the current version which does work for Release builds
EDIT 2
NSClassFromString
is working at all optimisations in Xcode 6.3/Swift 1.2
Solution 2 - Ios
For non-swift code, pure objective-C do this
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];
}
Solution 3 - Ios
I was annoyed that I kept having to write out both situations, so I wrote a compatible UIAlertController that works for iOS 7 as well so I just threw it up on GitHub. I did my best to replicate the (much better) methods of adding buttons and actions of the UIAlertController. Works with both Objective-C and Swift. I'm posting this as I found this question when searching on Google and figured it could be helpful for others.
Solution 4 - Ios
You can resolve your issue using this 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)
}
and UIKit had to be marked Optional rather than Required.
Courtsey :- https://stackoverflow.com/q/24058543/1865424
Solution 5 - Ios
Swift 2.0
if #available(iOS 8.0, *) {
} else {
}
Solution 6 - Ios
If this is shared code, and there is the possibility that the code can be used in an iOS 8 extension (where UIAlertView and UIActionSheet are restricted APIs) as well as iOS 7, where UIAlertController does not exist, have a look at: JVAlertController
It is an API-compatible back-port of UIAlertController to iOS 7, that I undertook to make SDK code safe for use in both iOS 7 and iOS 8 extensions.
Solution 7 - Ios
You could use a category to solve that (though you'll need to convert it to 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
Then just call
[UIView showSimpleAlertWithTitle: @"Error" message: message cancelButtonTitle: @"OK"];
But, if you do not want to check the system version, just use
BOOL lowerThaniOS8 = NSClassFromString( @"UIAlertController" ) == nil;
inside the category UIView( AlertCompatibility )
Solution 8 - Ios
If your using both iOS 7- UIAlertView and iOS 8+ UIAlertController as described above, and you want your UIAlertController blocks to call your UIAlertView's delegate (e.g. MyController) alertView:didDismissWithButtonIndex method to continue processing the results, here is an example of how to do that:
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];
}
];
...
This uses Apple's recommendation for capturing self in a block: Avoid Strong Reference Cycles when Capturing self
Of course, this method assumes you only have one UIAlertView in the controller and therefore pass nil as its value to the delegate method. Otherwise, you'd need to instantiate (and tag) a "fake" UIAlertView to pass to alertView:didDismissWithButtonIndex.
Solution 9 - Ios
Here to check two way of UIAlertView
and UIAlertContoller.
Check 1 : iOS verstion check 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()
}
Check 2 : check UIAlertController
nil then iOS version below 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()
}
Solution 10 - Ios
If you want to be compatible with iOS 7, just don't use UIAlertController
. Simple as that.
UIAlertView
has not been replaced, it still works perfectly and will continue to work perfectly for the foreseeable future.
Solution 11 - Ios
Here is my drag and drop swift solution:
//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()
}
}
Call like this:
CozAlert("reportTitle", message: "reportText", action: "reportButton", sender: self)
Beware this is only for the most basic alerts, you might need additional code for advanced stuff.
Solution 12 - Ios
Try below code. It works fine for both iOS 8 and below version.
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];
}
Solution 13 - Ios
In iOS8, there is a new class UIAlertController
that replaces UIAlertView
and UIActionSheet
. From iOS8 on, use UIAlertController, and for iOS8 and before use UIAlertView and UIActionSheet. I think that iOS8 added size classes
that change UIAlertView
display direction. See: https://github.com/wangyangcc/FYAlertManage
Solution 14 - Ios
download alert class from this link and use it easily for ios 6 ,7 and 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];