As everyone's aware, UIAlertView is deprecated now and Apple wants us to use the new UIAlertController with the style UIAlertControllerStyleAlert. However, using the UIAlertControllerStyleAlert you cannot trigger the UIAlertAction with style UIAlertActionStyleCancel by tapping outside the alert view.
Does anyone know of a way to dismiss the alert view by tapping outside of it?
Cheers
You can add a separate cancel action with style UIAlertActionStyleCancel for the alertViewController so that when user taps outside, you would get the callback.
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Alert Title" message:#"A Message" preferredStyle:UIAlertControllerStyleActionSheet];
[alertController addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// Called when user taps outside
}]];
Try this code:
- (void)viewDidLoad {
[super viewDidLoad];
[self button];
}
- (void) button {
UIButton * AlertButton = [UIButton buttonWithType:UIButtonTypeSystem];
[AlertButton setTitle:#"Button" forState:UIControlStateNormal];
AlertButton.frame = CGRectMake((self.view.frame.size.width/2) - 50 , (self.view.frame.size.height/2) - 25, 100, 50);
[AlertButton addTarget:self action:#selector(Alert) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:AlertButton];
}
- (void)Alert {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Alert Title" message:#"Alert Message" preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController: alert animated: YES completion:^{ alert.view.superview.userInteractionEnabled = YES; [alert.view.superview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(DismissAlertByTab)]]; }];
}
- (void)DismissAlertByTab
{
[self dismissViewControllerAnimated: YES completion: nil];
}
Related
I have made an ActionSheet like this:
Now I want change the color Cancel And Delete to give the feel like they are disabled.
I am able to change the color of all buttons but not individuals.
I have tried using categories but I am keep getting warining that ActionSheet is Depreciated. And the code didn't work also.
I have written this Code:
- (void)setButton:(NSInteger)buttonIndex toState:(BOOL)enabled {
for (UIView* view in self.subviews)
{
if ([view isKindOfClass:[UIButton class]])
{
if (buttonIndex == 0) {
if ([view respondsToSelector:#selector(setEnabled:)])
{
UIButton* button = (UIButton*)view;
button.enabled = enabled;
}
}
buttonIndex--;
}
}
}
Is it possible to give disabled look to some of the buttons of Action sheet presented via AlertViewController.
Below is the my code to present Action Sheet:
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:#"Action Sheet" message:#"Using the alert controller" preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet.view setTintColor:[UIColor redColor]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// Cancel button tappped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Delete" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// Distructive button tapped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:#"Group 1" style:UIAlertActionStyleCancel 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];
What you can do is use the setEnabled property of UIAlertAction.
This will render your button as 'greyed out'.
I have modified a piece of your code to incorporate this change:
UIAlertAction *action = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// OK button tapped.
[self dismissViewControllerAnimated:YES completion:^{
}];
}];
[action setEnabled:NO];
[actionSheet addAction:action];
Ofcourse, you can't click on the button after you disable it.
Other options would involve using UIAlertActionStyle.
There are three:
UIAlertActionStyleDefault,
UIAlertActionStyleCancel,
UIAlertActionStyleDestructive
UIAlertActionStyleDefault will render your button with tintColor.
UIAlertActionStyleCancel will render your button on the bottom of the sheet.
UIAlertActionStyleDestructive renders your button with a 'red' color. (Think: Delete button)
Get the actions array and change the state for the action you want.
This way you can change the state even after adding the action.
Here is the sample code.
NSArray* actionlist = actionSheet.actions;
UIAlertAction * action2 = [actionlist objectAtIndex:1];
action2.enabled = false;
// Present action sheet.
[self presentViewController:actionSheet animated:YES completion: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
When I am trying to present alert view controller my app is crashing.
-(void)setupAlertCtrl{
self.alertCtrl=[UIAlertController alertControllerWithTitle:#"Select Image"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
}
- (IBAction)selectImagePressed {
**[self presentViewController:self.alertCtrl
animated:YES
completion:nil];//this funcanility is breaking**
}
Exception :
libc++abi.dylib: terminating with uncaught exception of type NSException
libc++abi.dylib: terminating with uncaught exception of type NSException
This could also happen if you've wired up a button to an IBAction that doesn't exist anymore (or has been renamed)"
If you're running into this problem than go to your storyboard, RIGHT click on the yellow box icon (view controller) at the top of the phone outline and DELETE the outlet(s) with yellow flags.
What happens in instances like this is you probably named an action, then renamed it.
Hope this will help you.
This code will definitely help you.
I suspect you are using iPad(normal action sheet crashes in iPad)
-(void)setupAlertCtrl
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:#"Your message" preferredStyle:UIAlertControllerStyleActionSheet];
// Remove arrow from action sheet.
[alertController.popoverPresentationController setPermittedArrowDirections:0];
//For set action sheet to middle of view.
CGRect rect = self.view.frame;
rect.origin.x = self.view.frame.size.width / 20;
rect.origin.y = self.view.frame.size.height / 20;
alertController.popoverPresentationController.sourceView = self.view;
alertController.popoverPresentationController.sourceRect = rect;
[self presentViewController:alertController animated:YES completion:nil];
}
else
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:#"Your message" preferredStyle:UIAlertControllerStyleActionSheet];
[self presentViewController:alert animated:YES completion:nil];
}
}
you have to call the below function in view did load
-(void)setupAlertCtrl
Then you can open the alert on button click
You should have to write it like,
-(void)setupAlertCtrl
{
self.alertCtrl=[UIAlertController alertControllerWithTitle:#"Select Image"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[self presentViewController:self.alertCtrl animated:YES completion:nil];
}
-(IBAction)selectImagePressed
{
[self setupAlertCtrl];
}
Hope this will help you.
UIAlertController is a functionally identical, block-based replacement for both UIAlertView and UIActionSheet. Switching between an alert or action sheet is done by setting the preferred style when creating the controller.
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
I hope this will help you...
Its not crashing,
Just call the SetupAlertCntrl in viewDidLoad method.
Its working fine
You need to check your alertController exist or not first, if not let created it first. One more thing, UIAlertControl only can present from UIViewController so, make sure self in selectImagePressed function is kind of UIViewController.
- (IBAction)selectImagePressed {
if(self.alertController == nil) {
[self setupAlertCtrl];
}
[self presentViewController:self.alertCtrl
animated:YES
completion:nil];//this funcanility is breaking**
}
Try below code:
-(void)setupAlertCtrl
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Choose Profile Image " message:#" " preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* cameraAction = [UIAlertAction actionWithTitle:#"Take Photo" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(#"Camera Action");
}];
[alertController addAction:cameraAction];
UIAlertAction* gallaryAction = [UIAlertAction actionWithTitle:#"Gallary" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(#"Gallary Action");
}];
[alertController addAction:gallaryAction];
UIAlertAction* Cancel = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:Cancel];
[self presentViewController:alertController animated:YES completion:nil];
}
// Button Click action
- (IBAction)selectImagePressed:(id)sender
{
[self setupAlertCtrl];
}
hope this will help you.
Currently I have a button that makes a draggable UIView with a subview of a UIButton. When I long press that UIButton, an alert view comes up and I have two buttons, a delete button and a cancel button. The delete button is supposed to delete the last long pressed UIButton, however it deletes the most recently made UIButton.
I would like for the delete button on the alert view to delete the last long pressed UIButton.(not the most recently created) I have tried different if statements, but this is what I have so far. Here is my code for my .m file:
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateBegan ) {
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"Would you like to delete this rep?"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* deleteButton = [UIAlertAction
actionWithTitle:#"Delete"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[_buttonField removeFromSuperview];
[alert dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* cancelButton = [UIAlertAction
actionWithTitle:#"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:deleteButton];
[alert addAction:cancelButton];
[self presentViewController:alert animated:YES completion:nil];
}
}
- (void)panWasRecognized:(UIPanGestureRecognizer *)panner {
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.buttonField addGestureRecognizer:longPress];
_draggedView = panner.view;
CGPoint offset = [panner translationInView:_draggedView.superview];
CGPoint center = _draggedView.center;
_draggedView.center = CGPointMake(center.x + offset.x, center.y + offset.y);
_draggedView.layer.borderWidth = 2.0f;
_buttonField.layer.borderColor = [UIColor blackColor].CGColor;
[_buttonField setTintColor:[UIColor magentaColor]];
// Reset translation to zero so on the next `panWasRecognized:` message, the
// translation will just be the additional movement of the touch since now.
[panner setTranslation:CGPointZero inView:_draggedView.superview];
}
}
- (IBAction)addRepButton:(UIBarButtonItem *)newRep {
self.labelCounter++;
buttonCount ++;
if (buttonCount >= 0 )
{
_buttonField = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 28, 28)];
[_buttonField setTitle:[NSString stringWithFormat:#"%i", self.labelCounter]forState:UIControlStateNormal];
[_buttonField setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
_buttonField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
_buttonField.userInteractionEnabled = YES;
_buttonField.layer.cornerRadius = 14;
_buttonField.layer.borderColor = [UIColor blackColor].CGColor;
_buttonField.layer.borderWidth = 2.0f;
_buttonField.titleLabel.font = [UIFont systemFontOfSize: 18];
UIPanGestureRecognizer *panner = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(panWasRecognized:)];
[_buttonField addGestureRecognizer:panner];
[self.view addSubview:_buttonField];
}
}
How do I go about making the delete button remove the most recently long pressed _buttonField?
You are saying:
[_buttonField removeFromSuperview];
Well, as your loop shows (inside addRepButton), _buttonField is the most recently added button, because every time you add a button, you set it to that button. So what is happening is exactly what you are saying to happen.
I presume, although it is a little hard to tell from your code, that the button you want to delete is the one whose long press gesture recognizer this is — that is, gesture.view.
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateBegan ) {
//Update
UIButton *buttonPressedLatest;
UIView *ifBtnPressed = gesture.view;
if([ifBtnPressed isKindOfClass:[UIButton class]]){
buttonPressedLatest = (UIButton *)ifBtnPressed;
}
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"Would you like to delete this rep?"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* deleteButton = [UIAlertAction
actionWithTitle:#"Delete"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[buttonPressedLatest removeFromSuperview];
[alert dismissViewControllerAnimated:YES completion:nil];
}];
UIAlertAction* cancelButton = [UIAlertAction
actionWithTitle:#"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:deleteButton];
[alert addAction:cancelButton];
[self presentViewController:alert animated:YES completion:nil];
}
}
Try this once and tell me if this works.
I have an application in which I need to display a popup to pick a date and write that date back to the view controller.
I have searched online a lot but wasn't successful in finding out a relevant solution.
Any kind of help is much appreciated.
Do not use any kind of UIAlertView or UIAlertController. Make your own view and pop it up (using, probably, a presented view controller). The view can be small, like an date picker, and you can put a shadow view behind it, like an alert does.
This is my Code :
- (id)initWithDatePicker:(NSString*)title parentView:(UIView*)parentView {
self = [super init];
if (self) {
datePickerView = [[UIDatePicker alloc] init];
datePickerView.datePickerMode = UIDatePickerModeDateAndTime;
if (IS_IOS8_AND_UP) {
alertViewController = [UIAlertController
alertControllerWithTitle:EMPTY_STRING
message:title
preferredStyle:UIAlertControllerStyleActionSheet];
UIView* aboveBlurLayer = alertViewController.view.subviews[0];
[aboveBlurLayer addSubview:datePickerView];
[aboveBlurLayer setWidth:SCREEN_WIDTH - 16];
[datePickerView setWidth:SCREEN_WIDTH - 16];
[alertViewController.view setWidth:SCREEN_WIDTH - 16];
[alertViewController.view setBackgroundColor:[UIColor whiteColor]];
UIAlertAction* alertAction =
[UIAlertAction actionWithTitle:EMPTY_STRING
style:UIAlertActionStyleDefault
handler:nil];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[datePickerView setBackgroundColor:[UIColor whiteColor]];
[aboveBlurLayer addSubview:datePickerView];
} else {
actionSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
[actionSheet addSubview:datePickerView];
}
[self addToolBar];
isDatePicker = YES;
parent = parentView;
}
return self;
}
On Tool bar I have two buttons Done and Cancel
On Done i send back a call via delegate with selected date and on cancel I dismiss.
This code is for both iOS 7 and iOS 8