Simon McNeil
Simon McNeil

Reputation: 463

UIAlertController as child view controller not responding to tap

So I have a UIAlertController which needs to be presented on top of another view controller, so a child view controller. I can get the child view controller to display on top of the parent no problem, but I can't get the alert buttons to respond when I bring this child view controller to the front.

UIAlertController* alert = [AlertHelper createAlertWithTitle:title
                                                                 message:message
                                                                 cancelButton:nil
                                                                 continueButtonText:Ok
                                                                 continueAction:nil
                                                                 cancelAction:nil];
                        
alert.view.translatesAutoresizingMaskIntoConstraints = false;

[self addChildViewController:alert];
alert.view.frame = self.view.bounds;
[self.view addSubview:alert.view];
[alert didMoveToParentViewController:self];
[alert.view.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
[alert.view.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES;

Upvotes: 0

Views: 93

Answers (1)

DonMag
DonMag

Reputation: 77637

UIAlertController is a specialized controller that makes use of UIAlertAction.

Worth noting from Apple's docs:

Important

The UIAlertController class is intended to be used as-is and doesn’t support subclassing. The view hierarchy for this class is private and must not be modified.

While you may not be subclassing it (you didn't provide your AlertHelper code), you're clearly not using it "as-is."

Instead, you probably want to design your own "simulated" alert controller.

Here's a quick example...


AlertHelper.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface AlertHelper : NSObject

+ (UIAlertController *)createAlertWithTitle:(NSString *)title
                                    message:(NSString *)message
                               cancelButton:(NSString *)cancel
                         continueButtonText:(NSString *)ok
                             continueAction:(UIAction *)continueAction
                               cancelAction:(UIAction *)cancelAction;

@end

NS_ASSUME_NONNULL_END

AlertHelper.m

#import "AlertHelper.h"

@implementation AlertHelper

+ (UIViewController *)createAlertWithTitle:(NSString *)title
                                    message:(NSString *)message
                               cancelButton:(NSString *)cancel
                         continueButtonText:(NSString *)ok
                             continueAction:(UIAction *)continueAction
                               cancelAction:(UIAction *)cancelAction;
{
    UIViewController *vc = [UIViewController new];
    vc.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.25];
    
    UIView *bkg = [UIView new];
    bkg.clipsToBounds = YES;
    bkg.backgroundColor = [UIColor systemBackgroundColor];
    bkg.layer.cornerRadius = 12.0;
    
    UILabel *titleLabel = [UILabel new];
    titleLabel.font = [UIFont systemFontOfSize:17.0 weight:UIFontWeightSemibold];
    titleLabel.numberOfLines = 0;
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.text = title;
    
    UILabel *msgLabel = [UILabel new];
    msgLabel.font = [UIFont systemFontOfSize:13.0 weight:UIFontWeightRegular];
    msgLabel.numberOfLines = 0;
    msgLabel.textAlignment = NSTextAlignmentCenter;
    msgLabel.text = message;

    UIButton *cancelButton = [UIButton systemButtonWithPrimaryAction:cancelAction];
    UIButton *okButton = [UIButton systemButtonWithPrimaryAction:continueAction];
    
    cancelButton.layer.borderColor = [UIColor systemBlueColor].CGColor;
    okButton.layer.borderColor = [UIColor systemBlueColor].CGColor;
    cancelButton.layer.borderWidth = 1.0;
    okButton.layer.borderWidth = 1.0;

    for (UIView *v in @[bkg, titleLabel, msgLabel, okButton, cancelButton]) {
        v.translatesAutoresizingMaskIntoConstraints = NO;
    }
    for (UIView *v in @[titleLabel, msgLabel, okButton, cancelButton]) {
        [bkg addSubview:v];
    }
    [vc.view addSubview:bkg];
    
    [NSLayoutConstraint activateConstraints:@[
        
        [titleLabel.topAnchor constraintEqualToAnchor:bkg.topAnchor constant:20.0],

        [titleLabel.leadingAnchor constraintEqualToAnchor:bkg.leadingAnchor constant:8.0],
        [titleLabel.trailingAnchor constraintEqualToAnchor:bkg.trailingAnchor constant:-8.0],
        
        [msgLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:4.0],
        [msgLabel.leadingAnchor constraintEqualToAnchor:bkg.leadingAnchor constant:8.0],
        [msgLabel.trailingAnchor constraintEqualToAnchor:bkg.trailingAnchor constant:-8.0],
        
        [cancelButton.topAnchor constraintEqualToAnchor:msgLabel.bottomAnchor constant:20.0],
        [cancelButton.leadingAnchor constraintEqualToAnchor:bkg.leadingAnchor constant:-1.0],

        [okButton.topAnchor constraintEqualToAnchor:cancelButton.topAnchor constant:0.0],
        [okButton.trailingAnchor constraintEqualToAnchor:bkg.trailingAnchor constant:1.0],

        [cancelButton.bottomAnchor constraintEqualToAnchor:bkg.bottomAnchor constant:1.0],
        
        [cancelButton.trailingAnchor constraintEqualToAnchor:okButton.leadingAnchor constant:1.0],
        [cancelButton.widthAnchor constraintEqualToAnchor:okButton.widthAnchor],

        [cancelButton.heightAnchor constraintEqualToConstant:40.0],
        [okButton.heightAnchor constraintEqualToAnchor:cancelButton.heightAnchor],
        
        [bkg.widthAnchor constraintEqualToConstant:274.0],
        [bkg.centerXAnchor constraintEqualToAnchor:vc.view.centerXAnchor],
        [bkg.centerYAnchor constraintEqualToAnchor:vc.view.centerYAnchor],

    ]];

    return vc;
}

@end

and an example view controller:

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@end

ViewController.m

#import "ViewController.h"
#import "AlertHelper.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor systemYellowColor];

    UIAction *act = [UIAction actionWithTitle:@"Show Fake Alert VC" image:nil identifier:nil handler:^(UIAction * _Nonnull action) {
        [self showFakeAlertVC];
    }];

    UIButton *btn = [UIButton systemButtonWithPrimaryAction:act];
    btn.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
    btn.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:btn];
    
    UILayoutGuide *g = self.view.safeAreaLayoutGuide;
    
    [NSLayoutConstraint activateConstraints:@[
        [btn.topAnchor constraintEqualToAnchor:g.topAnchor constant:60.0],
        [btn.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:80.0],
        [btn.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-80.0],
    ]];
}

- (void)removeFakeAlertVC {
    UIViewController *vc;
    for (int i = 0; i < [self.childViewControllers count]; i++) {
        if ([self.childViewControllers[i].title isEqualToString:@"MyFakeAlertVC"]) {
            vc = self.childViewControllers[i];
            break;
        }
    }
    if (nil != vc) {
        [vc willMoveToParentViewController:nil];
        [vc.view removeFromSuperview];
        [vc removeFromParentViewController];
    }
}
- (void)showFakeAlertVC {
    
    UIAction *okAction = [UIAction actionWithTitle:@"Continue" image:nil identifier:nil handler:^(UIAction * _Nonnull action) {
        // do something because OK / Continue was tapped
        NSLog(@"OK tapped!");
        [self removeFakeAlertVC];
    }];
    UIAction *cancelAction = [UIAction actionWithTitle:@"Cancel" image:nil identifier:nil handler:^(UIAction * _Nonnull action) {
        // do something because Cancel was tapped
        NSLog(@"Cancel tapped!");
        [self removeFakeAlertVC];
    }];
    
    UIViewController *alert = [AlertHelper createAlertWithTitle:@"Some really long Title that we expect to wrap."
                                                        message:@"Some really long Message that we also expect to wrap."
                                                   cancelButton:@"Cancel Btn"
                                             continueButtonText:@"Continue Btn"
                                                 continueAction:okAction
                                                   cancelAction:cancelAction
    ];
    
    [self addChildViewController:alert];

    // we'll use this when we want to remove the
    //  view and child controller
    //  in case we have more than one child
    alert.title = @"MyFakeAlertVC";
    
    UIView *sv = self.view;
    UIView *v = alert.view;
    
    v.translatesAutoresizingMaskIntoConstraints = false;
    
    [sv addSubview:v];
    [alert didMoveToParentViewController:self];
    
    [NSLayoutConstraint activateConstraints:@[
        
        [v.topAnchor constraintEqualToAnchor:sv.topAnchor constant:0.0],
        [v.leadingAnchor constraintEqualToAnchor:sv.leadingAnchor constant:0.0],
        [v.trailingAnchor constraintEqualToAnchor:sv.trailingAnchor constant:0.0],
        [v.bottomAnchor constraintEqualToAnchor:sv.bottomAnchor constant:0.0],

    ]];
    
}

@end

Output:

enter image description here

Upvotes: 1

Related Questions