0Pricing
Swift Academy · درس

أساسيات بروتوكول Layout

نفّذ sizeThatFits و placeSubviews.

أساسيات بروتوكول Layout درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Swift Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Swift Academy 4 دروس في المجموع.

لماذا تخطيط مخصص؟

توفر SwiftUI العناصر HStack وVStack وZStack، لكنك تحتاج أحيانًا إلى منطق ترتيب لا يمكنها التعبير عنه، مثل القوائم الشعاعية وتخطيطات التدفق والشبكات المتراصة.

يتيح لك بروتوكول Layout كتابة هذا المنطق مع الاستمرار في تركيبه مع عروض SwiftUI العادية.

بروتوكول Layout

يتطلب Layout أسلوبين: sizeThatFits (ما الحجم الذي أحتاج إليه؟) وplaceSubviews (أين يوضع كل عنصر فرعي؟).

تجعل نوعًا من القيم يمتثل له وتستخدمه مثل أي stack مضمّن.

struct MyLayout: Layout {
    func sizeThatFits(proposal: ProposedViewSize,
                      subviews: Subviews,
                      cache: inout ()) -> CGSize { .zero }
    func placeSubviews(in bounds: CGRect,
                       proposal: ProposedViewSize,
                       subviews: Subviews,
                       cache: inout ()) { }
}

استخدام تخطيط مخصص

تعمل قيمة Layout كحاوية. وتمرر العروض الفرعية داخل الإغلاق اللاحق لها تمامًا كما تفعل مع stack.

var body: some View {
    MyLayout {
        Text("One")
        Text("Two")
        Text("Three")
    }
}

ProposedViewSize

يقترح العنصر الأب حجمًا لتخطيطك عبر ProposedViewSize. وتكون خاصيتا width وheight اختياريتين: وتعني nil «غير محدد، اختر الحجم المثالي».

تختبر القيم الخاصة .zero و.infinity و.unspecified الأحجام الدنيا والقصوى والمثالية.

func sizeThatFits(proposal: ProposedViewSize,
                  subviews: Subviews,
                  cache: inout ()) -> CGSize {
    let width = proposal.width ?? 0
    return CGSize(width: width, height: 44)
}

تفاصيل sizeThatFits

يعيد sizeThatFits الحجم الذي يريده تخطيطك وفقًا لاقتراح معين. وقد تستدعيه SwiftUI عدة مرات مع اقتراحات مختلفة لفهم مدى مرونتك.

func sizeThatFits(proposal: ProposedViewSize,
                  subviews: Subviews,
                  cache: inout ()) -> CGSize {
    // Sum child ideal sizes for a simple horizontal stack
    let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
    let w = sizes.reduce(0) { $0 + $1.width }
    let h = sizes.map(\.height).max() ?? 0
    return CGSize(width: w, height: h)
}

مجموعة Subviews

إن Subviews مجموعة من الوكلاء الذين يمثل كل منهم عنصرًا فرعيًا. ويجيب كل وكيل عن sizeThatFits، ويعرض أولويات التخطيط والقيم المخصصة.

لن ترى العروض الحقيقية أبدًا، بل وكلاء القياس هؤلاء فقط.

for subview in subviews {
    let size = subview.sizeThatFits(.unspecified)
    print(size)
}

placeSubviews

يحدد placeSubviews موضع كل عنصر فرعي داخل مستطيل bounds الممنوح لك.

استدعِ place(at:anchor:proposal:) على وكيل كل عرض فرعي.

func placeSubviews(in bounds: CGRect,
                   proposal: ProposedViewSize,
                   subviews: Subviews,
                   cache: inout ()) {
    var x = bounds.minX
    for subview in subviews {
        let size = subview.sizeThatFits(.unspecified)
        subview.place(at: CGPoint(x: x, y: bounds.midY),
                      anchor: .leading,
                      proposal: .unspecified)
        x += size.width
    }
}

نقاط الارتكاز عند الوضع

تحدد المعلمة anchor النقطة من العنصر الفرعي التي تستقر على الموضع الذي تحدده. يضع .topLeading العنصر باستخدام زاويته العلوية اليسرى؛ بينما يوسّطه .center حول النقطة.

subview.place(at: CGPoint(x: 0, y: 0),
              anchor: .topLeading,
              proposal: .unspecified)

لا تبدأ الحدود دائمًا عند الأصل

قد لا يبدأ مستطيل bounds عند (0,0). حدّد الموضع دائمًا نسبةً إلى bounds.minX وbounds.minY، ولا تستخدم الصفر المطلق أبدًا.

let startX = bounds.minX // not 0
let startY = bounds.minY

تخطيط أدنى بتباعد متساوٍ

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

struct SimpleHStack: Layout {
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
        return CGSize(width: sizes.reduce(0){ $0 + $1.width },
                      height: sizes.map(\.height).max() ?? 0)
    }
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        var x = bounds.minX
        for s in subviews {
            let size = s.sizeThatFits(.unspecified)
            s.place(at: CGPoint(x: x, y: bounds.minY), anchor: .topLeading, proposal: .unspecified)
            x += size.width
        }
    }
}

الاتساق مسؤوليتك

تثق SwiftUI بأن يتفق أسلوباك. فإذا استخدم placeSubviews مساحة أكبر من المساحة التي أبلغ عنها sizeThatFits، فقد تُقص العناصر الفرعية أو تتداخل.

حافظ على مشاركة حسابات الهندسة بين الأسلوبين.

تحقق سريع: أساسيات Layout

اختبر مدى استيعابك لبروتوكول Layout.

مراجعة: أساسيات بروتوكول Layout

يحوّل بروتوكول Layout منطق الترتيب المخصص إلى حاوية قابلة لإعادة الاستخدام. ويبلغ sizeThatFits عن حجمك وفقًا لـ ProposedViewSize، بينما يحدد placeSubviews موضع كل وكيل لعنصر فرعي داخل bounds باستخدام place(at:anchor:proposal:).

تحمل الاقتراحات أبعادًا اختيارية، وقد تكون الحدود مزاحة عن الأصل، ويجب أن يظل الأسلوبان متسقين هندسيًا. سننتقل بعد ذلك إلى قياس العروض الفرعية بالتفصيل.

الأسئلة الشائعة

هل درس «أساسيات بروتوكول Layout» مجاني؟

نعم — نص درس «أساسيات بروتوكول Layout» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 4 دروس في المجموع.

ماذا ستتعلم في «أساسيات بروتوكول Layout»؟

نفّذ sizeThatFits و placeSubviews. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟

لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «أساسيات بروتوكول Layout»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟

نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساسيات بروتوكول Layout
  2. قياس العروض الفرعية
  3. إنشاء تخطيط التدفق
  4. ذاكرة التخزين المؤقت للتخطيط والأداء
← العودة إلى Swift Academy