Auto Layout هو نظام تحديد المواقع التكيفي لعناصر الواجهة من Apple، استناداً إلى القيود الرياضية (constraints). تم تطويره لنظام iOS 6 (2012)، ويسمح Auto Layout بإنشاء واجهات تعرض بشكل صحيح على جميع الأجهزة — من iPhone SE (4.7″) إلى iPad Pro (12.9″) و Dynamic Island. وفقاً لجلسة Apple WWDC Session 202 (2024)، أكثر من 90% من التطبيقات في App Store تستخدم Auto Layout أو بديله التصريحي — SwiftUI layout system. تصف Constraints العلاقات بين عناصر الواجهة من خلال معادلات خطية: view1.leading = view2.trailing + 8.
أهم النقاط
Auto Layout هو نظام التخطيط التكيفي من Apple الذي يستخدم قيوداً رياضية (constraints) لتحديد مواضع عناصر الواجهة. على عكس التخطيط القائم على الإطارات (frame-based layout)، حيث يكون لكل عنصر إحداثيات ثابتة x, y, width, height، يصف Auto Layout العلاقات بين العناصر: «الزر يبعد 8pt عن الحافة اليمنى للأصل» أو «عرض حقل النص يساوي نصف عرض الشاشة». الآلية تستند إلى خوارزمية Cassowary، التي طورتها University of Washington (Greg J. Badros, 1999) ونفذتها Apple في iOS 6. يقوم Cassowary بحل نظام من المتباينات الخطية مع أولويات — Required (1000), Default High (750), Default Low (250) — مما يسمح بإدارة تعارضات القيود. يدعم Auto Layout ثلاثة أنواع من الأحجام: intrinsic (الحجم الطبيعي للعنصر المحدد بالمحتوى)، explicit (قيود محددة صراحة) و compressible/stretchable (وضع مرن عبر Content Hugging Priority و Compression Resistance Priority).
لكل عنصر واجهة في Auto Layout Intrinsic Content Size — حجم طبيعي يحدده محتواه. بالنسبة لـ UILabel يعتمد هذا على النص والخط، بالنسبة لـ UIImageView — على أبعاد الصورة. تتحكم Content Hugging Priority (مقاومة التمدد) و Compression Resistance Priority (مقاومة الضغط) في سلوك العنصر عندما تتغير المساحة المتاحة. القيم القياسية: 251 لـ hugging و 749 لـ compression resistance. إذا تنافس عنصران على المساحة، تحدد الأولوية أيهما يتمدد أولاً. فهم هذه الأولويات هو مفتاح حل Ambiguous Layout (التخطيط الغامض)، الذي يبرزه Xcode في المصحح.
يتم وصف الـ constraint بمعادلة: view1.attribute = multiplier × view2.attribute + constant. تشمل السمات leading, trailing, top, bottom, centerX, centerY, width, height, firstBaseline, lastBaseline. يستخدم multiplier للعلاقات النسبية (عرض view1 = 0.5 × عرض superview). يحدد constant إزاحة ثابتة (leading = superview.leading + 16). تتيح أدوات Interface Builder إنشاء constraints بصرياً عبر السحب بزر Ctrl، لكن التخطيطات المعقدة تتطلب إنشاءً برمجياً عبر NSLayoutConstraint أو VFL (Visual Format Language)، الذي توصي Apple باستبداله بـ NSLayoutConstraint منذ iOS 9.
يتم حل نظام الـ constraints كمسألة برمجة خطية: تجد خوارزمية Cassowary الترتيب الأمثل لجميع العناصر الذي يلبي جميع القيود مع مراعاة أولوياتها. إذا تناقضت الـ constraints مع بعضها، يحدث Unsatisfiable Layout — استثناء يسجله Xcode مع وصف تفصيلي للتعارض. إذا لم تكن هناك قيود كافية لتحديد موضع عنصر واحد على الأقل، يحدث Ambiguous Layout — تظهر العناصر في مواضع عشوائية. توصي Apple بمجموعة دنيا: لكل عنصر يجب تعيين position (x, y) و size (width, height) — صراحة أو عبر intrinsic content size. يمكن أن تكون الـ constraints من الدرجة الأولى: العنصر القائد (مثل superview) والتابع (العرض الفرعي) ينشئان تسلسلاً هرمياً.
يستخدم Cassowary طريقة Sequential Quadratic Programming لحل أنظمة المتباينات الخطية. لكل constraint أولوية من 1 إلى 1000. Required (1000) هو قيد إلزامي؛ إذا تعذر تحقيقه، يتعطل التطبيق مع NSConstraintException. Default High (750) توصية؛ Default Low (250) الأقل أهمية. أثناء التعارض، يخفف Cassowary القيود ذات الأولوية الأقل. على سبيل المثال، إذا كان عنصران يتطلبان عرضاً ثابتاً لكن الشاشة ضيقة جداً، يتم تخفيف الـ constraint ذي الأولوية الأقل. في Xcode Debug View Hierarchy (أداة تصحيح متاحة منذ Xcode 6) يبرز فقط المشاكل مع Required constraints — الباقي يتم معالجته بدون خطأ.
UIStackView هو حاوية تم تقديمها في iOS 9 (2015) تقوم تلقائياً بإنشاء وإدارة constraints للعروض المتداخلة arrangedSubviews. يدعم UIStackView محورين: أفقي ورأسي. تحدد إعدادات distribution توزيع المساحة: fill (تعبئة نسبية حسب hugging priority), fillEqually (أحجام متساوية), fillProportionally (نسبي إلى intrinsic content size), equalSpacing (تباعد متساوٍ), equalCentering (مسافات متساوية بين المراكز). يحدد alignment المحاذاة العرضية: fill, leading, center, trailing (للأفقي) أو fill, top, center, bottom (للرأسي). يدير UIStackView تلقائياً التباعد ومحاذاة خط الأساس والتكيف مع Dynamic Type.
import UIKit
class StackViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stack = UIStackView()
stack.axis = NSLayoutConstraint.Axis.vertical
stack.distribution = .fillEqually
stack.spacing = 8
stack.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Auto Layout Guide"
label.font = UIFont.preferredFont(forTextStyle: .headline)
let button = UIButton(type: .system)
button.setTitle("Apply", for: .normal)
stack.addArrangedSubview(label)
stack.addArrangedSubview(button)
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stack.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 16),
stack.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -16)
])
}
}ينشئ الكود UIStackView عمودياً بعنصرين (UILabel و UIButton)، موزعين بالتساوي (fillEqually) بتباعد 8pt. يتم توسيط الـ stack على الشاشة بهوامش لا تقل عن 16pt من الحواف. translatesAutoresizingMaskIntoConstraints = false إلزامي عند إنشاء constraints برمجياً — بدونه لا يعمل Auto Layout. في IT Sectr، يُستخدم UIStackView في 80% من شاشات مشاريع iOS لبناء نماذج تكيفية وقوائم إعدادات وبطاقات.
يمكن تداخل UIStackView: stack أفقي داخل stack رأسي هو نمط قياسي للتخطيطات المعقدة. يدير الـ stack الخارجي الصفوف، ويدير الداخلي الأعمدة داخل كل صف. يوفر الجمع بين axis و alignment و distribution في كل مستوى مرونة غير محدودة تقريباً دون أي constraint يدوي واحد. توصي Apple باستخدام UIStackView كأداة التخطيط الرئيسية في UIKit، مع اللجوء إلى NSLayoutConstraint اليدوي فقط للحالات التي لا تغطيها الـ stacks: العروض المتداخلة، تحديد المواقع بدقة البكسل، الرسوم المتحركة المخصصة للـ bounds.
NSLayoutConstraint هو API برمجي لإنشاء قيود فردية في الكود. يتم إنشاء كل constraint عبر مُهيئ مع معاملات: item, attribute, relatedBy, toItem, attribute, multiplier, constant. منذ iOS 9، قدمت Apple Anchor API — بناء جملة أكثر قابلية للقراءة عبر خصائص view.leadingAnchor, view.trailingAnchor, view.topAnchor, view.bottomAnchor, view.centerXAnchor, view.centerYAnchor, view.widthAnchor, view.heightAnchor. يقوم Anchor API تلقائياً بتعيين relatedBy = .equal ويستخدم First Item/Second Item من Anchors، مما يقلل الكود بنسبة 40% مقارنة بـ NSLayoutConstraint الكلاسيكي.
import UIKit
class ConstraintViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIView()
childView.backgroundColor = .systemBlue
childView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(childView)
NSLayoutConstraint.activate([
childView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24),
childView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
childView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
childView.heightAnchor.constraint(equalToConstant: 120),
childView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -24)
])
}
}يقوم الكود بتحديد موضع childView بهوامش من safeAreaLayoutGuide (top) وحواف الشاشة (leading/trailing). يضمن lessThanOrEqualTo للـ bottom عدم تجاوز العرض للحد السفلي. يلقي Anchor API استثناء في وقت الترجمة إذا كانت الـ anchors غير متوافقة (مثلاً، خلط leadingAnchor مع rightAnchor)، مما يمنع أخطاء وقت التشغيل. توصي Apple بـ Anchor API كمعيار لـ Auto Layout البرمجي منذ iOS 9.
Safe Area هي منطقة الشاشة التي لا تغطيها عناصر النظام: Dynamic Island و Notch و Status Bar و Home Indicator والزوايا الدائرية. في iOS 11، استبدلت Apple topLayoutGuide/bottomLayoutGuide بـ safeAreaLayoutGuide، الذي يتكيف تلقائياً مع اتجاه الجهاز ووجود قطع الشاشة. Layout Margins هي هوامش داخلية افتراضية للعرض (16pt في iOS، 20pt في iPadOS). بالنسبة لـ UILayoutGuide يمكن تعيين directionalLayoutMargins مخصصة مع مراعاة التوطين RIGHT-TO-LEFT. يحترم Auto Layout تلقائياً safe area عند استخدام safeAreaLayoutGuide في الـ anchors.
على الأجهزة ذات Dynamic Island (iPhone 14 Pro والإصدارات الأحدث) و Notch (iPhone X–13)، تستثني Safe Area 44pt من الأعلى في الوضع العمودي (59pt مع Dynamic Island في الحالة النشطة). يضيف Home Indicator 34pt من الأسفل. للتكيف الصحيح، يجب ربط جميع constraints العلوية بـ safeAreaLayoutGuide.topAnchor، وليس view.topAnchor. يجب ربط constraints السفلية بـ safeAreaLayoutGuide.bottomAnchor أو view.bottomAnchor مع هامش لـ Home Indicator. في IT Sectr، نختبر جميع الشاشات على محاكيات iPhone SE (2022) و iPhone 14 Pro Max و iPad Pro 12.9″ — ثلاثة أجهزة تغطي جميع متغيرات safe area.
الأخطاء الأكثر شيوعاً عند العمل مع Auto Layout: نسيان translatesAutoresizingMaskIntoConstraints = false، تعارض Required constraints (الأولوية 1000)، Ambiguous Layout (قيود غير كافية لتحديد الموضع)، Content Hugging Priority غير صحيح لـ UILabel متعدد الأسطر وخلط anchors leading/trailing مع left/right. يعرض Xcode 15+ مشكلات التخطيط في Runtime Issue Navigator ويقدم تصحيحات تلقائية. للتخطيطات المعقدة، استخدم Debug View Hierarchy: العلامات الصفراء تشير إلى ambiguous layout، الحمراء تشير إلى unsatisfiable.
| الخطأ | السبب | الحل |
|---|---|---|
| translatesAutoresizingMaskIntoConstraints = true | Auto Layout غير مفعل للعرض | تعيين false لجميع العروض البرمجية |
| Unsatisfiable Layout | تعارض Required (1000) constraints | خفض أولوية أحدها إلى Default High (750) |
| Ambiguous Layout | قيود غير كافية لـ x/y/w/h | إضافة constraint مفقود أو التحقق من intrinsic size |
| اقتطاع النص في UILabel | Content Hugging Priority أقل من المنافس | رفع hugging priority إلى 252+ |
| خلط anchors LTR/RTL | leadingAnchor مع rightAnchor | استخدام leading/trailing فقط لدعم RTL |
الأسئلة المتكررة
التخطيط القائم على الإطارات يحدد إحداثيات ثابتة x, y, width, height لكل عنصر. يستخدم Auto Layout قيوداً رياضية (constraints) — علاقات بين العناصر: «label.leading = button.trailing + 8». التخطيط القائم على الإطارات لا يتكيف مع حجم الشاشة؛ يعيد Auto Layout حساب المواضع تلقائياً عند التدوير أو Split View أو تغيير Dynamic Type.
UIStackView مثالي للتخطيطات الخطية: الصفوف والأعمدة والنماذج وقوائم المعلمات. NSLayoutConstraint ضروري للعروض المتداخلة وتحديد المواقع بدقة البكسل والرسوم المتحركة المخصصة للـ bounds والحالات التي يكون فيها توزيع المساحة غير متساوٍ ولا يغطيه distribution في UIStackView. عملياً، 80% من التخطيطات تُحل بـ UIStackView و 20% بـ constraints يدوية.
Content Hugging Priority (مقاومة التمدد) هي أولوية تحدد مدى مقاومة العنصر لزيادة حجمه فوق Intrinsic Content Size. القيمة الافتراضية هي 251. إذا تنافس عنصران على مساحة حرة، يبقى العنصر ذو hugging priority الأعلى بحجمه بينما يتمدد الثاني. Compression Resistance Priority (749 افتراضياً) تعمل بالمثل للضغط.
يتكيف Auto Layout تلقائياً مع Dynamic Type إذا كانت الـ constraints تستخدم intrinsic content size للتسميات. عند زيادة حجم الخط، يتمدد UILabel مزاحاً العناصر المجاورة عبر الـ constraints. يعيد UIStackView مع distribution = fillProportionally توزيع المساحة نسبياً مع الأحجام الجديدة. Safe Area و Layout Margins تراعي أيضاً إعدادات إمكانية الوصول.
Unsatisfiable Layout يحدث عندما يتعارض اثنان من Required (priority = 1000) constraints مع بعضهما: مثلاً، view.leading = superview.leading + 16 و view.trailing = superview.leading + 200 مع superview عرضه 100pt. لا تستطيع خوارزمية Cassowary إيجاد حل ويتعطل التطبيق مع NSConstraintException. الحل هو خفض أولوية أحد الـ constraints المتعارضة إلى Default High (750).
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.