Отключение дескриптора UIAlertController при нажатии снаружи (IPad)

До iOS8 мы использовали UIActionSheet для отображения предупреждений, а теперь нам нужно использовать UIAlertController.

Когда мы использовали UIActionSheet, мы могли легко справиться с ситуациями, когда пользователь щелкнул вне всплывающего окна (что означает, что он хочет отменить операцию), сравнив clickedButtonAtIndex с cancelButtonIndex - если пользователь действительно нажал за пределами всплывающего окна, мы получили индекс кнопки отмены в этой функции.

Как мы можем справиться с этими ситуациями с новым UIAlertController? Я пробовал использовать блок «завершение», но у него нет контекста. Есть ли простой способ справиться с этим? (кроме "сохранения" состояний действий в некоторой общей переменной).


person Tomer Peled    schedule 23.08.2014    source источник


Ответы (3)


Вы можете добавить действие со стилем: UIAlertActionStyleCancel, и обработчик этого действия вызывается, когда пользователь касается вне всплывающего окна.

if ([UIAlertController class]) {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert Title" message:@"A Message" preferredStyle:UIAlertControllerStyleActionSheet];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        NSLog(@"User clicked button called %@ or tapped elsewhere",action.title);
    }]];

    [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        NSLog(@"User clicked button called %@",action.title);
    }]];

    [alertController addAction:[UIAlertAction actionWithTitle:@"Other" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
        NSLog(@"User clicked button called %@",action.title);
    }]];

    UIControl *aControl = (UIControl *) sender;
    CGRect frameInView = [aControl convertRect:aControl.bounds toView:self.view];
    alertController.popoverPresentationController.sourceRect = frameInView;
    alertController.popoverPresentationController.sourceView = self.view;
    alertController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny;
    [self presentViewController:alertController animated:YES completion:nil];
}
person Gareth    schedule 24.08.2014
comment
Возможно, мне что-то не хватает (я новичок в iOS). Но, по моему опыту, использование UIAlertControllerStyleActionSheet с UIAlertActionStyleCancel, похоже, не добавляет действие «Отмена». При изменении стиля на UIAlertControllerStyleAlert всегда отображается действие отмены. - person Joey Carson; 19.11.2014
comment
Обратите внимание, что UIAlertController удалит кнопку отмены при использовании всплывающего окна, даже если вы добавите его как действие. Пользователь отменяет всплывающее окно, касаясь его за пределами всплывающего окна, поэтому это не требуется. - person Reefwing; 05.02.2015
comment
Примечание. При наличии действия в стиле UIAlertActionStyleCancel AlertController отключается только в том случае, если вы используете стиль UIAlertControllerStyleActionSheet AlertController. Он не работает с UIAlertControllerStyleAlert. - person micnguyen; 24.04.2015
comment
@JDG Да, но стиль предупреждения не может быть отменен внешним краном, так что это не имеет значения. - person Supertecnoboff; 04.05.2016
comment
это нормально, когда я даю его для действия кнопки, и теперь я использую его для uitextfield, поэтому, как я должен вызывать этот контроллер, я также пытался вызвать метод - (BOOL) textFieldShouldBeginEditing: (UITextField *) textField {} с помощью это предупреждение открывается снова и снова, пожалуйста, помогите мне .. мне нужно вызвать его в текстовом поле, спасибо. - person Hari Narayanan; 16.07.2016
comment
Это отличается от вопроса OP, но вы можете использовать что-то вроде SDCAlertView Cocoapod, что при желании позволяет вам закрыть внешний кран. - person Chris Prince; 13.04.2019
comment
Обратите внимание, что даже если на iPad действие отмены не будет отображаться в контроллере, его обработчик завершения все равно вызывается. - person Jason McClinsey; 26.08.2019

Решение, которое работает для UIAlertController со стилем предупреждения. Просто нужно добавить распознаватель жестов в супервизор alertController.

    [self presentViewController: alertController
                   animated: YES
                 completion:^{
                     alertController.view.superview.userInteractionEnabled = YES;
                     [alertController.view.superview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(alertControllerBackgroundTapped)]];
                 }];

- (void)alertControllerBackgroundTapped
{
    [self dismissViewControllerAnimated: YES
                             completion: nil];
}
person nominanza    schedule 29.10.2015

UITapGestureRecognizer у меня не работал, поэтому я использовал такой способ:

func addDismissControl(_ toView: UIView) {
    let dismissControl = UIControl()
    dismissControl.addTarget(self, action: #selector(self.dismissAlertController), for: .touchDown)
    dismissControl.frame = toView.superview?.frame ?? CGRect.zero
    toView.superview?.insertSubview(dismissControl, belowSubview: toView)
}

func dismissAlertController() {
    self.dismiss(animated: true, completion: nil)
}

func presentAlertController(title: String?, message: String?, preferredStyle: UIAlertControllerStyle,  handler: ((UIAlertAction) -> Swift.Void)? = nil, completion: (() -> Swift.Void)? = nil) {

    let alertController = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
    alertController.addAction(UIAlertAction(title: "OK", style: .default) { (alertAction) -> Void in
        handler?(alertAction)
    })

    self.present(alertController, animated: true, completion: {
        self.addDismissControl(alertController.view)
        completion?()
    })
}

func someWhereInYourViewController() {
    // ...
    presentAlertController(title: "SomeTitle", message: "SomeMessage", preferredStyle: .actionSheet, handler: { (alertAction) -> Void in
        //do some action
    }, completion: {
        //do something after presentation
    })
    // ...
}
person Apoc    schedule 04.07.2017