Я добавляю UITextField
к UIAlertController
, который отображается как AlertView
. Прежде чем отклонить UIAlertController
, я хочу проверить ввод UITextField
. На основании проверки я хочу отклонить UIAlertController
или нет. Но я понятия не имею, как предотвратить удаление UIAlertController
при нажатии кнопки. Кто-нибудь решил эту проблему или есть идеи, с чего начать? Я пошел в Google, но не повезло: / Спасибо!
Предотвратить увольнение UIAlertController
Ответы (5)
Вы правы: если пользователь может нажать кнопку в вашем оповещении, оповещение будет отклонено. Итак, вы хотите, чтобы пользователь не нажимал кнопку! Это всего лишь вопрос отключения ваших кнопок UIAlertAction. Если действие оповещения отключено, пользователь не может коснуться его, чтобы закрыть.
Чтобы объединить это с проверкой текстового поля, используйте метод делегата текстового поля или метод действия (настроенный в обработчике конфигурации текстового поля при его создании), чтобы соответствующим образом включить/отключить UIAlertActions в зависимости от того, какой текст был (или не был) введен. .
Вот пример. Мы создали текстовое поле следующим образом:
alert.addTextFieldWithConfigurationHandler {
(tf:UITextField!) in
tf.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
}
У нас есть действие «Отмена» и действие «ОК», и мы отключили действие «ОК»:
(alert.actions[1] as UIAlertAction).enabled = false
Впоследствии пользователь не может нажать OK, если в текстовом поле нет фактического текста:
func textChanged(sender:AnyObject) {
let tf = sender as UITextField
var resp : UIResponder = tf
while !(resp is UIAlertController) { resp = resp.nextResponder() }
let alert = resp as UIAlertController
(alert.actions[1] as UIAlertAction).enabled = (tf.text != "")
}
EDIT Вот текущая (Swift 3.0.1 и более поздние версии) версия приведенного выше кода:
alert.addTextField { tf in
tf.addTarget(self, action: #selector(self.textChanged), for: .editingChanged)
}
и
alert.actions[1].isEnabled = false
и
@objc func textChanged(_ sender: Any) {
let tf = sender as! UITextField
var resp : UIResponder! = tf
while !(resp is UIAlertController) { resp = resp.next }
let alert = resp as! UIAlertController
alert.actions[1].isEnabled = (tf.text != "")
}
Я упростил ответ Мэтта без обхода иерархии представлений. Вместо этого это удерживает само действие как слабую переменную. Это полностью рабочий пример:
weak var actionToEnable : UIAlertAction?
func showAlert()
{
let titleStr = "title"
let messageStr = "message"
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert)
let placeholderStr = "placeholder"
alert.addTextFieldWithConfigurationHandler({(textField: UITextField) in
textField.placeholder = placeholderStr
textField.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
})
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (_) -> Void in
})
let action = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (_) -> Void in
let textfield = alert.textFields!.first!
//Do what you want with the textfield!
})
alert.addAction(cancel)
alert.addAction(action)
self.actionToEnable = action
action.enabled = false
self.presentViewController(alert, animated: true, completion: nil)
}
func textChanged(sender:UITextField) {
self.actionToEnable?.enabled = (sender.text! == "Validation")
}
Отрываясь от ответа @Matt, вот как я сделал то же самое в Obj-C.
- (BOOL)textField: (UITextField*) textField shouldChangeCharactersInRange: (NSRange) range replacementString: (NSString*)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange: range withString: string];
// check string length
NSInteger newLength = [newString length];
BOOL okToChange = (newLength <= 16); // don't allow names longer than this
if (okToChange)
{
// Find our Ok button
UIResponder *responder = textField;
Class uiacClass = [UIAlertController class];
while (![responder isKindOfClass: uiacClass])
{
responder = [responder nextResponder];
}
UIAlertController *alert = (UIAlertController*) responder;
UIAlertAction *okAction = [alert.actions objectAtIndex: 0];
// Dis/enable Ok button based on same-name
BOOL duplicateName = NO;
// <check for duplicates, here>
okAction.enabled = !duplicateName;
}
return (okToChange);
}
Я понимаю, что это в Objectiv-C, но он показывает принцип. Я обновлю это быстрой версией позже.
Вы также можете сделать то же самое, используя блок в качестве цели.
Добавьте свойство в свой ViewController
, чтобы блок (замыкание для swift) имел сильную ссылку
@property (strong, nonatomic) id textValidationBlock;
Затем создайте AlertViewController
вот так:
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"Message" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
__weak typeof(self) weakSelf = self;
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[weakSelf doSomething];
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[alertController.actions lastObject].enabled = NO;
self.textValidationBlock = [^{
UITextField *textField = [alertController.textFields firstObject];
if (something) {
alertController.message = @"Warning message";
[alertController.actions lastObject].enabled = NO;
} else if (somethingElse) {
alertController.message = @"Another warning message";
[alertController.actions lastObject].enabled = NO;
} else {
//Validation passed
alertController.message = @"";
[alertController.actions lastObject].enabled = YES;
}
} copy];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"placeholder here";
[textField addTarget:weakSelf.textValidationBlock action:@selector(invoke) forControlEvents:UIControlEventEditingChanged];
}];
[self presentViewController:alertController animated:YES completion:nil];
Здесь та же идея, что и в других ответах, но мне нужен простой метод, изолированный в расширении и доступный для использования в любом подклассе UIViewController. Он показывает оповещение с одним полем ввода текста и двумя кнопками: ок и отмена.
extension UIViewController {
func askForTextAndConfirmWithAlert(title: String, placeholder: String, okHandler: @escaping (String?)->Void) {
let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert)
let textChangeHandler = TextFieldTextChangeHandler { text in
alertController.actions.first?.isEnabled = !(text ?? "").isEmpty
}
var textHandlerKey = 0
objc_setAssociatedObject(self, &textHandlerKey, textChangeHandler, .OBJC_ASSOCIATION_RETAIN)
alertController.addTextField { textField in
textField.placeholder = placeholder
textField.clearButtonMode = .whileEditing
textField.borderStyle = .none
textField.addTarget(textChangeHandler, action: #selector(TextFieldTextChangeHandler.onTextChanged(sender:)), for: .editingChanged)
}
let okAction = UIAlertAction(title: CommonLocStr.ok, style: .default, handler: { _ in
guard let text = alertController.textFields?.first?.text else {
return
}
okHandler(text)
objc_setAssociatedObject(self, &textHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN)
})
okAction.isEnabled = false
alertController.addAction(okAction)
alertController.addAction(UIAlertAction(title: CommonLocStr.cancel, style: .cancel, handler: { _ in
objc_setAssociatedObject(self, &textHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN)
}))
present(alertController, animated: true, completion: nil)
}
}
class TextFieldTextChangeHandler {
let handler: (String?)->Void
init(handler: @escaping (String?)->Void) {
self.handler = handler
}
@objc func onTextChanged(sender: AnyObject) {
handler((sender as? UITextField)?.text)
}
}