
ahmdsat
عضو شرف
المساهمات :
42
النقاط :
82
التقيم :
1
الاوسمة :

2018-12-31, 2:39 pm
في هذا المقال راح نستعرض طريقتي المفضلة في تصميم واجهات المستخدم باستخدام ال CollectionView و يعتبر هذا الدرس متوسط الصعوبة.
[size=33]المتطلبات:
- المام بنظام ال iOS
- المام بال CollectionView
- المام بال Delegate Design Pattern
- معرفة بال Auto Layout
[size=33]الفكرة:[/size]
سوف نقوم بالاستفادة من ال
[/size]
بتجهيز قوالب واجهة المستخدم و هي عبارة عن
[/size][/size]
وهذه القوالب سوف تحتوي على أساس واجهة المستخدم من أزرار و عبارات و غيرها. وبالمثال راح يتضح المقال بإذن الله.[/size]
[size=33]الخطوات:[/size]
بعد إنشاء المشروع و تجهيزه نقوم بعمل class جديد يرث
[/size]
و نسميه
[/size][/size]
(راح نترك الكلاس هذا و نقوم بتجهيز ال
[/size][/size]
)[/size]
الآن سوف نقوم بتجهيز ال
[/size]
وسيكون هو الأساس اللي راح يغذي ال
[/size][/size]
نقوم بإنشاء كلاس و نسميه
[/size]
ونضع هذا الكود بداخله[/size]
[/size]
نعود الآن الى
[/size]
ونضيف متغير من نوع
[/size][/size]
من نوع
[/size][/size]
ومن ثم نقوم بتجهيز ال
[/size]
في كلاس
[/size][/size]
كالتالي[/size]
[/size]
نقوم الآن بتجهيز الخلايا حتى نستخدمها لعرضها. و نبدأ بال
[/size]
حيث نقوم بإنشاء كلاس بهذا الاسم يرث
[/size][/size]
وكذلك نقوم بإنشاء ملف xib بنفس الاسم ونقوم بوضع الكود التالي:[/size]
[/size]
وهذا شكل ملف ال xib
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
وهذه هي ال Constraints
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
**ولا ننسى من استخدام ال ReuseIdentifier كما هو في ال ViewModel
الآن سوف نقوم بالعودة الى
[/size]
لنختبر أن ما قمنا به الى الان يعمل بالشكل الصحيح وطريقتي المفضلة هي أن نقوم بعمل Extension على كلاس
[/size][/size]
و يحتوي على الدوال المساعدة لإضافة هذه الخلايا. ويكون هذا الكود بداخل ال Extension [/size]
[/size]
و نقوم الآن بتسجيل الخلية الجديدة في ال
[/size]
ومن ثم استخدامها في
[/size][/size]
كالتالي:[/size]
[/size]
الآن قمنا بتجهيز جميع ما نحتاجه و لكن تبقى شيء واحد و هو أن نضمن أن أبعاد الخلية كما يجب و حتى نتمكن من ذلك نقوم بإضافة الكود التالي:
[/size]
الحمد لله. الآن كل ما تبقى علينا هو استخدام هذا الكود و سوف نقوم بالتعديل على كلاس
[/size]
الذي تم إنشائه من ال XCode حسب التالي:[/size]
[/size]
ولكن قبل ذلك نحتاج أن نضيف في ال Storyboard واجهة جديدة من نوع Collection View Controller وتسنيدها الى الكلاس السابق وتعيينها ك initial view controller و حذف الواجهة القديمة من الStoryboard
وتكون هذه هي النتيجة:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
الان سوف نقوم بإعادة الخطوات نفسها لإضافة عبارات من نوع UILabel الى واجهة المستخدم. و سوف تكون من نوع key/value بحيث يكون Label للعنوان و Label آخر للقيمة
نبدأ بتجهيز الخلية كما فعلنا في السابق مع العنوان وننشئ كلاس يرث UICollectionViewCell ونسميه LabelCollectionViewCell ونقوم بإنشاء ملف ال xib كذلك و نقوم بتجهيزه كالتالي:
[/size]
ونجهز ملف xib كالتالي:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
وال constraints كالتالي:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
نقوم الآن بالتعديل على كلاس
[/size]
وتجهيز الدوال المساعدة لإضافة الخلية الجديدة.[/size]
[/size]
** ولا ننسى إضافة ال reuseIdentifier كما هو من ال ViewModel
و من ثم نقوم بتسجيل الخلية لاستخدامها في ال
[/size]
كالتالي:[/size]
[/size]
نقوم الآن بالتعديل على
[/size]
لتجهيز الخلية من النوع الجديد بإضافة case جديد كالتالي:[/size]
[/size]
الآن فقط نقوم باستخدام الدالة الجديدة في كلاس
[/size]
لإضافة واجهة مستخدم من نوع Key/Value كالتالي:[/size]
[/size]
و تكون هذه هي النتيجة:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
الى هنا تنتهي هذه المقالة!!!!
ولكن ماذا عن إضافة الأازرار والخلايا الفراغة؟ سوف أترك عمل هذا كتمرين لكم واستقبل أي استفسارات بهذا الخصوص.
ستجدون الأكواد كاملة هنا على GitHub
[size=33]الفوائد[/size]
[/size]
[size]
في الختام شكرا لكم على القراءة واتمنى أن المقالة كانت لها فائدة. [/size]
[size=33]بسم الله الرحمن الرحيم[/size]
في هذا المقال راح نستعرض طريقتي المفضلة في تصميم واجهات المستخدم باستخدام ال CollectionView و يعتبر هذا الدرس متوسط الصعوبة.
[size=33]المتطلبات:
- المام بنظام ال iOS
- المام بال CollectionView
- المام بال Delegate Design Pattern
- معرفة بال Auto Layout
[size=33]الفكرة:[/size]
سوف نقوم بالاستفادة من ال
[/size]
- الكود:
UICollectionView
بتجهيز قوالب واجهة المستخدم و هي عبارة عن
[/size][/size]
- الكود:
UICollectionViewCell
وهذه القوالب سوف تحتوي على أساس واجهة المستخدم من أزرار و عبارات و غيرها. وبالمثال راح يتضح المقال بإذن الله.[/size]
[size=33]الخطوات:[/size]
بعد إنشاء المشروع و تجهيزه نقوم بعمل class جديد يرث
[/size]
- الكود:
UICollectionViewController
و نسميه
[/size][/size]
- الكود:
DetailsCollectionViewController
(راح نترك الكلاس هذا و نقوم بتجهيز ال
[/size][/size]
- الكود:
ViewModel
)[/size]
الآن سوف نقوم بتجهيز ال
[/size]
- الكود:
ViewModel
وسيكون هو الأساس اللي راح يغذي ال
[/size][/size]
- الكود:
DetailsCollectionViewController
نقوم بإنشاء كلاس و نسميه
[/size]
- الكود:
DetailsViewModel
ونضع هذا الكود بداخله[/size]
[/size]
- الكود:
import Foundation
import UIKit
// هذا ال Enum يمثل انواع الواجهات التي سنحتاجها في تطبيقنا
// مثلا هنا نحتاج ترأيس للمحتويات و نحتاج عبارات وأزرار و خلية بدون محتويات لإضافة فراغ بين المحتويات
enum DetailsCellType {
case header
case details
case button
case empty
}
// أي Struct ينتهي بكلمة Property سوف يحمل المعلومات اللتي تحتاجها كل خلية لعرض محتوياتها
struct HeaderCellProperty {
let title: String
}
struct DetailsCellProperty {
let title: String
let subtitle: String
}
struct ButtonCellProperty {
let buttonTitle: String
let buttonColor: UIColor
let buttonTitleColor: UIColor
let buttonClickHandler: () -> Void
}
struct EmptyCellProperty {
}
// هذا هو ال View Model ويحتوي على جميع الخصائص المذكورة سابقا
struct DetailsViewModel {
var cellType: DetailsCellType
var headerCellProperty: HeaderCellProperty?
var detailsCellProperty: DetailsCellProperty?
var buttonCellProperty: ButtonCellProperty?
var emptyCellProperty: EmptyCellProperty?
init(headerCellProperty: HeaderCellProperty) {
self.cellType = .header
self.headerCellProperty = headerCellProperty
}
init(detailsCellProperty: DetailsCellProperty) {
self.cellType = .details
self.detailsCellProperty = detailsCellProperty
}
init(buttonCellProperty: ButtonCellProperty) {
self.cellType = .button
self.buttonCellProperty = buttonCellProperty
}
init(emptyCellProperty: EmptyCellProperty) {
self.cellType = .empty
self.emptyCellProperty = emptyCellProperty
}
func reuseIdentifier() -> String {
switch self.cellType {
case .header:
return "HeaderNameCollectionViewCell"
case .details:
return "DetailTextCollectionViewCell"
case .button:
return "ButtonCollectionViewCell"
case .empty:
return "EmptyCollectionViewCell"
}
}
}
نعود الآن الى
[/size]
- الكود:
DetailsCollectionViewController
ونضيف متغير من نوع
[/size][/size]
- الكود:
Array
من نوع
[/size][/size]
- الكود:
DetailsViewModel
- الكود:
var viewModels = [DetailsViewModel]()
ومن ثم نقوم بتجهيز ال
[/size]
- الكود:
CollectionViewDataSource
في كلاس
[/size][/size]
- الكود:
DetailsCollectionViewController
كالتالي[/size]
[/size]
- الكود:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewModels.count
}
// لا نستطيع القيام بشيد هنا حتى نقوم بتجهيز الخلايا
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
return cell
}
نقوم الآن بتجهيز الخلايا حتى نستخدمها لعرضها. و نبدأ بال
[/size]
- الكود:
HeaderCollectionViewCell
حيث نقوم بإنشاء كلاس بهذا الاسم يرث
[/size][/size]
- الكود:
UICollectionViewCell
وكذلك نقوم بإنشاء ملف xib بنفس الاسم ونقوم بوضع الكود التالي:[/size]
[/size]
- الكود:
import UIKit
class HeaderCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var headerLabel: UILabel!
@IBOutlet weak var seperatorView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.headerLabel.textColor = .black
self.headerLabel.font = UIFont.preferredFont(forTextStyle: .headline)
self.seperatorView.backgroundColor = .black
}
}
وهذا شكل ملف ال xib
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
وهذه هي ال Constraints
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
**ولا ننسى من استخدام ال ReuseIdentifier كما هو في ال ViewModel
الآن سوف نقوم بالعودة الى
[/size]
- الكود:
DetailsCollectionViewController
لنختبر أن ما قمنا به الى الان يعمل بالشكل الصحيح وطريقتي المفضلة هي أن نقوم بعمل Extension على كلاس
[/size][/size]
- الكود:
DetailsCollectionViewController
و يحتوي على الدوال المساعدة لإضافة هذه الخلايا. ويكون هذا الكود بداخل ال Extension [/size]
[/size]
- الكود:
extension DetailsCollectionViewController {
func addHeaderNameWithTitle(title: String) {
let headerCellProperty = HeaderCellProperty(title: title)
let viewModel = DetailsViewModel(headerCellProperty: headerCellProperty)
self.viewModels.append(viewModel)
}
}
و نقوم الآن بتسجيل الخلية الجديدة في ال
[/size]
- الكود:
CollectionView
ومن ثم استخدامها في
[/size][/size]
- الكود:
CellForItemAtIndexPath
كالتالي:[/size]
[/size]
- الكود:
override func viewDidLoad() {
super.viewDidLoad()
let headerNameNib = UINib(nibName: "HeaderCollectionViewCell", bundle: Bundle.main)
self.collectionView?.register(headerNameNib, forCellWithReuseIdentifier: "HeaderNameCollectionViewCell")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let viewModel = self.viewModels[indexPath.item]
switch viewModel.cellType {
case .header:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: viewModel.reuseIdentifier(), for: indexPath) as! HeaderCollectionViewCell
cell.headerLabel.text = viewModel.headerCellProperty?.title
return cell
default:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
return cell
}
}
الآن قمنا بتجهيز جميع ما نحتاجه و لكن تبقى شيء واحد و هو أن نضمن أن أبعاد الخلية كما يجب و حتى نتمكن من ذلك نقوم بإضافة الكود التالي:
[/size]
- الكود:
//نحتاج لهذا البروتوكول حتى يقوم النظام باستدعاء الدوال للأحجام
class WalletDetailsViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height: CGFloat
let viewItem = self.viewModels[indexPath.item]
switch viewItem.cellType {
case .header:
height = 50
case .details:
height = 50
case.button:
height = 100
case .empty:
height = 20
}
let width = self.view.frame.size.width
return CGSize(width: width, height: height)
}
الحمد لله. الآن كل ما تبقى علينا هو استخدام هذا الكود و سوف نقوم بالتعديل على كلاس
[/size]
- الكود:
ViewController
الذي تم إنشائه من ال XCode حسب التالي:[/size]
[/size]
- الكود:
import UIKit
//فقط نجعل الكلاس يرث DetailsCollectionViewController
class ViewController: DetailsCollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
//نقوم باستدعاء الدالة المساعدة لإضافة العنوان/الترأيس
self.addHeaderNameWithTitle(title: "My First Title")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ولكن قبل ذلك نحتاج أن نضيف في ال Storyboard واجهة جديدة من نوع Collection View Controller وتسنيدها الى الكلاس السابق وتعيينها ك initial view controller و حذف الواجهة القديمة من الStoryboard
وتكون هذه هي النتيجة:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
الان سوف نقوم بإعادة الخطوات نفسها لإضافة عبارات من نوع UILabel الى واجهة المستخدم. و سوف تكون من نوع key/value بحيث يكون Label للعنوان و Label آخر للقيمة
نبدأ بتجهيز الخلية كما فعلنا في السابق مع العنوان وننشئ كلاس يرث UICollectionViewCell ونسميه LabelCollectionViewCell ونقوم بإنشاء ملف ال xib كذلك و نقوم بتجهيزه كالتالي:
[/size]
- الكود:
import UIKit
class LabelCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.titleLabel.font = UIFont.preferredFont(forTextStyle: .body)
self.subtitleLabel.font = UIFont.preferredFont(forTextStyle: .body)
self.titleLabel.textColor = UIColor.darkText
}
}
ونجهز ملف xib كالتالي:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
وال constraints كالتالي:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
نقوم الآن بالتعديل على كلاس
[/size]
- الكود:
DetailsCollectionViewController
وتجهيز الدوال المساعدة لإضافة الخلية الجديدة.[/size]
[/size]
- الكود:
func addDetails(with title: String, and subtitle: String) {
let detailsCellProperty = DetailsCellProperty(title: title, subtitle: subtitle)
let viewModel = DetailsViewModel(detailsCellProperty: detailsCellProperty)
self.viewModels.append(viewModel)
}
** ولا ننسى إضافة ال reuseIdentifier كما هو من ال ViewModel
و من ثم نقوم بتسجيل الخلية لاستخدامها في ال
[/size]
- الكود:
CollectionView
كالتالي:[/size]
[/size]
- الكود:
let detailsNameNib = UINib(nibName: "LabelCollectionViewCell", bundle: Bundle.main)
self.collectionView?.register(detailsNameNib, forCellWithReuseIdentifier: "DetailTextCollectionViewCell")
نقوم الآن بالتعديل على
[/size]
- الكود:
CellForItemAtIndexPath
لتجهيز الخلية من النوع الجديد بإضافة case جديد كالتالي:[/size]
[/size]
- الكود:
case .details:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: viewModel.reuseIdentifier(), for: indexPath) as! LabelCollectionViewCell
cell.titleLabel.text = viewModel.detailsCellProperty?.title
cell.subtitleLabel.text = viewModel.detailsCellProperty?.subtitle
return cell
الآن فقط نقوم باستخدام الدالة الجديدة في كلاس
[/size]
- الكود:
ViewController
لإضافة واجهة مستخدم من نوع Key/Value كالتالي:[/size]
[/size]
- الكود:
self.addDetails(with: "Title 1", and: "This is the first subtitle")
self.addDetails(with: "Title 2", and: "This is the second subtitle")
self.addDetails(with: "Title 3", and: "This is the third subtitle")
self.addDetails(with: "Title 4", and: "This is the fourth subtitle")
self.addDetails(with: "Title 5", and: "This is the fifth subtitle")
و تكون هذه هي النتيجة:
[ندعوك للتسجيل في المنتدى أو التعريف بنفسك لمعاينة هذه الصورة]
الى هنا تنتهي هذه المقالة!!!!
ولكن ماذا عن إضافة الأازرار والخلايا الفراغة؟ سوف أترك عمل هذا كتمرين لكم واستقبل أي استفسارات بهذا الخصوص.
ستجدون الأكواد كاملة هنا على GitHub
[size=33]الفوائد[/size]
[/size]
- يمكننا انشاء العديد من الواجهات بدون إعادة كتابة الأكواد فقط نقوم بإنشاء كلاس يرث DetailsCollectionViewController
- للشاشات الطويلة سيتكفل ال CollectionView بال scroll ولا نحتاج إلى إضافة أي سطر لعمل ال Scrolling
[size]
في الختام شكرا لكم على القراءة واتمنى أن المقالة كانت لها فائدة. [/size]