Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIAlertController and UIAlertControllerStyleActionSheet customization

I'm trying to create a custom UIAlertController with style UIAlertControllerStyleActionSheet (formerly UIActionSheet) and I'm encountering so much trouble to simply customize it. I want my UIAlertAction to contain an image on the left and decrease buttons size.

I know it's feasable :

enter image description here

(that's exactly what I'm seeking for)

I searched for hours but I can't find any tut's on the internet that can help me to achieve that simple task. Plus, as UIActionSheet is deprecated since IOS 9, anytime I try to find a solution it's not usable anymore. Also, i read that UIAlertController is not intended to be subclassed...

Does anyone know how to achieve that light customization of my alert? Do I have to sublass a UIView to make my own alertsheet?

like image 727
Olympiloutre Avatar asked Feb 05 '23 16:02

Olympiloutre


2 Answers

This solution does use private APIs/properties. Based my research and experience, this is the only way I know that you can customize UIAlertController. If you look at the public header, UIAlertContoller has little room for customization. However, this solution is commonly used among developers after the launch of UIAlertController in iOS 8. You can totally rely on depedencies from Github. I hope my answer can solve your problem. I believe this is what you are looking for, the result loooks like this:

First, you have to create a UIAlertController

UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Alert Title" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];

Custom font! even for just certain characters.

NSMutableAttributedString *hogan = [[NSMutableAttributedString alloc] initWithString:@"Presenting the great... StackOverFlow!"];
[hogan addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:30.0] range:NSMakeRange(24, 11)];
[alertVC setValue:hogan forKey:@"attributedTitle"];

Now, let's create a UIAlertAction, where you can add action handlers and also add icons.

UIAlertAction *button = [UIAlertAction actionWithTitle:@"First Button"
                                                 style:UIAlertActionStyleDefault
                                               handler:^(UIAlertAction *action){
                                                   //add code to make something happen once tapped
                                               }];
UIAlertAction *button2 = [UIAlertAction actionWithTitle:@"Second Button"
                                                 style:UIAlertActionStyleDefault
                                               handler:^(UIAlertAction *action){
                                                   //add code to make something happen once tapped
                                               }];

Here, you add the icon to the AlertAction. For me, you have to specify UIImageRenderingModeAlwaysOriginal

[button setValue:[[UIImage imageNamed:@"image.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forKey:@"image"];

[alertVC addAction:button2];
[alertVC addAction:button];

Remember to present the ViewController

[self presentViewController:alertVC animated:YES completion:nil];

enter image description here

like image 66
NeilNie Avatar answered Feb 25 '23 09:02

NeilNie


For those who could be interested in how I dit to overcome this, I post my solution:

I finally gave up on UIAlertController, which is not intended to be subclassed. Instead, in my ViewController, I created a method that programmatically re-create the same effect and rendering :

  1. Create an UIView that occupies the whole screen an set it's alpha to ~0.7. (This has two objectives, a esthetical one and a physical one the hide the current view, avoiding buttons behind the custom Alert to be clicked)

  2. Create UIButtons and place it manually in the current view.

  3. Add this foregroundView and buttons to the current view and when button is clicked simply remove it.

Example code :

-(void) displayAlertChoice{

    // create foreground layer to hide current view
    foregroundLayer = [[UIView alloc] initWithFrame:CGRectMake(0,0,screenWidth,screenHeight)];
    foregroundLayer.backgroundColor = [UIColor blackColor];
    foregroundLayer.alpha = 0.0;
    [self.view addSubview:foregroundLayer];
    [UIView animateWithDuration:0.2 animations:^{foregroundLayer.alpha = 0.7;}]; // animation de son apparition (alpha)

    // Hide foreground layer when clicked
    UITapGestureRecognizer *tapForegroundLayer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickForegroundLayer:)];
    [foregroundLayer addGestureRecognizer:tapForegroundLayer];


    // CancelButton
    cancelButton= [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [cancelButton setFrame:CGRectMake(marginWidth,
                                      2*screenHeight - buttonHeight - marginHeight, // OFF SCREEN
                                      buttonWidth,
                                      buttonHeight)];
    [cancelButton setTitle:@"Annuler" forState:UIControlStateNormal];
    [cancelButton addTarget:self action:@selector(alertCancelButton:) forControlEvents:UIControlEventTouchUpInside];
    [cancelButton setBackgroundColor:[UIColor whiteColor]];
    cancelButton.layer.cornerRadius = 8;
    [self.view addSubview:cancelButton];
    [UIView animateWithDuration:0.2 animations:^{ // animation de son apparition (fram up)
        [cancelButton setFrame:CGRectMake(marginWidth,
                                          screenHeight - buttonHeight - marginHeight, // ON SCREEN
                                          buttonWidth,
                                          buttonHeight)];
    }];
}

This code is a bit more complex than just adding view and buttons, in order to recreate animation when the Alert showes up :

  • Using [UIView animateWithDuration: animations:^{}]; i was able to gradually obscure the foreground view and to make the cancelbutton showing up from the bottom of the screen.
  • Note: you cannot add UIButtons directly to the foregroundView (here called foregroundLayer), otherwise they will herit of the 0.7 alpha we set earlier
  • I added a gesture recognizer selector to the foregroundView that has the same effect of the cancelButton selector : it remove all created objects from superview. This way, when the user clicks outside the edges of the list, it closes the custom AlertView.
like image 28
Olympiloutre Avatar answered Feb 25 '23 09:02

Olympiloutre