SwiftUI لآخر مهمة تنافسية لـ Telegram Charts (مارس 2019): كل شيء بسيط



سأبدأ بالملاحظة بأن التطبيق الذي تمت مناقشته في هذه المقالة يتطلب Xcode 11 و MacOS Catalina إذا كنت تريد استخدام Live Previews و Mojave إذا كنت تستخدم جهاز المحاكاة. رمز التطبيق على جيثب .

هذا العام في WWDC 2019 ، أعلنت Apple عن SwiftUI ، وهي طريقة SwiftUI جديدة لبناء واجهة مستخدم (UI) على جميع أجهزة Apple . يعد هذا خروجًا تقريبًا عن UIKit المعتادة ، وأنا - مثل العديد من المطورين الآخرين - أردت حقًا رؤية هذه الأداة الجديدة قيد التنفيذ.

تقدم هذه المقالة تجربة حل مع SwiftUI مشكلة يكون UIKit داخل UIKit أكثر تعقيدًا بشكل لا يُضاهى ولا يمكن UIKit في رأيي بطريقة يمكن قراءتها.

تتعلق المهمة بمسابقة Telegram الأخيرة لمطوري Android و iOS و JS ، والتي عقدت في الفترة من 10 مارس إلى 24 مارس 2019. في هذه المسابقة ، تم اقتراح مهمة بسيطة لعرض شدة استخدام مورد معين على الإنترنت وفقًا للوقت بناءً على بيانات JSON . كمطور iOS ، يجب عليك استخدام Swift لإرسال التعليمات البرمجية المكتوبة من البداية إلى المنافسة دون استخدام أي مكتبات متخصصة غريبة للتخطيط.

تتطلب هذه المهمة مهارات للعمل مع إمكانات الرسم والرسوم المتحركة لنظام iOS: Core Graphics ، Core Animation ، Metal ، OpenGL ES . بعض هذه الأدوات هي أدوات برمجة منخفضة المستوى وغير موجهة للكائنات. في الأساس ، في iOS لم تكن هناك قوالب مقبولة لحل مثل هذه المهام التي تبدو خفيفة للوهلة الأولى. لذلك ، اخترع كل متسابق رسامًا خاصًا به ( Render ) استنادًا إلى Metal ، CALayers ، OpenGL ، CADisplayLink . تم إنشاء هذا الكيلومترات من الكود الذي لم يكن من الممكن استعارة أي شيء وتطويره ، حيث إنها أعمال "محمية بحقوق طبع" بحتة لا يمكن إلا للمؤلفين تطويرها. ومع ذلك ، هذا لا ينبغي أن يكون كذلك.

وفي أوائل يونيو في WWDC 2019 ، يظهر SwifUI - framework جديد تم تطويره بواسطة Apple ، مكتوبًا في Swift ومصمم لوصف واجهة المستخدم ( UI ) في الشفرة بشكل SwifUI . يمكنك تحديد subviews التي subviews عرضها في View الخاصة بك ، وما هي البيانات التي تؤدي إلى تغيير هذه subviews ، والمعدلات التي تحتاج إلى تطبيقها عليها ، لجعلها في المكان المناسب ، ولديها الحجم والنمط المناسبين. من العناصر التي لا تقل أهمية في SwiftUI هو التحكم في تدفق البيانات القابلة للتعديل بواسطة المستخدم ، والتي بدورها تقوم بتحديث UI .

في هذه المقالة ، أريد أن SwiftUI تم حل مهمة مسابقة Telegram على SwiftUI بسرعة وسهولة. بالإضافة إلى ذلك ، هذه عملية مثيرة للغاية.

مهمة


يجب أن يعرض التطبيق التنافسي في وقت واحد 5 "مجموعات من الرسوم البيانية" على الشاشة باستخدام البيانات المقدمة من Telegram . بالنسبة لـ "مجموعة من المخططات" ، تكون UI كما يلي:



يوجد في الجزء العلوي "منطقة مخطط" مع نطاق مشترك على طول المحور Y العادي مع علامات وخطوط الشبكة الأفقية. أسفل ذلك يوجد خط زاحف مع طوابع زمنية على طول المحور X كتواريخ.

والأقل من ذلك هو ما يسمى بـ "الخريطة المصغرة" (كما في Xcode 11 ) ، أي "نافذة" شفافة تحدد هذا الجزء من الفترة الزمنية لـ "المخططات" الخاصة بنا ، والتي يتم تقديمها بمزيد من التفصيل في "مخطط الرسوم البيانية" العلوي. لا يمكن نقل هذه "الخريطة المصغرة" فقط على طول المحور X ، ولكن أيضًا يمكن تغيير عرضها ، مما يؤثر على النطاق الزمني في "منطقة المخططات".

بمساعدة checkboxs المطلية بألوان "الرسوم البيانية" والمقدمة بأسمائها ، يمكنك رفض إظهار "الرسومات" المقابلة لهذا اللون في "منطقة المخططات".

هناك العديد من "مجموعات الرسوم البيانية" ، في مثال الاختبار الخاص بنا ، يوجد 5 منها ، على سبيل المثال ، ويجب أن تكون جميعها موجودة على شاشة واحدة.

في UI المصممة باستخدام SwiftUI ليست هناك حاجة لزر للتبديل بين وضعي Dark Light ، وهو مدمج بالفعل في SwiftUI . بالإضافة إلى ذلك ، SwiftUI خيارات أكثر بكثير للجمع بين "مجموعات المخططات" (أي مجموعات الشاشات المعروضة أعلاه) من مجرد طاولة التمرير لأسفل ، وسوف ننظر في بعض هذه الخيارات المثيرة للاهتمام للغاية.

ولكن أولاً ، دعنا نركز على عرض "مجموعة ChartView " واحدة والتي SwiftUI بإنشاء ChartView :



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

  • GraphsForChart - هذه هي الرسوم البيانية نفسها ، GraphsForChart أجل "مجموعة من الرسوم البيانية" محددة واحدة. يتم عرض "الرسوم البيانية" للنطاق الزمني الذي يتحكم فيه المستخدم باستخدام "الخريطة المصغرة" RangeView ، والتي سيتم عرضها أدناه.
  • YTickerView هو المحور Y مع الارتفاعات والشبكة الأفقية المقابلة.
  • IndicatorView هو IndicatorView أفقي يحركه المستخدم ويسمح لك بمشاهدة قيم "الرسوم البيانية" ووقت موضع المؤشر المقابل على محور الوقت على المحور X
  • TickerView - "خط الزحف" يعرض الطوابع الزمنية على المحور X كتواريخ ،
  • RangeView - "نافذة" مؤقتة ، قابلة للتخصيص من قبل المستخدم باستخدام الإيماءات ، لضبط الفاصل الزمني لـ "المخططات" ،
  • CheckMarksView - يحتوي على "أزرار" ملونة بألوان "الرسوم البيانية" وتتيح لك التحكم في وجود " ChartView " على ChartView .

يمكن للمستخدم التفاعل مع ChartView بثلاث طرق:

1. التحكم في "الخريطة المصغرة" باستخدام لفتة DragGesture - يمكن أن تحول "النافذة" المؤقتة إلى اليمين واليسار وتقليل / زيادة حجمها:



2. انقل المؤشر في الاتجاه الأفقي ، مبينًا قيم "الرسوم البيانية" في نقطة زمنية محددة:



3. إخفاء / إظهار "مخططات" معينة باستخدام أزرار ملونة بألوان "الرسوم البيانية" والموجودة في الجزء السفلي من ChartView :



يمكننا الجمع بين "مجموعات المخططات" المختلفة (لدينا 5 منها في بيانات الاختبار) بطرق مختلفة ، على سبيل المثال ، عن طريق وضعها في وقت واحد على شاشة واحدة باستخدام List القائمة (مثل جدول قابل للتمرير لأعلى ولأسفل):



أو استخدام ScrollView ومكدس أفقي لـ HStack مع تأثير ثلاثي الأبعاد:



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



في وحدات UI المعقدة هذه - "طاولة قابلة للتمرير" ، كومة أفقية ذات تأثير 3D ، و ZStack "البطاقات" ZStack على بعضها البعض - تعمل جميع وسائل تفاعل المستخدم بشكل كامل: التحرك على طول الخط الزمني وتغيير "مقياس" أزرار mini - map والمؤشر والاختباء "الرسومات".

علاوة على ذلك ، سننظر بالتفصيل في تصميم UI هذه باستخدام SwiftUI - بدءًا من العناصر البسيطة وحتى التركيبات الأكثر تعقيدًا. لكن أولاً ، دعونا نفهم بنية البيانات التي لدينا.

لذلك ، تم تقسيم حل مشكلتنا إلى عدة مراحل:

  • قم بتنزيل البيانات من ملف JSON وتقديمها بتنسيق "داخلي" مناسب
  • إنشاء UI واحدة لـ "مجموعة من المخططات"
  • الجمع بين مختلف "مجموعات الرسم البياني"

تحميل البيانات


تحت تصرفنا ، قدمت Telegram بيانات JSON تحتوي على "مجموعات من الرسوم البيانية". تحتوي كل "مجموعة chart " فردية من chart على عدة "مخططات" (أو "خطوط") من chart.columns . لكل "رسم بياني" ("خطوط") علامة في الموضع 0 - "x" ، "y0" ، "y1" ، "y2" ، "y3" ، متبوعة بقيم زمنية على المحور X ("x") أو قيم "Graphics" ("Lines") ( "y0" ، "y1" ، "y2" ، "y3" ) على المحور Y :



وجود جميع "الخطوط" في "مجموعة المخططات" اختياري. قيم "العمود" x هي الطوابع الزمنية UNIX بالمللي ثانية.

بالإضافة إلى ذلك ، يتم تزويد كل "مجموعة chart " فردية من chart بألوان chart.colors بتنسيق مكون من 6 أرقام ست عشرية (على سبيل المثال ، "#AAAAAA") و chart.names .

لبناء نموذج البيانات الموجود في ملف JSON ، استخدمت خدمة quicktype الممتازة. في هذا الموقع ، تقوم بإدخال جزء من النص من ملف JSON وتحديد لغة البرمجة ( Swift ) ، اسم البنية ( Chart ) ، والتي سيتم تشكيلها بعد "تحليل" بيانات JSON هذه وتلك هي.

يتم إنشاء رمز في الجزء المركزي من الشاشة ، والذي نقوم بنسخه في تطبيقنا في ملف منفصل باسم Chart.swift . هذا هو المكان الذي سنضع فيه نموذج بيانات JSON. باستخدام محمل البيانات من ملف JSON إلى النموذج المقترض من العروض التوضيحية Generic SwiftUI ، حصلت على مجموعة من columns: [ChartElement] ، وهي عبارة عن مجموعة من "مجموعات columns: [ChartElement] " بتنسيق Telegram .

ChartElement بيانات ChartElement ، التي تحتوي على صفائف من العناصر غير المتجانسة ، ليست مناسبة جدًا للعمل التفاعلي المكثف مع المخططات ، بالإضافة إلى ذلك ، يتم تقديم الطوابع الزمنية بتنسيق UNIX بالمللي ثانية (على سبيل المثال ، 1542412800000, 1542499200000, 1542585600000, 1542672000000 ) ، والألوان بتنسيق سداسي عشري الأرقام (على سبيل المثال ، "#AAAAAA" ).

لذلك ، داخل تطبيقنا سوف نستخدم نفس البيانات ، ولكن بتنسيق مختلف "داخلي" وبسيط إلى حد ما [LinesSet] . الصفيف [LinesSet] عبارة عن مجموعة من "مجموعات LinesSet " LinesSet ، كل منها يحتوي على طوابع زمنية xTime بالتنسيق "Feb 12, 2019" (محور X ) والعديد من lines "الرسوم البيانية" (المحور Y ):



يتم تقديم بيانات كل مخطط خطي (خط)

  • مجموعة من points: [Int] الصحيحة points: [Int] ،
  • المسمى "الرسومات" title: String ،
  • اكتب "الرسومات" type: String? .
  • color : UIColor في تنسيق UIColor Swift ،
  • عدد النقاط countY: Int .

بالإضافة إلى ذلك ، يمكن إخفاء أي "رسم بياني" أو إظهاره اعتمادًا على قيمة isHidden: Bool . upperBound و upperBound لضبط النطاق الزمني قيمًا من 0 إلى 1 ولا تُظهر فقط حجم إطار وقت "الخريطة المصغرة" ( upperBound - lowerBound ) ، ولكن أيضًا موقعها على المحور الزمني X :



توجد بنيات البيانات JSON [ChartElement] وهياكل البيانات LinesSet و Line "الداخلية" في ملف Chart.swift . يوجد رمز تحميل بيانات JSON وتحويلها إلى بنية داخلية في ملف Data.swift . يمكن الاطلاع على تفاصيل حول هذه التحولات هنا .

نتيجة لذلك ، تلقينا بيانات حول "مجموعات chartsData " بالتنسيق الداخلي كمجموعة من chartsData .



هذا هو البيانات الخاص بنا ، ولكن للعمل في SwiftUI من الضروري التأكد من أن أي تغييرات يقوم بها المستخدم في صفيف chartsData (تغيير "النافذة" المؤقتة ، إخفاء / إظهار "المخططات") تؤدي إلى تحديثات تلقائية chartsData .

سنقوم بإنشاء @EnvironmentObject . سيتيح لنا ذلك استخدام البيانات أينما دعت الحاجة ، وبالإضافة إلى ذلك ، نقوم تلقائيًا بتحديث Views بنا إذا تغيرت البيانات. هذا شيء مثل Singleton أو البيانات العالمية.

@EnvironmentObject يتطلب منا إنشاء بعض final class UserData ، والذي يقع في ملف UserData.swift ، بتخزين البيانات chartsData وتنفذ بروتوكول ObservableObject :



سيسمح لك وجود "wrappers" @Published بنشر "أخبار" بأن هذه الخصائص charts فئة UserData قد تغيرت ، حتى تتمكن أي Views "مشترك في هذا الخبر" في SwiftUI من تحديد البيانات والتحديثات الجديدة تلقائيًا.

تذكر أنه في خاصية charts ، يمكن تغيير القيم isHidden لأي " isHidden " (تسمح لك بإخفاء أو إظهار "المخططات" هذه) ، بالإضافة إلى الجزء السفلي السفلي lowerBound العلوي العلوي upperBound الفاصل الزمني لكل "مجموعة من المخططات" الفردية.

نرغب في استخدام خاصية charts لفئة UserData جميع أنحاء تطبيقنا وليس لدينا مزامنة مع UI يدويًا بفضل @EnvironmentObject .

للقيام بذلك ، عند بدء تشغيل التطبيق ، يجب علينا إنشاء مثيل لفئة UserData () حتى نتمكن لاحقًا من الوصول إليه في أي مكان في تطبيقنا. سنفعل ذلك في ملف SceneDelegate.swift داخل scene (_ : , willConnectTo: , options: ) method. هذا هو المكان الذي يتم فيه إنشاء ContentView ، وهنا يجب علينا تمرير ContentView أي @EnvironmentObject حتى يتمكن SwiftUI إتاحتها لأي View أخرى:



الآن ، في أي View للوصول إلى البيانات @Published من فئة UserData ، نحتاج إلى إنشاء متغير var باستخدام برنامج التضمين @EnvironmentObject . على سبيل المثال ، عند تعيين النطاق الزمني في RangeView نقوم بإنشاء متغير var userData باستخدام UserData TYPE:



لذلك ، ما أن ننفذ بعضًا من @EnvironmentObject في "بيئة" التطبيق ، يمكننا على الفور البدء في استخدامه إما على أعلى مستوى أو في المستوى العاشر أدناه - لا يهم. ولكن الأهم من ذلك أنه كلما تغيرت View "البيئة" ، سيتم تلقائيًا @EnvironmentObject جميع Views التي تحتوي على @EnvironmentObject ، مما يضمن التزامن مع البيانات.

دعنا ننتقل إلى تصميم واجهة المستخدم ( UI ).

واجهة المستخدم (UI) لمجموعة واحدة من الرسوم البيانية


يقدم SwiftUI تقنية مركبة لإنشاء SwiftUI من العديد من Views الصغيرة ، وقد رأينا بالفعل أن تطبيقنا يتناسب تمامًا مع هذه التكنولوجيا ، حيث ينقسم إلى أجزاء صغيرة: مجموعة " ChartView Charts" ، " GraphsForChart Charts" ، Y -axis YTickerView - YTickerView ، قيمة المؤشر التي يحركها المستخدم لـ "Charts" IndicatorView ، " TickerView " TickerView مع TickerView زمنية على المحور X ، "إطار زمني" يتم التحكم فيه بواسطة المستخدم ، علامات على إخفاء / إظهار "مخططات" CheckMarksView . لا يمكننا فقط إنشاء كل هذه Views بشكل مستقل عن بعضنا البعض ، ولكننا أيضًا نختبر فورًا في Xcode 11 باستخدام Previews (المشاهدات الأولية "المباشرة") على بيانات الاختبار. سوف تفاجأ بمدى بساطة الشفرة في إنشائها من Views أكثر أساسية.

الرسم البياني - "الرسم البياني" ("الخط")


View الأول ، الذي سنبدأ به ، هو في الواقع "الرسم البياني" نفسه (أو "الخط"). سوف نسميها GraphView :



يبدأ إنشاء GraphView ، كالمعتاد ، بإنشاء ملف جديد في Xcode 11 باستخدام القائمة FileNewFile :



ثم نختار نوع الملف المطلوب - هذا هو ملف SwiftUI :



... أعط اسم "GraphView" إلى View بنا وأشير إلى موقعه:



انقر فوق الزر "Create" واحصل على View قياسي مع Text ( "Hello World!") في منتصف الشاشة:



مهمتنا هي استبدال Text ("Hello World!") "Graph" ، ولكن أولاً ، دعونا نرى البيانات الأولية التي لدينا لإنشاء "Graph":

  • لدينا قيم line.points line: Line "Graphics" line: Line ،
  • نطاق الوقت rangeTime ، وهو عبارة عن مجموعة من الفهارس Range الطوابع الزمنية xTime على المحور X ،
  • مجموعة من القيم rangeY: Range "Graphics" rangeY: Range Y ،
  • سمك خط السكتة الدماغية "الرسومات" lineWidth .

أضف هذه الخصائص إلى هيكل GraphView :



إذا كنا نرغب في استخدام Previews "الرسومات" الخاصة بنا (المعاينات) ، والتي لا تكون ممكنة إلا لنظام MacOS Catalyna ، فيجب علينا بدء GraphView مع مجموعة من rangeTime الفهارس rangeTime وبيانات line "الرسومات" نفسها:



لدينا بالفعل بيانات اختبار chartsData التي حصلنا عليها من ملف chart.json JSON ، وقد استخدمناها في Previews .

في حالتنا ، سيكون هذا هو chartsData[0] الأول "مجموعة chartsData[0] " chartsData[0] والأول "مخطط" في هذه المجموعة chartsData[0].lines[0] ، والتي سوف نقدم GraphView كمعلمة line .

سنستخدم مجموعة كاملة من المؤشرات 0..<(chartsData[0].xTime.count - 1) كمجال زمني زمني.
يمكن ضبط lineWidth و lineWidth خارجيًا ، أو لا يتم تعيينها ، نظرًا لأن لها بالفعل قيم أولية: rangeY هي nil ، و lineWidth هي 1 .

لقد توصلنا عمدا إلى TYPE من rangeY TYPE ، لأنه إذا لم rangeY تعيين rangeY = nil خارجيا و rangeY = nil ، فإننا نحسب الحد الأدنى والحد الأدنى minY maxY "للرسومات" مباشرة من بيانات line.points :



يتم تجميع هذا الرمز ، ولكن لا يزال لدينا View قياسية على الشاشة مع النص Text ("Hello World!") في منتصف الشاشة:



لأنه في body يتعين علينا استبدال النص Text ("Hello World!") line.points ، الذي سيقوم بإنشاء "Graph: points: line.points باستخدام أمر addLines(_:) (يشبه تقريبًا في Core Graphics )




lineWidth دائرة stroke (...) مسارنا بخط يحتوي سمكه Path خط lineWidth ، lineWidth لون خط الحد اللون الافتراضي (أي الأسود):



يمكننا استبدال اللون الأسود لخط الحد باللون المحدد في رسمنا "الخط" الخاص "اللون":



من أجل وضع "الرسم البياني" الخاص بنا في مستطيلات من أي حجم ، نستخدم حاوية GeometryReader . في وثائق Apple GeometryReader عبارة عن View "حاوية" تحدد محتوياتها كدالة بحجمها الخاص ومساحة التنسيق. أساسا ، GeometryReader هو View آخر! لأن كل شيء تقريبًا في SwiftUI هو View ! سيتيح لك GeometryReader ، بخلاف Views الأخرى Views الوصول إلى بعض المعلومات المفيدة الإضافية التي يمكنك استخدامها عند تصميم View المخصصة.

نستخدم حاويات GeometryReader و Path لإنشاء GraphView قابلة للتكيف مع أي حجم. وإذا نظرنا بعناية إلى التعليمات البرمجية الخاصة بنا ، فسنرى في الإغلاق لـ GeometryReader متغيرًا يسمى geometry :



يحتوي هذا المتغير على GeometryProxy TYPE ، والذي بدوره هيكل هيكلي به العديد من "المفاجآت":

 public var size: CGSize { get } public var safeAreaInsets: EdgeInsets { get } public func frame(in coordinateSpace: CoordinateSpace) -> CGRect public subscript<T>(anchor: Anchor<T>) -> T where T : Equatable { get } 

من تعريف GeometryProxy ، نرى أن هناك متغيرين var safeAreaInsets var size و var safeAreaInsets ، frame( in:) وظيفة واحد frame( in:) وجهاز subscript getter . كنا في حاجة فقط إلى متغير size لتحديد عرض geometry.size.width وارتفاع geometry.size.height لمنطقة الرسم "Graphics".

بالإضافة إلى ذلك ، يمكننا تمكين "الرسم البياني" لدينا لتحريك باستخدام معدل animation (.linear(duration: 0.6)) .



يسمح GraphView_Previews لنا باختبار أي "مخططات" من أي "مجموعة" بكل بساطة. يوجد أدناه "المخطط" من "مجموعة chartsData[4] " مع الفهرس 4: chartsData[4] والفهرس 0 "الرسومات" في هذه المجموعة: chartsData[4].lines[0] :



قمنا بتعيين height "الرسومات" على 400 باستخدام frame (height: 400) ، ويظل العرض هو نفسه عرض الشاشة. إذا لم نستخدم frame (height: 400) ، فسيشغل "الرسم البياني" الشاشة بأكملها. rangeY GraphView nil , , «» rangeTime :



Path animation (.linear(duration: 0.6)) , , , rangeY «». «» «» rangeY .

: SwiftUI , «» rangeY , SwiftUI , «» rangeY , SwiftUI Animatable .

, View - «», View , Shape , Animatable . , animatableData , , EmptyAnimatableData , .

, , «» GraphView Shape . , func path (in rect:CGRect) -> Path , , , animatableData , :



, SwiftUI «Advanced SwiftUI Animations – Part 1: Paths» .

«» Graph GraphViewNew «» :



, GeometryReader «» GraphViewNew , Shape «» Graph View .

Previews , GraphView :



GraphViewNew «».

GraphsForChart — «» («»)


View - «» («») « » chart rangeTime Y , «» lineWidth :



GraphView GraphViewNew , GraphsForChart GraphsForChart.swift « »:

  • « » chart: LineSet ( Y ),
  • rangeTime: Range ( X ) «»,
  • «» lineWidth

rangeY: Range « » ( Y ) c ( isHidden = false ) «», «»:



rangeOfRanges :



«» ( isHidden = false ) ZStack ForEach , «» « „“ transition(.move(edge: .top)) :



„“ ChartView , Y .

drawingGroup() Metal . Metal Metal , iPhone , . , drawingGroup() , »Advanced SwiftUI Animations – Part 1: Paths" 237 WWDC 2019 ( Building Custom Views with SwiftUI ).

GraphViewNew GraphsForChart Previews « », , 0 :



IndicatorView — «».


«» X :



« » chart X «» «». «», «» .



DragGesture :



“” . value.translation.width , onChanged , , : value.translation.width - self.prevTranslation . .

IndicatorView Previews « » chart View «» GraphsForChart :



, , rangeTime IndicatorView , «» GraphsForChart . , «», «», .

TickerView - X .


«» , X Y . X TickerMarkView . TickerMarkView View VStack , Path Text :



« » chart : LineSet TickerView rangeTime estimatedMarksNumber , :



«» ScrollView HStack , rangeTime .

TickerView step , TimeMarkView , rangeTime widthRange



… c step chart.xTime indexes .

X — — overlay



HStack , TimeMarkView , offset :



, X - colorXAxis , — colorXMark :



YTickerViewY .


View Y YMarkView . YMarkView View VStack , Path ( ) Text :



Y « » chart YTickerView . rangeY «», « » rangeOfRanges . Y estimatedMarksNumber :



YTickerView «» rangeY . Y - — overlay



, Y — colorYAxis , — colorYMark :



RangeView - «mini-map».


( lowerBound , upperBound ) « »:



RangeViewmini - map " " Views .

View , RangeView :



  • « » chart: LineSet ( Y ),
  • height "mini-map" RangeView ,
  • widthRange "mini-map" RangeView ,
  • indent "mini-map" RangeView .

Views , DragGesture ( lowerBound , upperBound ) , ( lowerBound , upperBound ), , @EnvironmentObject var userData: UserData :



var userData Views , .

RangeView «», DragGesture :

1. «», «» X , :



2. , «» lowerBound , «»:



3. , «» upperBound , «»:



RangeView 3- : Rectangle () Image , lowerBound upperBound @EnvironmentObject var userData: UserData DragGesture :



«» ( overlay ) GraphsForChartView «» « » chart :



, «» «».

«» ( ), lowerBound upperBound userData onChanged DragGesture Rectangle () Image ...



, , Views ( «», X , Y c hartView ):



View @EnvironmentObject userData: UserData , Previews , .environmentObject (UserData()) :



CheckMarksView — «» «».


CheckMarksView HStack checkBoxes isHidden «» « » chart :



CheckBox Button CheckButton , SimulatedButton .



Button , List , , «» . , Xcode 11, 1 . SimulatedButton .

SimulatedButton , CheckButton View « » — CheckBoxView . HStack , Tex Image :



, CheckBoxView @Binding var line: Line . isHidden « » CheckBoView :




CheckBoView SimulatedButton CheckButton $ line :




isHidden line SimulatedButton onTapGesture



CheckButtonaction Button :



, SimulatedButton CheckButton @Binding var line: Line . $ CheckMarksView userData.charts[self.chartIndex].lines[self.lineIndex(line: line)].isHidden , @EnvironmentObject var userData :



CheckButton , Apple . , CheckButton CheckMarksView SimulatedButton , « » ChartView List ListChartsView .

View @EnvironmentObject var userData: UserData , Previews , .environmentObject(UserData()) :



Views .


SwiftUIViews , Views — .., Lego . SwiftUI Views :

  • VStack ,
  • HStack ,
  • «» ZStack ,
  • Group ,
  • ScrollView ,
  • List ,
  • Form ,
  • «» TabView
  • ..

GraphsViewForChart , «» « » GraphsForChart Y , X, «» ZStack :



Previews GraphsViewForChart NavigationView , Dark .collorScheme(.dark) .

« » Y , X « », : «mini — map» RangeView CheckMarksView «».

ChartView , « » :



VStack :



3 « » ChartView:

  1. « » List ,
  2. HStack 3D ,
  3. ZStack «»

« » ListChartsView List :



3D ScrollView , HStack ForEach :



: «» mini- map , «».

ZStack «».


CardView «»- « » X Y, : «mini — map» / . CardView ChartView , «» , , , ZStack « » cardBackgroundColor . , «» :



«» VStack , ZStack ForEach :



«», «3D-c» CardViewScalable , indexChat .

«3D-c » ( sequenced ) LongPressGesture DragGesture , «» indexChat == 0 :



( LongPress ) «» « », ( Drag ) , , , «» ZStack , «» «»:



«» TapGesture , LongPressGesture DragGesture :



Tap « » ChartView RangeView CheckMarksView :



TabView 3- « » ChartView .





3 c Image Text , VStack .

3- « » ChartViews :

  1. « » ListChartViews ,
  2. 3D HStackChartViews ,
  3. ZStack «» OverlayCardsViews .

: «» mini - map , «». 3- .

Github .

SwiftUI ...


, :

Mang To , Lets Build That Application , SwiftUI ,
— «SwiftUI by example» www.hackingwithswift.com/quick-start/swiftui
— , www.bigmountainstudio.com/swiftui-views-book
— 100 SwiftUI www.hackingwithswift.com/articles/201/start-the-100-days-of-swiftui , 31 2019 ,
— SwiftUI swiftui-lab.com
Majid ,
— pointFree.co www.pointfree.co «» Reducers SwiftUI ( ),
MovieSwiftUI , .

Source: https://habr.com/ru/post/ar468897/


All Articles