مرحبا يا هبر! أقدم إليكم ترجمة المقال
إنشاء عناصر واجهة المستخدم برمجياً باستخدام PureLayout بواسطة Aly Yaka.

مرحبًا بك في الجزء الثاني من المقالة حول إنشاء واجهة برمجياً باستخدام PureLayout.
في الجزء الأول ، أنشأنا واجهة مستخدم تطبيق جوال بسيط بشكل كامل في الكود ، دون استخدام القصص المصورة أو NIBs. في هذا الدليل ، سنغطي بعض عناصر واجهة المستخدم الأكثر استخدامًا في جميع التطبيقات:
- UINavigationController / Bar
- UITableView
- التحجيم الذاتي UITableViewCell
UINavigationController
في تطبيقنا ، ربما تحتاج إلى شريط تنقل بحيث يمكن للمستخدم الانتقال من قائمة جهات الاتصال إلى معلومات مفصلة حول جهة اتصال معينة ، ثم العودة مرة أخرى إلى القائمة. يمكن لـ
UINavigationController
حل هذه المشكلة بسهولة باستخدام شريط التنقل.
UINavigationController
هو مجرد مكدس حيث يمكنك نقل العديد من طرق العرض. يرى المستخدم العرض الأعلى (العرض الذي تم نقله آخر مرة) في الوقت الحالي (باستثناء عندما يكون لديك عرض آخر مقدم أعلى هذا ، دعنا نقول المفضلة). وعندما تضغط على وحدات التحكم في العرض العلوي بوحدة التحكم في التنقل ، تقوم وحدة التحكم في التنقل تلقائيًا بإنشاء زر "للخلف" (الجانب العلوي الأيسر أو الأيمن وفقًا لتفضيلات اللغة الحالية للجهاز) ، ويؤدي الضغط على هذا الزر إلى إعادتك إلى العرض السابق.
كل هذا يتم معالجته خارج الصندوق بواسطة وحدة التحكم في التنقل. ستحتاج إضافة واحدة أخرى إلى سطر إضافي واحد فقط من التعليمات البرمجية (إذا كنت لا ترغب في تخصيص شريط التنقل).
انتقل إلى AppDelegate.swift وأضف السطر التالي من التعليمات البرمجية أدناه ، دع
viewController = ViewController ():
let navigationController = UINavigationController(rootViewController: viewController)
الآن تغيير
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
. في السطر الأول ، أنشأنا مثيل
UINavigationController
على
UINavigationController
لدينا باعتباره
rootViewController
، والذي هو وحدة تحكم العرض في أسفل المكدس ، مما يعني أنه لن يكون هناك زر للخلف على شريط التنقل في طريقة العرض هذه. ثم نعطي نافذتنا وحدة تحكم في الملاحة باسم
rootViewController
، لأنه
rootViewController
الآن على جميع المشاهدات في التطبيق.
الآن قم بتشغيل التطبيق الخاص بك. يجب أن تبدو النتيجة كما يلي:
لسوء الحظ ، حدث خطأ ما. يبدو أن شريط التنقل يتداخل مع عرضنا العلوي ، ولدينا عدة طرق لإصلاح هذا:
- قم بزيادة حجم
upperView
بنا لتناسب ارتفاع شريط التنقل. - قم بتعيين الخاصية
isTranslucent
لشريط التنقل إلى false
. سيؤدي ذلك إلى جعل شريط التنقل معتمًا (إذا لم تكن لاحظت أنه شفاف بعض الشيء) ، والآن ستصبح الحافة العلوية من superview أسفل شريط التنقل.
أنا شخصياً سأختار الخيار الثاني ، لكنك ستدرس الأول. أوصي أيضًا بالتحقق من مستندات Apple وقراءتها بعناية على
UINavigationController
و
UINavigationBar
:
انتقل الآن إلى طريقة viewDidLoad وأضف هذا السطر
self.navigationController? .NavigationBar.isTranslucent = false super.viewDidLoad ()
self.navigationController? .NavigationBar.isTranslucent = false super.viewDidLoad ()
، بحيث يبدو كالتالي:
override func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.isTranslucent = false self.view.backgroundColor = .white self.addSubviews() self.setupConstraints() self.view.bringSubview(toFront: avatar) self.view.setNeedsUpdateConstraints() }
يمكنك أيضًا إضافة هذا السطر
self.title = "John Doe" viewDidLoad
، والذي سيضيف "ملف تعريف" إلى شريط التنقل حتى يعرف المستخدم مكان وجوده حاليًا. قم بتشغيل التطبيق وستكون النتيجة كما يلي:
إعادة بيع دينا عرض المراقب المالي
قبل المتابعة ، نحتاج إلى تقليل ملف
ViewController.swift
بنا
ViewController.swift
نتمكن من استخدام المنطق الحقيقي فقط ، وليس فقط رمز عناصر واجهة المستخدم. يمكننا القيام بذلك عن طريق إنشاء فئة فرعية من
UIView
ونقل جميع عناصر واجهة المستخدم الخاصة بنا هناك. السبب وراء قيامنا بذلك هو اتباع النموذج المعماري لطراز العرض أو MVC المعماري. تعرف على المزيد حول MVC
Model-View-Controller (MVC) على نظام التشغيل iOS: أسلوب حديث .
الآن انقر بزر الماوس الأيمن على مجلد
ContactCard
في Project Navigator وحدد "ملف جديد":

انقر فوق Cocoa Touch Class ثم التالي. الآن اكتب "ProfileView" كاسم للفئة ، وبجانب "Subclass of:" تأكد من إدخال "UIView". إنها فقط تطلب من Xcode أن تجعل صفنا يرث تلقائيًا من
UIView
، وسيضيف بعضًا من التعليمات البرمجية. الآن انقر فوق "التالي" ، ثم قم بإنشاء وحذف رمز التعليق علق:
ونحن الآن على استعداد لإعادة بيع المساكن.
قص ولصق كل المتغيرات البطيئة من وحدة التحكم في العرض في طريقة العرض الجديدة.
أسفل المتغير الأخير المعلق ، تجاوز
init(frame :)
عن طريق كتابة
init
ثم حدد أول نتيجة للإكمال التلقائي من Xcode.

سيظهر خطأ يوضح أنه يجب توفير المُهيئ "المطلوب" "init (coder :)" بواسطة فئة فرعية من "UIView":

يمكنك إصلاح ذلك عن طريق النقر على الدائرة الحمراء ثم إصلاح.

في أي مهيئ تم تجاوزه ، يجب عليك دائمًا استدعاء مُهيئ الطبقة الفائقة ، لذا أضف سطر التعليمات البرمجية هذا في الجزء العلوي من الأسلوب:
super.init (frame: frame)
.
قص ولصق طريقة
addSubviews()
أسفل
self.view
وحذف
self.view
قبل كل مكالمة
addSubview
.
func addSubviews() { addSubview(avatar) addSubview(upperView) addSubview(segmentedControl) addSubview(editButton) }
ثم اتصل بهذه الطريقة من المُهيئ:
override init(frame: CGRect) { super.init(frame: frame) addSubviews() bringSubview(toFront: avatar) }
بالنسبة للقيود ، تجاوز
updateConstraints()
وأضف مكالمة في نهاية هذه الوظيفة (حيث ستبقى دائمًا):
override func updateConstraints() {
عند تجاوز أي طريقة ، يكون من المفيد دائمًا التحقق من الوثائق من خلال زيارة مستندات Apple أو ، ببساطة ، الضغط باستمرار على المفتاح Option (أو Alt) والنقر على اسم الوظيفة:

قص ولصق رمز القيد من وحدة التحكم في طريقة العرض الجديدة:
override func updateConstraints() { avatar.autoAlignAxis(toSuperviewAxis: .vertical) avatar.autoPinEdge(toSuperviewEdge: .top, withInset: 64.0) upperView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom) segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: 8.0) segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) segmentedControl.autoPinEdge(.top, to: .bottom, of: avatar, withOffset: 16.0) editButton.autoPinEdge(.top, to: .bottom, of: upperView, withOffset: 16.0) editButton.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) super.updateConstraints() }
انتقل الآن مرة أخرى إلى وحدة تحكم العرض وتهيئة مثيل
ProfileView
عبر طريقة
viewDidLoad
let profileView = ProfileView(frame: .zero)
، أضفه كطريقة عرض فرعية إلى
ViewController
.
الآن تم تخفيض تحكم عرضنا إلى بضعة أسطر من التعليمات البرمجية!
import UIKit import PureLayout class ViewController: UIViewController { let profileView = ProfileView(frame: .zero) override func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.isTranslucent = false self.title = "Profile" self.view.backgroundColor = .white self.view.addSubview(self.profileView) self.profileView.autoPinEdgesToSuperviewEdges() self.view.layoutIfNeeded() } }
للتأكد من أن كل شيء يعمل كما هو مقصود ، قم بتشغيل التطبيق الخاص بك وتحقق من شكله.
يجب أن يكون هدفك دائمًا وجود جهاز تحكم رفيع ومراجع للمراجعة. قد يستغرق هذا الكثير من الوقت ، ولكنه سيوفر لك من المتاعب غير الضرورية أثناء الصيانة.
UITableView
بعد ذلك ، سنضيف UITableView لتقديم معلومات الاتصال مثل رقم الهاتف والعنوان وما إلى ذلك.
إذا لم تكن قد قمت بذلك بالفعل ، فانتقل إلى وثائق Apple للتحقق من UITableView و UITableViewDataSource و UITableViewDelegate.
انتقل إلى
ViewController.swift
وأضف
lazy var
viewDidLoad()
أعلاه
viewDidLoad()
:
lazy var tableView: UITableView = { let tableView = UITableView() tableView.translatesAutoresizingMaskIntoConstraints = false tableView.delegate = self tableView.dataSource = self return tableView }()
إذا حاولت تشغيل التطبيق ، فسوف تشكو Xcode من أن هذا الفصل ليس مفوضًا أو مصدر بيانات لـ
UITableViewController
، وبالتالي سنضيف هذين البروتوكولين إلى الفصل:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { . . .
مرة أخرى ، سوف يشكو Xcode من فئة لا تتوافق مع بروتوكول
UITableViewDataSource
، مما يعني أن هناك طرق إلزامية في هذا البروتوكول لم يتم تعريفها في الفصل الدراسي. لمعرفة أي من هذه الطرق يجب عليك تنفيذها أثناء الضغط على Cmd + Control ، انقر فوق بروتوكول
UITableViewDataSource
في تعريف الفئة وسوف تنتقل إلى تعريف البروتوكول. لأي طريقة لا تسبقها كلمة "
optional
، يجب تنفيذ فئة متوافقة مع هذا البروتوكول.
هنا لدينا طريقتان نحتاج إلى تنفيذهما:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
- تخبر هذه الطريقة طريقة عرض الجدول بعدد الصفوف التي نريد عرضها.public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
تستعلم هذه الطريقة الخلية في كل صف. هنا نقوم بتهيئة (أو إعادة استخدام) الخلية وإدراج المعلومات التي نريد عرضها للمستخدم. على سبيل المثال ، ستعرض الخلية الأولى رقم الهاتف ، وستعرض الخلية الثانية العنوان وما إلى ذلك.
انتقل الآن إلى
ViewController.swift
، وابدأ بكتابة
numberOfRowsInSection
، وعندما يظهر الإكمال التلقائي ، حدد الخيار الأول.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { <#code#> }
حذف رمز الكلمة والعودة الآن 1.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
ضمن هذه الوظيفة ، ابدأ في كتابة
cellForRowAt
وحدد الطريقة الأولى من الإكمال التلقائي مرة أخرى.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { <#code#> }
ومرة أخرى ، الآن ، إرجاع
UITableViewCell
.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return UITableViewCell() }
الآن ، لتوصيل طريقة عرض الجدول الخاصة بنا داخل
ProfileView
،
ProfileView
جديدًا يأخذ طريقة عرض الجدول كمعلمة ، بحيث يمكنه إضافتها كطريقة عرض فرعية وتعيين القيود المقابلة لها.
انتقل إلى
ProfileView.swift
وأضف سمة عرض الجدول مباشرة أعلى
ProfileView.swift
:
var tableView: UITableView!
مصممة ، لذلك ، نحن لسنا متأكدين من أنه سيكون ثابتا.
استبدل الآن
init (frame :)
بتنفيذ:
init(tableView: UITableView) { super.init(frame: .zero) self.tableView = tableView addSubviews() bringSubview(toFront: avatar) }
سوف يشكو Xcode الآن من
init (frame :)
المفقود
init (frame :)
لـ
ProfileView
، لذا ارجع إلى
ViewController.swift
واسم
let profileView = ProfileView (frame: .zero)
بـ
lazy var profileView: UIView = { return ProfileView(tableView: self.tableView) }()
الآن لدينا
ProfileView
لديه رابط لعرض الجدول ، ويمكننا إضافته بمثابة عرض فرعي وتعيين القيود الصحيحة لذلك.
العودة إلى
ProfileView.swift
، أضف
addSubview(tableView)
في نهاية
addSubviews()
وقم بتعيين هذه القيود على
updateConstraints()
على
super.updateConstraints
:
tableView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) tableView.autoPinEdge(.top, to: .bottom, of: segmentedControl, withOffset: 8)
يضيف السطر الأول ثلاثة قيود بين عرض الجدول وعرضه الفائق: الجوانب اليمنى واليسرى والسفلية لطريقة عرض الجدول متصلة بالجانبين الأيمن والأيسر والأسفل لطريقة عرض الملف الشخصي.
يربط السطر الثاني الجزء العلوي من عرض الجدول بأسفل عنصر التحكم المجزأ بفاصل زمني قدره ثماني نقاط بينهما. قم بتشغيل التطبيق وستكون النتيجة كما يلي:
عظيم ، الآن كل شيء في مكانه ، ويمكننا البدء في تقديم خلايانا.
UITableViewCell
لتنفيذ
UITableViewCell
، سنحتاج دائمًا إلى تصنيف هذه الفئة ، لذا انقر بزر الماوس الأيمن على مجلد
ContactCard
في Project Navigator ، ثم "ملف جديد ..." ، ثم "Cocoa Touch Class" و "Next".
أدخل "UITableViewCell" في الحقل "Subclass of:" ، وسيقوم Xcode تلقائيًا بتعبئة اسم الفصل "TableViewCell". أدخل "ProfileView" قبل الإكمال التلقائي حتى يكون الاسم النهائي هو "ProfileInfoTableViewCell" ، ثم انقر فوق "التالي" و "إنشاء". المضي قدما وحذف الأساليب التي تم إنشاؤها ، لأننا لن نحتاج إليها. إذا أردت ، يمكنك أولاً قراءة أوصافها لفهم سبب عدم حاجتنا إليها في الوقت الحالي.
كما قلنا سابقًا ، ستحتوي خليتنا على معلومات أساسية ، وهي اسم الحقل ووصفه ، وبالتالي نحتاج إلى تسميات لهم.
lazy var titleLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "Title" return label }() lazy var descriptionLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "Description" label.textColor = .gray return label }()
والآن سنعيد تعريف المُهيئ بحيث يمكن تكوين الخلية:
override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(titleLabel) contentView.addSubview(descriptionLabel) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
فيما يتعلق بالقيود ، سنفعل شيئًا مختلفًا بعض الشيء ، ومع ذلك ، فإنه مفيد للغاية:
override func updateConstraints() { let titleInsets = UIEdgeInsetsMake(16, 16, 0, 8) titleLabel.autoPinEdgesToSuperviewEdges(with: titleInsets, excludingEdge: .bottom) let descInsets = UIEdgeInsetsMake(0, 16, 4, 8) descriptionLabel.autoPinEdgesToSuperviewEdges(with: descInsets, excludingEdge: .top) descriptionLabel.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: 16) super.updateConstraints() }
هنا نبدأ في استخدام
UIEdgeInsets
لتعيين التباعد حول كل تسمية. يمكن إنشاء كائن
UIEdgeInsets
باستخدام
UIEdgeInsetsMake(top:, left:, bottom:, right:)
method. على سبيل المثال ، بالنسبة إلى
titleLabel
نقول إننا نريد أن يكون الحد الأعلى أربع نقاط ، واليمين واليسار ثماني نقاط. نحن لا نهتم بالجزء السفلي ، لأننا نستبعده ، لأننا نعلقه في الجزء العلوي من علامة الوصف. يستغرق دقيقة واحدة لقراءة وتصور كل القيد في رأسك.
حسنًا ، الآن يمكننا البدء في رسم الخلايا في عرض الجدول الخاص بنا. دعنا ننتقل إلى
ViewController.swift
وتغيير التهيئة البطيئة لعرض الجدول لدينا لتسجيل هذه الفئة من الخلايا في عرض الجدول وتعيين الارتفاع لكل خلية.
let profileInfoCellReuseIdentifier = "profileInfoCellReuseIdentifier" lazy var tableView: UITableView = { ... tableView.register(ProfileInfoTableViewCell.self, forCellReuseIdentifier: profileInfoCellReuseIdentifier) tableView.rowHeight = 68 return tableView }()
نضيف أيضًا ثابتًا لمعرف إعادة استخدام الخلية. يستخدم هذا المعرف لإزالة الخلايا من عرض الجدول عندما يتم عرضها. هذا هو التحسين الذي يمكن (وينبغي) استخدامه لمساعدة
UITableView
إعادة استخدام الخلايا التي تم تقديمها مسبقًا لعرض محتوى جديد بدلاً من إعادة رسم الخلية الجديدة من البداية.
الآن ، اسمحوا لي أن أريك كيفية إعادة استخدام الخلايا في سطر واحد من التعليمات البرمجية في طريقة
cellForRowAt
:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: profileInfoCellReuseIdentifier, for: indexPath) as! ProfileInfoTableViewCell return cell }
نحن هنا نعلم طريقة عرض الجدول بسحب خلية قابلة لإعادة الاستخدام من قائمة الانتظار باستخدام المعرف الذي سجلنا بموجبه المسار إلى الخلية التي يوشك المستخدم أن يظهر فيها. ثم نجبر الخلية على
ProfileInfoTableViewCell
لتكون قادرة على الوصول إلى خصائصها حتى نتمكن ، على سبيل المثال ، من تعيين العنوان والوصف. يمكن القيام بذلك باستخدام ما يلي:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ... switch indexPath.row { case 0: cell.titleLabel.text = "Phone Number" cell.descriptionLabel.text = "+234567890" case 1: cell.titleLabel.text = "Email" cell.descriptionLabel.text = "john@doe.co" case 2: cell.titleLabel.text = "LinkedIn" cell.descriptionLabel.text = "www.linkedin.com/john-doe" default: break } return cell }
الآن قم بتعيين
numberOfRowsInSection
لإرجاع "3" وإطلاق التطبيق الخاص بك.
حق مدهش؟
خلايا التحجيم الذاتي
ربما ، وعلى الأرجح ، ستكون هناك حالة عندما تريد أن يكون للخلايا المختلفة ارتفاعات وفقًا للمعلومات الموجودة فيها ، وهو أمر غير معروف مقدمًا. للقيام بذلك ، تحتاج إلى عرض جدول بأبعاد محسوبة تلقائيًا ، وفي الواقع هناك طريقة بسيطة جدًا للقيام بذلك.
أولاً وقبل كل شيء ، في
ProfileInfoTableViewCell
أضف هذا السطر إلى
descriptionLabel
المُهيئ البطيء
descriptionLabel
:
label.numberOfLines = 0
العودة إلى
ViewController
وإضافة هذين السطرين إلى مُهيئ عرض الجدول:
lazy var tableView: UITableView = { ... tableView.estimatedRowHeight = 64 tableView.rowHeight = UITableViewAutomaticDimension return tableView }()
نعلم هنا طريقة عرض الجدول بأن ارتفاع الصف يجب أن يكون له قيمة محسوبة تلقائيًا بناءً على محتوياته.
فيما يتعلق بارتفاع الصف المقدر:
"توفير تقدير غير سلبي لارتفاع الصفوف يمكن أن يحسن أداء تحميل عرض الجدول." - مستندات Apple
في
ViewDidLoad
نحتاج إلى إعادة تحميل عرض الجدول لتصبح هذه التغييرات نافذة المفعول:
override func viewDidLoad() { super.viewDidLoad() ... DispatchQueue.main.async { self.tableView.reloadData() } }
تابع الآن وأضف خلية أخرى ، وزاد عدد الصفوف إلى أربعة
cellForRow
switch
أخرى إلى
cellForRow
:
case 3: cell.titleLabel.text = "Address" cell.descriptionLabel.text = "45, Walt Disney St.\n37485, Mickey Mouse State"
قم الآن بتشغيل التطبيق ، ويجب أن يبدو مثل هذا:
استنتاج
حق مدهش؟
وكتذكير بالسبب في قيامنا بترميز واجهة المستخدم الخاصة بنا ، إليك مدونة كاملة مكتوبة بواسطة فريق الجوّال لدينا حول سبب
عدم استخدام لوحات العمل في Instabug .
ماذا فعلت في جزأين من هذا الدرس:
main.storyboard
ملف main.storyboard
من مشروعك.- أنشأنا
UIWindow
برمجياً rootViewController
. - إنشاء عناصر واجهة مستخدم متعددة في التعليمة البرمجية ، مثل التسميات وطرق عرض الصور وعناصر التحكم المقطعية وطرق العرض مع خلاياهم.
UINavigationBar
المتداخلة في التطبيق الخاص بك.- إنشاء حجم ديناميكي
UITableViewCell
.