In ios8 delegate methods of UIActionSheet calling multiple times
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
I have checked in ipad 2 with IOS 8
Its a bug of the ios 8..
As the #rob said UIActionSheet is deprecated in iOS 8. (UIActionSheetDelegate is also deprecated.)
To create and manage action sheets in iOS 8 and later, use UIAlertController
This is indeed the current behavior that I've seen reported in several places. It does appear to be a bug and hopefully will be fixed soon but no way to know for sure.
You should UIAlertController to replace all AlertView and ActionSheet in iOS8.
If your target lower than iOS8, then you should check version and add more code
For example, this code is for iOS8 ActionSheet
-(void)testActionSheet{
UIAlertController* alertAS = [UIAlertController alertControllerWithTitle:#"Test ActionSheet"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"Action" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
NSLog(#"Action");
}];
[alertAS addAction:defaultAction];
UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}];
[alertAS addAction:cancleAction];
[self presentViewController:alertAS animated:YES completion:nil];
}
If you want to continue using the existing API, there is a bit of behavior that lets you know when to run your code and when to ignore the delegate call - the buttonIndex.
The first time the delegate methods are called, they are always passed the correct buttonIndex. The second time through, however, they are called with a buttonIndex of the cancel button.
Unfortunately UIActionSheet is deprecated in iOS 8:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIActionSheet_Class/
I had the same issue. One workaround: Use actionSheet.tag. Set it to a valid number (it will be 0 by default) such as 1, 2, ... while instantiating. Handle the response in:
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
Check if it is valid (ex: not -1 before handling it). Once the response is handled here, before returning, set:
actionSheet.tag = -1;
This ensures that you will ignore it, although the second call is made. This works in my case.
I would like to add: UIAlertController is the correct way to now to use UIAlertViews in iOS8.0+ (as UIAlertView is deprecated) however the bug of being able to select multiple options is somewhat mitigated.
The separate options in the alert view CAN be selected/highlighted at the same time, but the delegate method only seems to fire off one of them. Which one actually triggers off is undetermined, but I can confirm only one is fired off despite two/or more being highlighted if you use multiple fingers.
Related
I'm trying to first have a user enter credit card information and then agree to terms and conditions. Currently, the app is forcing a user to check terms and conditions and then enter credit card information. What is a better method or better way to do this? I am not strong in Objective C.
-(void)textFieldDidBeginEditing:(UITextField *)textField{
if(_agreedToTerms){
if(!_canceledScan){
CardIOPaymentViewController *cardV = [[CardIOPaymentViewController alloc]initWithPaymentDelegate:self];
[cardV setCollectCVV:NO];
//CardIOPaymentViewController *cardV = [[CardIOPaymentViewController alloc]initWithPaymentDelegate:self];
[cardV setCollectExpiry:YES];
// [cardV setCollectPostalCode:YES];
[self presentViewController:cardV animated:YES completion:nil];
}
}else{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Error" message:#"Please agree to terms" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
{
}]];
[self presentViewController:alertController animated:YES completion:nil];
}
}
What is a better method or better way to do this?
You haven't shown us enough code to make a specific recommendation, but in general if you want things to happen in a different order, then you need to change the order in which the code makes those things happen. For example, let's say you have a view controller that asks users to accept terms and conditions and another that lets them enter credit card info. The order in which those view controllers are presented to the user is entirely up to you, and swapping them should be a pretty simple matter: you'll either re-arrange them in the storyboard, or you'll swap their positions in the code that presents them.
My accessibility work on my app continues. The next issue I've discovered is that whenever an alertView appears, voice over only reads out the following
Alert
Alert Title
Even though I believe it's meant to read out the Alert Body as well.
To work around this issue I've had to do the following code
NSString *alertAction = notification.alertAction;
NSString *alertBody = notification.alertBody;
if (UIAccessibilityIsVoiceOverRunning())
{
// TODO - iOS VoiceOver has a bug where it only reads out the alert action, not the body.. combine everything into one
// for now so its read out together
alertAction = [NSString stringWithFormat:#"%#, %#", alertAction, alertBody];
alertBody = nil;
}
UIAlertController* alertController = [UIAlertController alertControllerWithTitle:alertAction
message:alertBody
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
}]];
[visibleViewController presentViewController:alertController animated:YES completion:nil];
To combine the title and message into one string which I use for the title. Clearing out the message.
This does seem to fix the problem, but it feels a bit clunky and obviously looks a little objectionable with so much text in the bold title font.
Anyone come across this issue, or got any other fixes to avoid having to butcher all my alerts this way?
Cheers
I'm working on an old iOS app originally written for iOS 6, and it had some UIActionSheets that needed to be changed, so I've been working to move them over to UIAlertControllers, using UIAlertActions. This has worked perfectly fine on a iPad2 Simulator, however when testing on an iPad Air (the only iPad I have access to) my UIAlertAction, and UIAlertController become nil directly after being created (looked at in the debugger, it receives a pointer on creation, however as soon as it executes the next line it becomes null). Here's a code sample:
//When hovering over the next line, alert has a pointer
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:#"info"
message:#"testmessage"
preferredStyle:UIAlertControllerStyleActionSheet];
//alert pointer is now nil
//test pointer shows up
UIAlertAction* test= [UIAlertAction
actionWithTitle:#"I'm a Button"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action){
[alert dismissViewControllerAnimated: YES completion:nil];
}
];
//test pointer is nil, test2 pointer exists
UIAlertAction* test2 = [UIAlertAction
actionWithTitle:#"I'm a Button"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action){
[alert dismissViewControllerAnimated: YES completion:nil];
}
];
//test2 pointer is nil
[alert addAction:test];
[self presentViewController:alert animated:YES completion:nil]; //app crashes, because I'm trying to present a nil modal.
Any thoughts or help would be much appreaciated!
UIAlertController is for iOS8 upwards. Use UIAlertView and UIActionSheet for prior to iOS8
You are best to check wether your device responds to the class,
if ([UIAlertController class]) {
// use UIAlertController for the action sheets as you have already posted
// For all operating systems (like iOS8 upwards) will land in here.
} else {
// use UIActionSheet - like you already used for iOS6
}
It's not wise to check the operating system deployment number, like if 8.0 etc, checking the if it responds to the class is the proper way to do it.
It prevents a crash means you're not relying on float numbers which are not guaranteed to be reliable, as if they change the way the operating systems is named in future, your code would crash.
I have an issue related to UIAlertView while running our app on iOS 8.
I am showing an alert with title as nil. It was working fine in iOS 7 but now UI looks odd.
I have attached screenshot here.
One solution I found is that when I provide empty string #“” it looks okay. See below screenshot. But I am not sure if the issue I mentioned is bug in beta iOS 8 version or if there is any other better solution. Even with the solution it's not exact as it was in iOS 7.
iOS 7 - showing alert view with title as nil. Screenshot here.
The closest I could get with iOS 8 was by setting the title instead of the message:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Location field required." message:nil delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
It should be noted, however, that UIAlertView is deprecated in iOS 8 and, if you're going to be using separate code paths for iOS 7 and iOS 8, you should be using UIAlertController instead:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Location field required." message:nil preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self dismissViewControllerAnimated:YES completion:nil];
}]];
[self presentViewController:alert animated:YES completion:nil];
I got the same results with both methods.
It has been the best practice for me to use initWithTitle:#"" for UIAlertView, UIActionSheet since iOS 6 because I was facing a design issue during that time when I was using initWithTitle:nil. I tried to find back, I couldn't find it what exactly is the reason.
From your screen shot on iOS 8, I think there is a change of view hierarchy on UIAlertView for iOS 8. I think Auto layout might be implemented on the view hierarachy as well as you can see the messageLabel jump up to the titleLabel.
I can not be sure because the view hierarchy for UIAlertView is private.
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
See: https://developer.apple.com/library/ios/documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
But, I use the code:-
NSLog(#"%#",[self.alertView description]);
Result on iOS 7.1:
<UIAlertView: 0x7fb3c05535b0; frame = (18 263; 284 62); opaque = NO; layer = <CALayer: 0x7fb3c0519810>>
Result on iOS 8.0:
<UIAlertView: 0x7bf64840; frame = (0 0; 0 0); layer = <CALayer: 0x7bf648f0>>
I am not sure why the UIAlertView frame for iOS 8 is (0 0; 0 0);
Like Mike said, I think you should learn to use UIAlertController for iOS 8.
I managed to get decent message alignment without the bold font by:
Setting the title to #"" instead of nil, and
(If IOS8) prepend a "\n" in front of the message.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#""
message:#"\nLocation field required."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
Please try this code.
it is working on my side
xcode version 9.2
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* btn1 = [UIAlertAction
actionWithTitle:#"OKAY"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
}];
[self presentViewController:alert animated:YES completion:nil];
I have a few ViewController subclasses inside a UINavigation controller. I have successfully used UIAlertViews elsewhere in the application, and I know how to set the delegate and include the correct delegate methods, etc.
In a ViewController with a UITableView, I have implemented a 'pull to refresh' with a UIRefreshControl. I have a separate class to manage the downloading and parsing of some XML data, and in the event of a connection error, I post a notification. The view controller containing the table view observes this notification and runs a method where I build and display an alert:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connection Error" message:[[notification userInfo] objectForKey:#"error"] delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
alertView.alertViewStyle = UIAlertViewStyleDefault;
[alertView show];
The alert displays correctly, but the cancelButton is unresponsive - there is no way to dismiss the alert! Putting similar code (identical, but without the notification's userinfo) in the VC's viewDidLoad method creates an alert that behaves normally.
Is the refresh gesture hogging first responder or something? I have tried [alertView becomeFirstResponder]. I would be grateful for any advice…
Update: screenshot included… is this the right info? (can't embed this image for lack of reputation) http://i.stack.imgur.com/4CGqS.png
Edit
It seems like you have a deadlock or your thread is stuck waiting. You should look at your code and see what causes this.
Original answer which lead to update in OP
Make sure the alert is shown on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
//Open alert here
});
This isn't a solution per se, but I might try two quick things as you troubleshoot:
1) Hardcode some text in the UIAlert, rather than passing in the notification object. See if there is any change in behavior.
2) Try adding another button to the alert and an accompanying method to catch it. So you'll see if the delegate is getting ny buttons messages at all.
Try adding a tag to the alertView
alertView.tag = 0;
Then create the method alertView:clickedButtonAtIndex: in the view controller.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 0) {
[alertView dismissWithClickedButtonIndex:0 animated:YES];
}
}