15

I would like to implement a UIAlertViewController like this (Reference: Pages and Keynote app):

enter image description here.

I have implemented a custom tableview and presented the view by mimicking the UIAlertViewController. But I cannot achieve a similar UI as above. Is there any default controller available for this?

-(void)share:(id)sender
{
    [self setModalPresentationStyle:UIModalPresentationCurrentContext];

    AlertViewController *renameCntrl = [self.storyboard instantiateViewControllerWithIdentifier:AlertTblViewidentifier];
    renameCntrl.optionViewDelegate = self;
    renameCntrl.providesPresentationContextTransitionStyle = YES;
    renameCntrl.definesPresentationContext = YES;
    [renameCntrl setModalPresentationStyle:UIModalPresentationOverCurrentContext];
    [self presentViewController:renameCntrl animated:YES completion:nil];
}
JAL
  • 41,701
  • 23
  • 172
  • 300
SMS
  • 558
  • 1
  • 9
  • 22

3 Answers3

11

UIAlertController has a private contentViewController property which allows you to set any UIViewController subclass as part of the alert controller. It works well with both action sheet or alert styles. You can mix content view controller with other alert actions.

This is how UIActivityViewController, the Airdrop preview controller, etc. are implemented.

Best practice is to subclass UIAlertController, and in either initWithNibName:bundle: or viewDidLoad, use setValue:forKey: to set the contentViewController property. Don't forget to set the preferredContentSize for your content view controller.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • 9
    "Best practice is to sublclass `UIAlertController`"... uhhh, no. From the docs: "The UIAlertController class is intended to be used as-is and does not support subclassing." – Firo Jan 30 '16 at 15:48
  • @Firo We are talking about private API here. I can attest it works well. – Léo Natan Jan 30 '16 at 15:49
  • 8
    I in no way doubt it works. Just would not consider it "best practice." I do not see any reference to the OP talking about private APIs, that is all. – Firo Jan 30 '16 at 15:51
  • @Firo I meant in the context of my answer. – Léo Natan Jan 30 '16 at 15:52
  • @LeoNatan, could this code be rejected by Apple? – Patrick Bodet Jun 21 '21 at 16:57
  • Anything is possible, but if you take the smallest care, like using setValueForKey, I don’t foresee issues with Apple’s automated private API checker. – Léo Natan Jun 22 '21 at 16:02
  • With style set to .alert Xcode log this message: A constraint factory method was passed a nil layout anchor. This is not allowed, and may cause confusing exceptions. Break on BOOL _NSLayoutConstraintToNilAnchor(void) to debug. This will be logged only once. This may break in the future. – Patrick Bodet Jun 23 '21 at 04:46
2

Adding on to Leo's answer, yes, there is a private property on UIAlertController contentViewController which allows you to set a UIViewController (and it's view) as the content of the UIAlertController.

You can create a private interface to access this property without using KVO or importing a private header like so:

@interface UIAlertController (ContentViewController)
@property (nonatomic, strong) UIViewController * contentViewController;
@end

Then, layout your custom view in your content view controller's view, via Interface Builder or programmatically.

Remember that you also need to override your view controller's preferredContentSize:

- (CGSize)preferredContentSize {
    CGSize contentSize = [super preferredContentSize]; //gets the preferredContentHeight from the view, will be set depending on how much content we have
    return CGSizeMake(contentSize.width, self.view.preferredContentHeight); 
}

Note: rather than overriding the getter, Leo Natan suggests setting the preferredContentSize directly, since it is a property on UIViewController.

You can also override your view controller's view in your subclass as well, if you want.

Set up your alert as your normally would:

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"Message" 
                                                                                    preferredStyle:UIAlertControllerStyleActionSheet];

Set your custom view:

[alertController setContentViewController:[[MyContentViewController alloc] init]];

Add your actions:

[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^{
                       NSLog(@"Cancel Button was pressed");
}];

Present as your normally would:

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

More information on the private APIs of UIAlertController can be found on the iPhone Dev Wiki's article on UIAlertController.

I would also check out the private interfaces on _UIAlertControllerActionView and UIAlertAction, as well as the UIAlertActionViewRepresentation_Internal protocol.

JAL
  • 41,701
  • 23
  • 172
  • 300
0

I think you should use UIAlertController with a preferredStyle set to UIAlertControllerStyleActionSheet.

But if this is not enough start from scratch, from UIWindow and UIViewController with tableView and make any UI you want, there are a lot of custom alertView implementations I'm sure you easily found one for example.

sage444
  • 5,661
  • 4
  • 33
  • 60