0Pricing
Swift Academy · درس

إنشاء تخطيط التدفق

وزّع العناصر على أسطر ديناميكيًا.

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

ما تخطيط التدفق؟

يضع تخطيط التدفق العناصر من اليسار إلى اليمين، ويلتف إلى صف جديد عندما يؤدي وضع العنصر التالي إلى تجاوز العرض المتاح، كما يحدث مع شرائح الوسوم أو التفاف الكلمات.

الخطة

سنحسب فواصل الصفوف في دالة مساعدة مشتركة، ونعيد الارتفاع الكلي من sizeThatFits، ثم نعيد استخدام الحساب نفسه في placeSubviews. تؤدي مشاركة المنطق إلى إبقاء الدالتين متسقتين.

نمذجة التموضع

يسجّل هيكل صغير موضع كل عرض فرعي. ويجعل حساب جميع مواضع التموضع مسبقًا كلتا الدالتين التابعتين للبروتوكول بسيطتين.

struct Placement {
    let index: Int
    let point: CGPoint
    let size: CGSize
}

خوارزمية الالتفاف

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

func layout(_ subviews: Subviews, maxWidth: CGFloat) -> (placements: [Placement], height: CGFloat) {
    var placements: [Placement] = []
    var x: CGFloat = 0, y: CGFloat = 0, rowHeight: CGFloat = 0
    let spacing: CGFloat = 8
    for (i, s) in subviews.enumerated() {
        let size = s.sizeThatFits(.unspecified)
        if x + size.width > maxWidth, x > 0 {
            x = 0; y += rowHeight + spacing; rowHeight = 0
        }
        placements.append(Placement(index: i, point: CGPoint(x: x, y: y), size: size))
        x += size.width + spacing
        rowHeight = max(rowHeight, size.height)
    }
    return (placements, y + rowHeight)
}

تنفيذ sizeThatFits

تشغّل sizeThatFits الخوارزمية باستخدام العرض المقترح، وتعيد عرض الاقتراح مضروبًا في الارتفاع الكلي المحسوب.

func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
    let maxWidth = proposal.width ?? .infinity
    let result = layout(subviews, maxWidth: maxWidth)
    return CGSize(width: maxWidth == .infinity ? result.placements.map { $0.point.x + $0.size.width }.max() ?? 0 : maxWidth,
                  height: result.height)
}

تنفيذ placeSubviews

تشغّل placeSubviews الخوارزمية نفسها، ثم تُزيح كل موضع تموضع بمقدار bounds.origin وتستدعي place.

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
    let result = layout(subviews, maxWidth: bounds.width)
    for p in result.placements {
        let pt = CGPoint(x: bounds.minX + p.point.x, y: bounds.minY + p.point.y)
        subviews[p.index].place(at: pt, anchor: .topLeading,
                                proposal: ProposedViewSize(p.size))
    }
}

النوع الكامل FlowLayout

يؤدي جمع الأجزاء إلى تخطيط تدفق كامل قابل لإعادة الاستخدام. وتضمن الدالة المساعدة المشتركة layout توافق الحجم مع التموضع.

struct FlowLayout: Layout {
    var spacing: CGFloat = 8
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let r = layout(subviews, maxWidth: proposal.width ?? .infinity)
        return CGSize(width: proposal.width ?? 0, height: r.height)
    }
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        let r = layout(subviews, maxWidth: bounds.width)
        for p in r.placements {
            subviews[p.index].place(at: CGPoint(x: bounds.minX + p.point.x, y: bounds.minY + p.point.y),
                                    anchor: .topLeading, proposal: ProposedViewSize(p.size))
        }
    }
}

استخدام تخطيط التدفق

أدرجوه مثل أي حاوية أخرى. ستلتف الوسوم تلقائيًا عند تقلّص العرض.

var body: some View {
    FlowLayout {
        ForEach(tags, id: \.self) { tag in
            Text(tag)
                .padding(8)
                .background(.blue.opacity(0.2))
                .clipShape(Capsule())
        }
    }
}

معالجة العنصر الأول في كل صف

يمنع الشرط x > 0 الالتفاف قبل العنصر الأول في الصف. ومن دونه، قد يدخل عنصر أعرض من الحاوية في حلقة لا نهائية أو يُدفَع بلا داعٍ إلى صف فارغ.

if x + size.width > maxWidth, x > 0 {
    // wrap only if the row already has content
}

خيارات التباعد

يُخزَّن التباعد هنا كخاصية لها قيمة افتراضية. أما في تطبيقات الإنتاج، فاقرؤوا تفضيل spacing لكل عرض فرعي لمراعاة مقاييس النظام بدلًا من استخدام ثابت مضمَّن في الشيفرة.

var spacing: CGFloat = 8 // configurable per instance

اختبار الحالات الحدّية

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

اختبار سريع: تخطيط التدفق

اختبروا مدى فهمكم لخوارزمية الالتفاف.

خلاصة: إنشاء تخطيط تدفق

يلف تخطيط التدفق العناصر من خلال تتبّع قيمة x المتزايدة وإعادة تعيينها إلى صف جديد عندما يؤدي وضع العنصر الفرعي التالي إلى تجاوز العرض. تحسب دالة مساعدة مشتركة جميع مواضع التموضع والارتفاع الكلي، وبذلك يظل كل من sizeThatFits وplaceSubviews متسقًا.

أزيحوا مواضع التموضع بمقدار bounds.origin، وتحققوا من العنصر الأول في كل صف، واختبروا حالات المحتوى الفارغ والعناصر الأكبر من الحاوية وكثرة العناصر. والنتيجة حاوية قابلة لإعادة الاستخدام لالتفاف العناصر.

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

هل درس «إنشاء تخطيط التدفق» مجاني؟

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

ماذا ستتعلم في «إنشاء تخطيط التدفق»؟

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

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

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

كم من الوقت يستغرق درس «إنشاء تخطيط التدفق»؟

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

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

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

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

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