
سأبدأ بالملاحظة بأن التطبيق الذي تمت مناقشته في هذه المقالة يتطلب
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
باستخدام القائمة
File
→
New
→
File
:

ثم نختار نوع الملف المطلوب - هذا هو ملف
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
:

YTickerView
— Y
.
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
) « »:

RangeView
—
mini - 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
…

…
CheckButton
—
action
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
.
SwiftUI
—
Views
,
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:
- « »
List
, HStack
3D ,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
:
- « »
ListChartViews
, - 3D
HStackChartViews
, - 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 , .