【问题标题】:Prevent dismissal of UIAlertController防止解除 UIAlertController
【发布时间】:2014-10-26 23:46:48
【问题描述】:

我将UITextField 添加到UIAlertController,它显示为AlertView。在关闭UIAlertController 之前,我想验证UITextField 的输入。基于验证我想解雇UIAlertController 或不。但我不知道如何防止按下按钮时UIAlertController 的解除操作。有没有人解决了这个问题或任何想法从哪里开始?我去了谷歌,但没有运气:/谢谢!

【问题讨论】:

    标签: swift uialertcontroller


    【解决方案1】:

    这里的想法与其他答案相同,但我想要一个在扩展中隔离的简单方法,可用于任何 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)
        }
    }
    

    【讨论】:

      【解决方案2】:

      您是对的:如果用户可以点击警报中的按钮,则警报将被解除。所以你想阻止用户点击按钮!这只是禁用 UIAlertAction 按钮的问题。如果警报操作被禁用,用户无法点击它来关闭它。

      要将其与文本字段验证相结合,请使用文本字段委托方法或操作方法(在创建时在文本字段的配置处理程序中配置)根据文本有(或没有)适当地启用/禁用 UIAlertActions ) 被输入。

      这是一个例子。我们创建了这样的文本字段:

      alert.addTextFieldWithConfigurationHandler {
          (tf:UITextField!) in
          tf.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
      }
      

      我们有一个 Cancel 动作和一个 OK 动作,我们将 OK 动作带入了禁用的世界:

      (alert.actions[1] as UIAlertAction).enabled = false
      

      随后,除非文本字段中有一些实际文本,否则用户无法点击确定:

      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 != "")
      }
      

      编辑这是上述代码的当前版本(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 != "")
      }
      

      【讨论】:

      • 有没有这样的 Objective-C 示例?
      • 如此华丽、优雅的答案。谢谢!我刚刚在一个 Swift 项目中使用了它。
      • 谢谢@AdrianB,你让我开心。
      • 感觉保持alert控制器作为一个弱变量比做nextResponder舞更可靠。整体解决方案很棒!
      【解决方案3】:

      我意识到这是在 Objectiv-C 中,但它显示了主体。稍后我将使用 swift 版本对其进行更新。

      你也可以使用块作为目标来做同样的事情。

      向您的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];
      

      【讨论】:

        【解决方案4】:

        我在没有遍历视图层次结构的情况下简化了 matt 的答案。这将动作本身作为一个弱变量来代替。这是一个完整的示例:

        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")
        }
        

        【讨论】:

        • @ullstrm:非常感谢。
        【解决方案5】:

        抄袭@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);
        }
        

        【讨论】:

          猜你喜欢
          • 2018-05-26
          • 2015-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多