حدث تغيير النص في UITextField: كل حرف مهم
\n\nتخيل: أنت تكتب تطبيقًا للملاحظات، أو البحث في الكتالوج، أو تطبيق مراسلة. المستخدم بدأ للتو في الكتابة في UITextField، وتحتاج أنت بالفعل إلى الرد — إظهار الاقتراحات، حساب الأحرف المتبقية، أو إرسال طلب إلى الخادم. كيف تلتقط هذه اللحظة بالذات عندما يتغير النص؟ سؤال يقلق المبتدئين وحتى مطوري iOS ذوي الخبرة. على Stack Overflow، موضوع حدث تغيير النص في UITextField حصل على آلاف المشاهدات — وليس بدون سبب.
في تطوير iOS، هناك على الأقل ثلاثة طرق عملية. واختيار الطريقة الخاطئة قد يكلفك أخطاء، تسرب ذاكرة، أو 100 سطر إضافي من الكود. اليوم سنقوم بتفصيل جميع طرق تتبع تغيير النص في UITextField: من المفوض الكلاسيكي إلى Combine الحديث. هيا بنا!
\n\nلماذا لا يكفي مجرد الضغط على زر؟
\n\nقد يبدو الأمر بسيطًا؟ المستخدم أدخل النص — ضغط على "تم" أو زر "بحث". لكن الواجهات الحديثة تتطلب رد فعل في الوقت الفعلي. إليك السيناريوهات النموذجية:
\n- \n
- الإكمال التلقائي (search as you type) — تظهر الاقتراحات مع كل ضغطة مفتاح. \n
- التحقق الفوري — التحقق من البريد الإلكتروني أو رقم الهاتف دون إرسال النموذج. \n
- عداد الأحرف — على سبيل المثال، في تطبيق تويتر تحتاج إلى إظهار عدد الأحرف المتبقية حتى الحد الأقصى. \n
- البحث الديناميكي — تصفية قائمة جهات الاتصال أو المنتجات. \n
في كل هذه الحالات، نحتاج إلى حدث UITextField text change event. والآن السؤال: كيف ننفذه بشكل صحيح في Swift؟
الطريقة 1: مفوض UITextFieldDelegate — الكلاسيكية
\n\nالطريقة الأكثر وضوحًا، وربما الأكثر شيوعًا. تقوم بتعيين مفوض لحقل النص الخاص بك وتنفيذ الطريقة textField(_:shouldChangeCharactersIn:replacementString:). لكن هناك فارق بسيط: هذه الطريقة تُستدعى قبل أن يتغير النص فعليًا. إنها تسأل: "هل يمكن إدراج هذا الحرف؟"
للحصول على النص الفعلي، تحتاج إلى بنائه يدويًا:
\n\nclass ViewController: UIViewController, UITextFieldDelegate {\n \n @IBOutlet weak var textField: UITextField!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n textField.delegate = self\n }\n \n func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {\n let currentText = textField.text ?? ""\n guard let stringRange = Range(range, in: currentText) else { return false }\n let updatedText = currentText.replacingCharacters(in: stringRange, with: string)\n \n print("النص الجديد: \\(updatedText)")\n // هنا يمكن تحديث الواجهة أو بدء البحث\n return true\n }\n}\n\n\nالمزايا:
\n- \n
- تحكم كامل في الإدخال — يمكن منع إدراج أحرف معينة. \n
- يعمل على جميع إصدارات iOS. \n
العيوب:
\n- \n
- الكود ضخم لمجرد "التقاط التغيير". \n
- تحتاج إلى تذكر
NSRangeوتحويله إلىRange<String.Index>. \n - لا يعمل عند تغيير النص برمجيًا مثل
textField.text = "نص جديد". \n
هذه الطريقة مناسبة تمامًا إذا كنت بحاجة إلى التحقق من الإدخال (مثل منع الأحرف في حقل رقم الهاتف). لكن لتتبع التغييرات البسيطة، هناك خيارات أكثر أناقة.
\n\nالطريقة 2: Target-Action (UIControl.Event.editingChanged) — البساطة والموثوقية
\n\nالطريقة الثانية هي إضافة هدف وإجراء لحدث .editingChanged. يتم استدعاؤه بعد كل تغيير في النص، والكود يبدو موجزًا: