Cegah pemberhentian UIAlertController

Saya menambahkan UITextField ke UIAlertController, yang muncul sebagai AlertView. Sebelum mengabaikan UIAlertController, saya ingin memvalidasi input UITextField. Berdasarkan validasi saya ingin mengabaikan UIAlertController atau tidak. Tapi saya tidak tahu bagaimana mencegah tindakan pemberhentian UIAlertController saat tombol ditekan. Adakah yang bisa memecahkan masalah ini atau ada ide harus mulai dari mana? Saya pergi ke Google tetapi tidak berhasil :/ Terima kasih!


person jona jürgen    schedule 02.09.2014    source sumber


Jawaban (5)


Anda benar: jika pengguna dapat mengetuk tombol di lansiran Anda, lansiran tersebut akan ditutup. Jadi, Anda ingin mencegah pengguna mengetuk tombol! Ini semua hanya masalah menonaktifkan tombol UIAlertAction Anda. Jika tindakan peringatan dinonaktifkan, pengguna tidak dapat mengetuknya untuk menutupnya.

Untuk menggabungkan ini dengan validasi bidang teks, gunakan metode delegasi bidang teks atau metode tindakan (dikonfigurasi dalam pengendali konfigurasi bidang teks saat Anda membuatnya) untuk mengaktifkan/menonaktifkan UIAlertActions dengan tepat bergantung pada teks apa yang telah (atau belum) dimasukkan .

Berikut ini contohnya. Kami membuat bidang teks seperti ini:

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

Kami memiliki tindakan Batal dan tindakan OK, dan kami menonaktifkan tindakan OK:

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

Selanjutnya, pengguna tidak dapat mengetuk OK kecuali ada teks sebenarnya di bidang teks:

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 Berikut versi kode di atas saat ini (Swift 3.0.1 dan yang lebih baru):

alert.addTextField { tf in
    tf.addTarget(self, action: #selector(self.textChanged), for: .editingChanged)
}

Dan

alert.actions[1].isEnabled = false

Dan

@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 != "")
}
person matt    schedule 02.09.2014
comment
Contoh lengkapnya di sini: github.com/ mattneub/Pemrograman-iOS-Buku-Contoh/blob/master/ - person matt; 02.09.2014
comment
Apakah ada contoh Objective-C tentang hal ini? - person Adrian; 24.09.2015
comment
Jawaban yang sangat indah dan elegan. Terima kasih! Saya baru saja menggunakan ini pada proyek Swift. - person Adrian; 15.10.2015
comment
Terima kasih @AdrianB, Anda membuat hari saya menyenangkan. - person matt; 15.10.2015
comment
Rasanya menjaga pengontrol peringatan sebagai variabel lemah lebih dapat diandalkan daripada melakukan tarian nextResponder. Solusi hebat secara keseluruhan!! - person Kaan Dedeoglu; 15.04.2016
comment
Untuk menambahkannya, Anda dapat membuat penutupan dan mengaturnya menjadi target dan mengatur pemilih untuk dipanggil. Dengan begitu Anda bisa menyimpan semuanya dalam fungsi yang sama - person Swinny89; 22.07.2016
comment
@ Swinny89 Saya tidak membayangkan apa yang ada dalam pikiran Anda. Bisakah Anda memberikannya sebagai jawaban terpisah? - person matt; 22.07.2016
comment
Saya punya contoh di Objective-C tetapi tidak di Swift saat ini - person Swinny89; 22.07.2016
comment
Selesai, saya akan memperbarui jawaban saya nanti malam dengan contoh cepat. - person Swinny89; 22.07.2016
comment
@ Swinny89 Keren, terima kasih! Saya berharap untuk melihatnya. - person matt; 22.07.2016
comment
Versi kode saat ini (Desember 2016) ada di sini: github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ - person matt; 08.12.2016
comment
Versi kode saat ini (Desember 2016) ... @matt alangkah baiknya jika jawaban yang bagus melihat langsung di sini di StackOverflow versi kode yang diperbarui - person Fmessina; 23.04.2018
comment
@Fmessina Versi kode saat ini ditampilkan langsung di sini, dalam jawaban saya. - person matt; 23.04.2018
comment
Itu melempar aplikasi Pengakhiran karena pengecualian 'NSRangeException' yang tidak tertangkap, alasan: '*** -[__NSSingleObjectArrayI objectAtIndex:]: indeks 1 melampaui batas [0 .. 0]' - person Pawan; 11.09.2018

Saya telah menyederhanakan jawaban matt tanpa melintasi hierarki tampilan. Ini menjadikan tindakan itu sendiri sebagai variabel lemah. Ini adalah contoh yang berfungsi penuh:

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")
}
person ullstrm    schedule 19.03.2016
comment
@ullstrm: Terima kasih banyak membantu. - person Pawan; 11.09.2018

Mengutip jawaban @ Matt, inilah cara saya melakukan hal yang sama di 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);
}
person Olie    schedule 03.11.2015

Saya menyadari bahwa ini ada di Objectiv-C tetapi ini menunjukkan prinsipnya. Saya akan memperbarui ini dengan versi cepat nanti.

Anda juga bisa melakukan hal yang sama dengan menggunakan blok sebagai target.

Tambahkan properti ke ViewController Anda sehingga blok (penutupan untuk Swift) memiliki referensi yang kuat

@property (strong, nonatomic) id textValidationBlock;

Kemudian buat AlertViewController seperti ini:

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];
person Swinny89    schedule 22.07.2016

Inilah ide yang sama seperti jawaban lainnya, tetapi saya ingin metode sederhana diisolasi dalam ekstensi dan tersedia untuk digunakan di subkelas UIViewController mana pun. Ini menunjukkan peringatan dengan satu kolom input teks dan dua tombol: ok dan batal.

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)
    }
}
person algrid    schedule 23.11.2020