in my app i have tableview and for clear all cells i given an action to clear button.
button work correctly and all cell deleted. but i want to give a option to user to canceled
i used UIAlertController class and its work only one time after i chosen delete button.(its work every time if i chosen cancel button)
warning in console:
Warning:
Attempt to present UIAlertController on HistoryViewController which is already presenting UINavigationController
- (IBAction)showNormalActionSheet:(id)event
{
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
}];
UIAlertAction *resetAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Clear all recent", #"Reset action")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action)
{
[self onDeleteClick];
NSLog(#"Reset action");
}];
[alertController addAction:resetAction];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
//- (IBAction)onDeleteClick:(id) event {
- (void)onDeleteClick {
linphone_core_clear_call_logs([LinphoneManager getLc]);
[tableController loadData];
editButton.hidden = ([tableView.dataSource tableView:tableView numberOfRowsInSection:0] == 0);
if(editButton.selected) {
[editButton toggle];
[self onEditClick:nil];
}
}
Try to use the code from the first answer in the link (by Kampai):
How to use UIAlertController to replace UIActionSheet?
However, the completion handler is not even called in my code.
The alert action sheet can be dismissed after pressing both buttons but nothing inside the completion handler works.
Any idea what might be the problem? I'm new to using completion handler and have tried to find answers online, but few have the same problem as mine.
- (IBAction)takePhotoButtonPressed:(UIButton *)sender {
pressedButtonTagNumber = sender.tag;
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel button tappped
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Take a Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(#"!");
// Take a Photo button tapped
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"0"); // NOT CALLED
// Initialize UIImagePickerController
UIImagePickerController *takePhotoImagePickerController = [[UIImagePickerController alloc] init]; takePhotoImagePickerController.delegate = self;
takePhotoImagePickerController.allowsEditing = YES;
NSLog(#"1");
// Check and assign image source
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSLog(#"2");
UIAlertController *noCameraErrorSheet = [UIAlertController alertControllerWithTitle:#"Camera is not available" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[noCameraErrorSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel button tappped
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
} else {
takePhotoImagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
// Present UIImagePickerController
[self presentViewController:takePhotoImagePickerController animated:YES completion:NULL];
}
}];
}]];
Solution:
#Paulw11 solution works great:
1) No need to dismissViewController for UIAlertController.
2) Cannot call a new UIAlertController if the one wraps it is dismissing (obviously).
3) Better to check and disable the button in advance.
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
}]];
UIAlertAction *takePhotoActionButton = [UIAlertAction actionWithTitle:#"Take Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self takePhoto];
}];
UIAlertAction *uploadPhotoActionButton = [UIAlertAction actionWithTitle:#"Upload from Library" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self uploadPhoto];
}];
// Disable take a photo button if source not available
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[takePhotoActionButton setEnabled:FALSE];
}
// Disable upload a photo button if source not available
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
[uploadPhotoActionButton setEnabled:FALSE];
}
[actionSheet addAction:takePhotoActionButton];
[actionSheet addAction:uploadPhotoActionButton];
// Present action sheet.
[self presentViewController:actionSheet animated:YES completion:nil];
You don't need to call dismissViewController:animated: from within the action handler to remove the alert. UIAlertController calls this to dismiss itself prior to invoking the action handler code.
In your action handler you simply need to execute whatever should be done when that action is selected:
In this case:
In your cancel action you don't need to do anything
In your "take a photo" action you take a photo
Also, from a user experience point of view, it may be better to disable the "take a photo" or present an alert as soon as they select it rather than issuing an alert after they have attempted to take a photo; in other words indicate the problem earlier rather than later
I am working on a universal app for the iPhone 6S / 6S Plus / and iPad form factors. Normally, presenting actionsheets / alertviews on an iPhone-only app is a simple manner. But my application crashes when I attempt to present these on an iPad, returning the following error:
"Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet. The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'"
It is my understanding that I must display a popover when the application is running on an iPad instead of a conventional actionsheet. For the sake of context, the action sheet is presented by a button in a custom cell, which is in a tableview.
What is the best way to handle UIAlertControllers / Action Sheets / UIPopoverControllers in a universal app?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *titleToUse = #"";
// switch (self.openGroup) {
// case 0:
// titleToUse = [self.deviceListData[indexPath.row] valueForKey:#"deviceName"];
// break;
//
// case 1:
// titleToUse = [self.computersData[indexPath.row] valueForKey:#"deviceName"];
// break;
//
// case 2:
// titleToUse = [self.mobileData[indexPath.row] valueForKey:#"deviceName"];
// break;
//
// case 3:
// titleToUse = [self.smartData[indexPath.row] valueForKey:#"deviceName"];
// break;
//
// default:
// break;
// }
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:titleToUse message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel button tappped.
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Get More Info" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
AlertDetailModal *alertDetail = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"alertDetailModal"];
alertDetail.delegate = self;
alertDetail.securityLevel = self.securityLevel;
UINavigationController *modalNavCon = [[UINavigationController alloc] initWithRootViewController:alertDetail];
[self presentViewController:modalNavCon animated:YES completion:nil];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Bandwidth Profile" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Alert Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Security Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Unblock Connection" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
// Present action sheet.
[self presentViewController:actionSheet animated:YES completion:nil];
}
The fact that its universal is irrelevant. You setup the alert controller's popoverPresentationController the same regardless. It will then display properly on all devices.
In this case you should set the sourceView to tableView and sourceRect to the rect for the selected row.
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:titleToUse message:nil preferredStyle:UIAlertControllerStyleActionSheet];
actionSheet.popoverPresentationController.sourceView = tableView;
actionSheet.popoverPresentationController.sourceRect = [tableView rectForRowAtIndexPath:indexPath];
I am using Objective-C to write about some UIAlertController code.
I have more buttons, but the buttons will show different UIAlertControllers and deal with different UIAlertAction handler .
So I want to create one UIAlertController,and UIAlertAction.
Like below:
-(void) initAlert{
alertController = [UIAlertController alertControllerWithTitle:#"hint" message:#"count down alert" preferredStyle:UIAlertControllerStyleAlert];
doneAction = [UIAlertAction actionWithTitle:#"okey" style:UIAlertActionStyleDefault handler:
^(UIAlertAction *action) {
NSLog(#"show log");
}];
[alertController addAction:doneAction];
}
-(void) showAlert{
[self presentViewController:alertController animated:YES completion:nil];
}
Then I want to using different button IBAction to call the showAlert method, and set different UIAlertController title, UIAlertAction title and deal different alertAction handler.
But I encounter some problems.
I call the method in different button like below:
- (IBAction)btn1Action:(UIButton *)sender {
alertController.title = #"controller 1";
alertController.message = #"message1";
[self showAlert];
}
- (IBAction)btn2Action:(UIButton *)sender {
alertController.title = #"controller 2";
alertController.message = #"message2";
[self showAlert];
}
I don't know how to change the UIAlertAction title with the same doneAction, I search some data show the UIAlertAction is readyonly property.
So have any other methods to change UIAlertAction title? or can we delete the UIAlertController addAction: method to add other UIAlertAction?
And how can I pass different UIAlertAction handler to AlertAction for the same UIAlertController to use?
Thank you very much.
UIAlertController should not be used multiple times. Just use a new UIAlertController instance each time you want to pop up the alert.
- (IBAction)btn1Action:(UIButton *)sender {
[self showAlert:#"Controller 1" message:#"Message 1" handler:^(UIAlertAction *action) {
NSLog(#"btn1Action");
}];
}
- (IBAction)btn2Action:(UIButton *)sender {
[self showAlert:#"Controller 2" message:#"Message 2" handler:^(UIAlertAction *action) {
NSLog(#"btn2Action");
}];
}
-(void)showAlert:(NSString*)alertTitle message:(NSString*)message handler:(void (^ __nullable)(UIAlertAction *action))handler {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:alertTitle message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * doneAction = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:handler];
[alertController addAction:doneAction];
[self presentViewController:alertController animated:YES completion:nil];
}
I am maintaining an old iOS project which based on SDK 6.0.
A method on this project called
-(void) showComboBox:(UIView*)view:withOptions:(NSDictionary*)options
is used to show a combo box. To achieve the goal, it used UIActionSheet, which is deprecated on iOS8.
My solution is like this:
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8) {
UIAlertController* alertController = [UIAlertController
alertControllerWithTitle:#"title"
message:#"message"
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* item = [UIAlertAction actionWithTitle:#"item"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//do something here
//inform the selection to the WebView
...
[alertController dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[alertController dismissViewControllerAnimated:YES completion:nil];
}];
[alertController addAction:item];
[alertController addAction:cancelAction];
//I am not sure whether it's the right way
if ([view.nextResponder isKindOfClass:UIViewController.class]) {
UIViewController* vc = (UIViewController*)view.nextResponder;
[vc presentViewController:alertController animated:YES completion:nil];
}
Is that a proper solution?
This is what I mostly concern about: UIAlertController needs to be added to a UIViewController but I can only get the pointer of the UIView, so I used view.nextResponder to get what I want, but it's that a good way?
I have used following code to show action sheet using UIAlertViewController and it works perfect.
Swift
let alert = UIAlertController(title: "Action Title", message: "Action Message", preferredStyle: .actionSheet)
let action = UIAlertAction(title: "Item", style: .default) {
UIAlertAction in
// Write your code here
}
alert.addAction(action)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
// It will dismiss action sheet
}
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
Objective C
- (IBAction)buttonClicked:(id)sender {
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:#"Action Sheet" message:#"Using the alert controller" preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel button tappped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Delete" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
// Distructive button tapped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// OK button tapped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
// Present action sheet.
[self presentViewController:actionSheet animated:YES completion:nil];
}
Edit:
You need to get UIViewController object here. You can set global variable or call a delegate method, or you can use notification to get view controller object in this code.
and last line in above code will be like.
[self.viewController presentViewController:actionSheet animated:YES completion:nil];
self.viewController is a global variable which will be set before you actually get this view.
Because the approach you are following now using view.nextResponder. I'm afraid that it may not work.
I have used action sheet for changing profile picture. I followed Kampai approach, just removed dismissviewController call since it was kicking me out of a view when pressing Cancel or photo selection view
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Cancel button tappped do nothing.
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Take photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// take photo button tapped.
[self takePhoto];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Choose photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// choose photo button tapped.
[self choosePhoto];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Delete Photo" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
// Distructive button tapped.
[self deletePhoto];
}]];
Swift update -
let actionSheet = UIAlertController.init(title: "Please choose a source type", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction.init(title: "Take Photo", style: UIAlertActionStyle.default, handler: { (action) in
self.openCamera()
}))
actionSheet.addAction(UIAlertAction.init(title: "Choose Photo", style: UIAlertActionStyle.default, handler: { (action) in
self.showPhotoLibrary()
}))
actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: UIAlertActionStyle.cancel, handler: { (action) in
// self.dismissViewControllerAnimated(true, completion: nil) is not needed, this is handled automatically,
//Plus whatever method you define here, gets called,
//If you tap outside the UIAlertController action buttons area, then also this handler gets called.
}))
//Present the controller
self.present(actionSheet, animated: true, completion: nil)
Swift 4
let alert = UIAlertController(title: "Select One", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet)
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Export", style: UIAlertActionStyle.default, handler: { (action) in
// TODO: Export wordlist
}))
alert.addAction(UIAlertAction(title: "Import", style: UIAlertActionStyle.default, handler: { (action) in
// TODO: Import wordlist
}))
self.present(alert, animated: true, completion: nil)
You can use view.window.rootViewController instead. If you don't care about presenter it is fine.
While it may look very simple, there is a nasty issue with using UIAlertController. It is memory leaks prone. In order to test if you have the issue, just put a breakpoint at your view controller's dealloc method and see if it's deallocated properly.
I was looking for a solution for quite some time and here is how I use an alert controller in my app.
+ (void)alertWithPresenting:(UIViewController *)presenting title:(NSString *)title
text:(NSString *)text buttons:(NSArray *)buttons
handler:(void (^)(UIAlertAction *action, NSUInteger index))handler
{
UIAlertController *alert = [UIAlertController
alertControllerWithTitle:title message:text
preferredStyle:UIAlertControllerStyleAlert];
__weak __typeof(alert) weakAlert = alert;
for (NSString *title in buttons) {
UIAlertActionStyle style = UIAlertActionStyleDefault;
if ([title isEqualToString:[L10n cancelButton]])
style = UIAlertActionStyleCancel;
else if ([title isEqualToString:[L10n deleteButton]])
style = UIAlertActionStyleDestructive;
else if ([title isEqualToString:[L10n archiveButton]])
style = UIAlertActionStyleDestructive;
UIAlertAction *action = [UIAlertAction actionWithTitle:title style:style handler:^(UIAlertAction *action) {
if (handler != nil)
handler(action, [buttons indexOfObject:action.title]);
[weakAlert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:action];
}
[presenting presentViewController:alert animated:YES completion:nil];
}
This is not all. Here is an example of how you use it in your view controller. In my case its tableview with search, so presenting controller may be different.
- (void) deleteCases:(NSArray *)selectedRows
{
NSString *text = NSLocalizedStringWithDefaultValue(#"cases.delete.alert.text",
#"Localizable", [NSBundle mainBundle],
#"Deleted cases cannot be restored. Continue with delete?",
#"Delete alert text");
NSString *title = NSLocalizedStringWithDefaultValue(#"cases.delete.alert.title",
#"Localizable", [NSBundle mainBundle],
#"Delete cases", #"Detete alert title");
UIViewController *presenting = self.searchController.active ? self.searchController : self;
__weak __typeof(presenting) weakPresenting = presenting;
__weak __typeof(self) weakSelf = self;
[YourClassName alertWithPresenting:weakPresenting title:title text:text
buttons:#[[L10n deleteButton], [L10n cancelButton]]
handler:^(UIAlertAction *action, NSUInteger index)
{
if (action.style == UIAlertActionStyleDestructive) {
__typeof(weakSelf) strongSelf = weakSelf;
// Perform your actions using #strongSelf
}
}];
}