Custom alert in Swift 5

تصميم Alert بلغة Swift عن طريق الكود

Mohammad Alhassonمنذ 5 سنوات

بسم الله الرحمن الرحيم

إذا كنت ممن يتعلم لغة Swift أو ممن يرغب في زيادة خبرته عموماً في مجال تطوير التطبيقات لنظام iOS حتماً ستنفعك هذه المقالة في تطوير تطبيقاتك في المستقبل, إذا لنبدأ مباشرة في صُلب الموضوع.
 

إذا أردت أن تقوم بإضافة Alert للمستخدم فأعتقد مباشرة أنك ستذهب لإضافة الخيار الرئيسي الذي توفره آبل طبعاً في لغة Swift و هو UIAlertContrller

لو أردنا أن نراجع بسرعة كيفية إضافة UIAlertController في Swift سوف يكون الأمر على النحو التالي:

 

let alert = UIAlertController(title: "Alert", message: "Delete this image", preferredStyle: .actionSheet)
        
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { (alert) in
     print("Delete button was tapped")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (alert) in
     print("Cancel button was tapped")
}))
        
present(alert, animated: true, completion: nil)

كما نعلم يوجد نمطين من رسائل التنبيه أو التحذير:

1. actionSheet

2.alert

لكن في هذه المقالة سوف نتطرق إلى النوع الأول و كيفية تصميم رسالة تحذير أو تنبيه خاصة بنا فقط عن طريق الكود و دون استخدام  Storyboard

الخطوة الأولى: 
إنشاء عنصر من نوع UIView ليكون هو العنصر الذي سيتم به الاستغناء عن الشكل الأساسي و لكن لأقوم بشرح الكود بشكل سريع
لقد قمت فقط بتحديد لون لهذا العنصر و أيضاً تغيير حدة الحواف الجانبية و أيضاً يتوجب عليك كتابة الكودد في السكر الثالث للعمل على الخطوة الثانة و هي إضافة Layout.

let alertView: UIView = {
    let view = UIView()
    view.backgroundColor = .white
    view.translatesAutoresizingMaskIntoConstraints = false
    view.layer.cornerRadius = 12
    return view
}()


الخطوة الثانية و هي إظهار هذا العنصر داخل التطبيق و ليكن داخل ViewDidLoad
سأقوم بإعطاءه Layout سريعة بهذا الشكل
 

view.backgroundColor = .gray
view.addSubview(alertView)

alertView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8).isActive = true
alertView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8).isActive = true
alertView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8).isActive = true
alertView.heightAnchor.constraint(equalToConstant: 180).isActive = true

شرح الكود السابق >>>
في البداية قمت بتغيير لون خلفية التطبيق لكي أتمكن من رؤية العنصر الخاص بي و من ثم يجب أن أضيف ذلك العنصر (View) إلى داخل الView الخاصة بالتطبيق ككل أو لندعوها الواجهة الرئيسية 
و من ثم يجب أيضاً إعطاء هذا العنصر لكي يأخذ مكانه من واجهة البرنامج و إلا لن يكون مكانه بالشكل المراد.
السطرين الأول و الثاني و هم لأضافة حدود من جهتي اليمين و اليسار 
السطر الثالث و هو لتحديد البعد من الأسفل و لكن قمت بإعطاءه قيمة مضافة إلى قيمة أو بعد المنطقة الأمنة Safe area و ذلك لنضع بالحسبان تشغيل التطبيق على أجهوة iPhone X و أحدث
أما السطر الأخير ببساطة هو لتحديد ارتفاع أو طول الـ View التي قمت بتعريفها من البداية و هي لب هذا الموضوع
الأن قم بتشغيل التطبيق و انظر إلى النتيجة:
 

Screenshot 1

حسناً وصلنا إلى نقطة جيدة و لكن ما زالت فارغة تماماً من أي شي سواء رسالة للمستخدم  أو أي شيء آخر

===========================================================

القسم الثاني و هو أضافة عناصر على سبيل المثال Button, Label, etc
1- إضافة عنوان لهذه الرسالة سوف يتم بالشكل التالي:
 

let alertTitle: UILabel = {
    let label = UILabel()
    label.text = "Delete this image"
    label.textColor = .gray
    label.font = UIFont.systemFont(ofSize: 17)
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

شرح الكود السابق >>>

قمت بتعريف Label و داخل القوسين أستطيع التعديل على خصائصه على سبيل المثال: اللون, الخط, وضعية النص, لون الخلفية و إلى ما ذلك
و لا ننسَ أيضاً إضافة السطر الأخير كما شرحت الغاية منه مسبقاً
 

علينا الآن أن نضيف هذه العنصر الجديد أو الرسالة داخل الـ View الخاصة بنا عن طريق الأمر التالي
 

alertView.addSubview(alertTitle)

قم بتشغيل التطبيق الآن و انظر إلى النتيجة
 

Alert label

تم إضافة الـ Label  ولكنه غير مقيد بأية حدود
لإصلاح هذه المشكلة قم بتحديده داخل الـ View عن طريق هذا الكود
 

alertTitle.topAnchor.constraint(equalTo: alertView.topAnchor, constant: 8).isActive = true
alertTitle.centerXAnchor.constraint(equalTo: alertView.centerXAnchor).isActive = true

ربما ترى أن هذه الكود مختلف قليلاً إذ أننا في هذه النقطة نحتاج فقط لإضافة حد من الأعلى (السطر الأول) و ثم تعيين مكان الرسالة في المنتصف (السطر الثاني)
الآن لنقوم بتشغيل التطبيق
 

Alert label

 

 

الأن سأقوم بإضافة العناصر التالية كما في الطريقة السابقة (لن أقوم بشرح الأمر مرة أخرى لأن الطريقة مشابهة لم شرحته سابقاً)
 

العنصر الثاني: message label
 

let alertMessage: UILabel = {
    let label = UILabel()
    label.text = "Do you want to delete this image?"
    label.textColor = .black
    label.textAlignment = .center
    label.font = UIFont(name: "AvenirNext-Medium", size: 20)
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

يمكنك طبعاً تغيير الخط و حجمه عوضاً عن خط النظام الأساسي

الآن للنتقل إلى إضافة Layout
 

alertView.addSubview(alertMessage)
alertMessage.topAnchor.constraint(equalTo: alertTitle.bottomAnchor, constant: 32).isActive = true
alertMessage.centerXAnchor.constraint(equalTo: alertView.centerXAnchor).isActive = true

 من الأعلى قمت بمحاذاة الرسالة للجهة السفى من العنوان (Alert title)

Message

في الخطوة ما قبل الأخيرة علينا إضافة أزرار للتحكم بهذه الرسالة 


العنصر الثالث: زر الحذف

static let deleteBtn: UIButton = {
       let button = UIButton()
       button.setTitle("Delete", for: .normal)
       button.setTitleColor(.white, for: .normal)
       button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
       button.backgroundColor = UIColor(red: 231/255, green: 76/255, blue: 60/255, alpha: 1)
       button.layer.cornerRadius = 8
       button.addTarget(self, action: #selector(deleteBtnTapped), for: .touchUpInside)
       return button
}()


العنصر الرابع:  زر الإلغاء
 

static let cancelBtn: UIButton = {
       let button = UIButton()
       button.setTitle("Delete", for: .normal)
       button.setTitleColor(UIColor(red: 47/255, green: 121/255, blue: 255/255, alpha: 1), for: .normal)
       button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
       button.backgroundColor = UIColor(red: 239/255, green: 239/255, blue: 244/255, alpha: 1)
       button.layer.cornerRadius = 8
       button.addTarget(self, action: #selector(cancelBtnTapped), for: .touchUpInside)
       return button
}()

عند إضافة Button يختلف الأمر قليلاً بالنسبة للخصائص و أيضاً لنجعل هذه الزر فعّال علينا ربط هذا الزر بـ Function لننفذه عن طريقه عملية معينة (على سبيل المثال حذف صورة)

و يتم ذلك بواسطة السطر الأخير من الكود السابق و داخل الـ Function لديك الحرية ي تنفيذ أي أمر تريده

@objc func deleteBtnTapped() {
    print("Delete button was tapped")
}
    
@objc func cancelBtnTapped() {
    print("Cancel button was tapped")
}

 *ملحوظة يجب أن تكتب في البداية objc@ ليتم الأمر 

(بعد إصدار  Swift 4 تم إضافة هذا أمر)

الآن قبل أن تقوم بتشغيل التطبيق 
لدنيا الآن زرين و أفضل طريقة لدمجهما معاً هي عن طريق إضافة StackView

لماذا؟
بدل أن نقوم بإضافة حدود مرتين سنختزل هذه الخطوة بكود واحد و نضمن أن الحجم سيكون مناسب داخل الـ View
 

=======================================================

3. العنصر الخامس:
 

let buttonsStackView: UIStackView = {
    let stackView = UIStackView(arrangedSubviews: [deleteBtn, cancelBtn])
    stackView.axis = .horizontal
    stackView.distribution = .fillEqually
    stackView.spacing = 16
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()

 

*شرح الكود >>

هذا الكود وظيفته هي إنشاء StackView و إضافة عناصر داخله و هي (زر الحذف + زر الإلغاء)

السطر الثاني هي لجعل الأزرار بشكل أفقي 
السطر الثالث لتقسيم المساحة بشكل متساوٍ بين الأزرار و من ثم قمت بإضافة مساحة بينهما في السطر الذي يليه
 

نأتي إلى الخطوة الأخير و هي إضافة الـStackView داخل الـ View بهذا الكود:
 

alertView.addSubview(buttonsStackView)
        
buttonsStackView.leftAnchor.constraint(equalTo: alertView.leftAnchor, constant: 16).isActive = true
buttonsStackView.rightAnchor.constraint(equalTo: alertView.rightAnchor, constant: -16).isActive = true
buttonsStackView.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -16).isActive = true
buttonsStackView.heightAnchor.constraint(equalToConstant: 46).isActive = true


*ملحوظة عند إضافة الحدود من جهة اليسار و من الأسفل يجب عليك أن تضيف القيمة بشكل سلبي.

الآن لنرى النتيجة 👨🏻‍💻

Final result

 

أمر رائع!!
هذا ما أردناه و لكن الأمر ينقصه بعض الحركة لإضفاء روح إلى هذه الرسالة

في الواقع ما أرغب به هو بالضبط ما تراه في هذه الصورة:
ليبدو الأمر أكثر جمالية
 

Animation alert view

 

 

=====================================================

لنبدأ بالجزء الثاني و هو كيفية إضافة حركة لهذه الـ View
 

سوف أقوم بعمل بعض التغييرات لتسهيل أمر الحركة علينا

أولاً علينا إنشاء متغير من نوع NSLayoutConstraint

وظيفة هذا المتغير هي تغيير الحد السفلي للـView للتحكم بإظهارها و إخفائها من الشاشة
 

var alertViewBottom: NSLayoutConstraint?

من ثم قم باستبدال هذا الكود السابق

 

alertView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8).isActive = true

 

ليكون بهذا الشكل الجديد
 

alertViewBottom = alertView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 200)
alertViewBottom?.isActive = true

شرح الكود >>

قمت بإسناد القيمة كاملاً إلى المتغير و في السطر الثاني علينا أيضاً أن نفعل هذه القيمة أو هذا الحد من الأسفل ( في هذه الحالة سوف تختفي الـ View بشكل كامل لأنها أصبحت بمقدار 200  خارج الشاشة بالكامل)

في الخطوة التالية يجب إضافة زر لإظهار الرسالة و سوف يتم الأمر بشكل بسيط كالتالي
 

@IBAction func alertBtnTapped(_ sender: UIButton) {
   alertViewBottom?.isActive = false
   alertViewBottom?.constant = -16
   alertViewBottom?.isActive = true
        
   UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 
   0.7, options: .curveEaseInOut, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)
    }

قمت بإضافة زر و في داخله الأوامر التالية

تعطيل الحد الأسفل من الـ View الخاصة بنا لإكمال الحركة بنجاح

من ثم إسناد قيمة جديدو و هي لإظهار الرسالة مرة أخرى 
الآن في السطر الثالث سوف يتم تفعيل قيمة الحد مرة أخرى من جديد

أما في النهاية هو أمر بسيط لجعل الحركة بشكل سلس و جميل 
 

بقي أمر واحد أخير و هو إخفاء الرسالة مرة أخرى عند الضغط على زر Cancel
 

@objc func cancelBtnTapped() {
        alertViewBottom?.constant = 200
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, 
        initialSpringVelocity: 0.7, options: .curveEaseInOut, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)
    }

 

بهذا الشكل نكون قد انتيهنا  🥳
في النهاية ربما تلاحظ أننا كتبينا الكثير من الأكواد, لما لا نجمعها في Function ؟

قم بتحديد جميع الأكواد في ViewDidLoad و اضغط بالزر الأيمن

 

add function

 

و الأن تم ترتيب الأمر أكثر من السابق 👌🏻
 

ViewDidLoad

أرجو أن يكون الشرح قد أعجبكم.. و نلتقي في درس آخر إن شاء الله

1
إعجاب
1223
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !