ZaatarLABS
تواصل معنا ←
→ كل المقالات
ZaatarLABS · هندسة

Liquid Glass، بالأرقام

رقمان — 120 تقطّعًا في الدقيقة و5 — يشرحان كل شيء عن الكلفة الحقيقية لـ Liquid Glass وأين تكمن هذه الكلفة.

4 دقائق قراءة

الرقم الذي استوقفني كان 120. تقطّعات في الدقيقة، وInstruments مفتوح، وعشر بطاقات زجاجية على الشاشة مع تشغيل حركة اللمعان. كنتُ أتعامل مع الاستجابة للإمالة على أنها مجانية تقريبًا — بدت صحيحة، وتتبّعت الضوء كما يفعل الزجاج الحقيقي، وشعرتُ أنها التفصيلة الصحيحة. ثم قستُها.

التحوّل إلى لمعان ثابت على خمس عشرة بطاقة خفّض الرقم إلى خمسة. بطاقات أكثر، بلا حركة: قائمة أملأ، و115 تقطّعًا أقل في الدقيقة. بقي الزجاج. ولم تبقَ الحركة.

تلك الفجوة هي موضوع هذا المقال.

ما استبعدته أولًا

كانت الفرضية البديهية هي عدد البطاقات. أسطح أكثر على الشاشة، عمل أكثر — هذا المنطق يصحّ في كثير من مشكلات العرض، وطاردته أطول مما ينبغي. ما جرّبته فعلًا: الانخفاض من 60 Hz إلى 30. نقل اللمعان من عرض SwiftUI إلى CALayer. تقييد الحركة، بحيث لا تعمل الحركة إلا حين يقول مقياس التسارع إن الجهاز ثابت. تحديد ارتفاع أطول بطاقة كي لا تتجاوز ارتفاعًا ثابتًا. لم يحرّك أيٌّ من ذلك الأرقام بما يكفي ليُحدث فرقًا.

الإصلاح الذي نجح كان أيضًا الأبسط: motionEnabled = false. لمعان ثابت، بلا استجابة للإمالة، بلا أخذ عينات في كل إطار.

ما الذي يفعله النظام فعلًا

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

عشر بطاقات. ستون إطارًا في الثانية. حركة لمعان تعمل فوقها. ستون إعادة تركيب لكل سطح في الثانية، مضروبة في عدد البطاقات الزجاجية في تسلسل العروض. 120 تقطّعًا في الدقيقة.

خمس عشرة بطاقة بلمعان ثابت: تؤخذ عينة الخلفية مرة واحدة وتُحفظ إلى أن يتغيّر شيء. خمسة تقطّعات في الدقيقة.

الحافة اللامعة لا تزال موجودة في النسخة المنشورة. مضاءة من زاوية ثابتة ولا تتحرك أبدًا. ما كان يجب أن يرحل هو الاستجابة للإمالة — الجزء الذي يتتبّع اتجاه الجهاز ويجعل اللمعة تنزاح كلما حرّكت الهاتف. لا تزال البطاقة تبدو زجاجًا. لكنها فقط لا تستجيب للجاذبية.

المساحة هي المحرّك الحقيقي للكلفة

بمجرد أن اتضح نموذج إعادة التركيب، تبعه أمر آخر: الكلفة تتناسب مع مساحة الخلفية، لا مع عدد الأسطح الزجاجية.

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

هذا يغيّر سؤال التصميم. ليس «كم سطحًا زجاجيًا أستطيع تحمّله على هذه الشاشة؟» بل «كم من مساحة العرض زجاج عند أي موضع تمرير؟»

مشكلة البطاقة الطويلة

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

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

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

تفصيلتان في التنفيذ تستحقان المعرفة

يجمع GlassEffectContainer الأسطح الزجاجية المتجاورة في تمريرة تركيب واحدة. إذا كانت لديك عدة عناصر زجاجية في المستوى نفسه من التسلسل — صف من بطاقات الإحصاءات، أو شبكة — فإن تغليفها بالحاوية أمر يستحق العناء. ومن دونها، يُركَّب كل سطح على حدة.

التفصيلة الأخرى: يجب أن يأتي .clipShape قبل .background في سلسلة المعدِّلات.

// Wrong — the background bleeds past the corners
.background(material)
.clipShape(RoundedRectangle(cornerRadius: 16))

// Right
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(material)

مادة الزجاج لا تقصّ عناصرها الفرعية. الصف الذي يرسم خلفيته الخاصة سيرسم زوايا مربعة تتجاوز الشكل المستدير للبطاقة إذا جاء القص بعده. اكتشفتُ هذا بالطريقة الصعبة في قائمة ذات صفوف داخلية.

ما كلّفه ذلك في التصميم

رحلت الاستجابة للإمالة. كانت أوضح سلوك يشبه الزجاج — اللمعة التي تتحرك كلما حرّكت الهاتف — وهي ما تتخلى عنه حين يقول المحلّل 120.

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

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

الملف هو التوثيق

يُنسخ LiquidGlassCard.swift حرفيًا عبر The Smart Dentist وBilling وCoach. عن قصد. قواعد الأداء تنتقل مع الملف بدل أن تعيش في ملاحظة في مكان ما. سياق الـ 120 إلى 5 موجود في المصدر نفسه مع الشيفرة، بجوار المفاتيح والتعليقات، كي لا يضطر من يفتحه بعدي إلى إعادة اكتشاف أيٍّ من هذا.

الأرقام هي وثيقة التصميم. وكل ما عداها تعليق.

بقلم Omar Al Homaidi · ZaatarLABS · @zaatarlabs