
Ich beginne mit der Beobachtung, dass für die in diesem Artikel beschriebene Anwendung 
Xcode 11 und 
MacOS Catalina erforderlich sind, wenn Sie 
Live Previews möchten, und 
Mojave wenn Sie den Simulator verwenden. Der Anwendungscode befindet sich auf 
Github .
In diesem Jahr kündigte 
Apple auf der 
WWDC 2019 SwiftUI , eine neue deklarative Methode zum Erstellen einer Benutzeroberfläche auf allen 
Apple Geräten. Dies ist fast eine völlige Abkehr vom üblichen 
UIKit , und ich wollte - wie viele andere Entwickler - dieses neue Tool unbedingt in Aktion sehen.
In diesem Artikel wird die Erfahrung mit der Lösung eines Problems mit 
SwiftUI , dessen Code in 
UIKit unvergleichlich komplexer ist und meiner Meinung nach nicht lesbar 
UIKit kann.
Die Aufgabe bezieht sich auf den letzten 
Telegrammwettbewerb für 
Android , 
iOS und 
JS Entwickler, der vom 10. bis 24. März 2019 stattfand. In diesem Wettbewerb wurde eine einfache Aufgabe vorgeschlagen, um die Nutzungsintensität einer bestimmten Ressource im Internet in Abhängigkeit von der Zeit basierend auf 
JSON Daten grafisch darzustellen. Als 
iOS Entwickler sollten Sie 
Swift , um von Grund auf neu geschriebenen Code an die Konkurrenz zu senden, ohne fremde Spezialbibliotheken zum Plotten zu verwenden.
Diese Aufgabe erforderte Kenntnisse für die Arbeit mit den Grafik- und Animationsfunktionen von iOS: 
Core Graphics , 
Core Animation , 
Metal , 
OpenGL ES . Einige dieser Tools sind einfache, nicht objektorientierte Programmiertools. Im Wesentlichen gab es in 
iOS keine akzeptablen Vorlagen zum Lösen derart scheinbar leichter grafischer Aufgaben auf den ersten Blick. Daher hat jeder Teilnehmer seinen eigenen Animator ( 
Render ) basierend auf 
Metal , 
CALayers , 
OpenGL , 
CADisplayLink erfunden . Dies erzeugte Tonnen von Code, aus denen es nicht möglich war, etwas auszuleihen und zu entwickeln, da es sich um rein „urheberrechtlich geschützte“ Werke handelt, die nur Autoren wirklich entwickeln können. Dies sollte jedoch nicht so sein.
Anfang Juni erscheint auf der 
WWDC 2019 SwifUI - ein neues 
framework das von 
Apple entwickelt wurde, in 
Swift geschrieben wurde und die Benutzeroberfläche ( 
UI ) im Code deklarativ beschreibt. Sie bestimmen, welche 
subviews in Ihrer 
View subviews , welche Daten dazu führen, dass sich diese 
subviews ändern, welche Modifikatoren Sie auf sie anwenden müssen, damit sie an der richtigen Stelle positioniert werden und die richtige Größe und den richtigen Stil haben. Ein ebenso wichtiges Element von 
SwiftUI ist die Steuerung des Flusses von vom Benutzer veränderbaren Daten, wodurch wiederum die 
UI aktualisiert wird.
In diesem Artikel möchte ich zeigen, wie die eigentliche Aufgabe des 
Telegrammwettbewerbs auf 
SwiftUI schnell und einfach gelöst werden kann. Darüber hinaus ist dies ein sehr aufregender Prozess.
Aufgabe
Die Konkurrenzanwendung sollte gleichzeitig 5 "Sätze von Diagrammen" auf dem Bildschirm anzeigen, wobei die von 
Telegram bereitgestellten Daten verwendet werden. Für einen „Satz von Diagrammen“ lautet die 
UI wie folgt:

Im oberen Teil befindet sich eine „Diagrammzone“ mit einer gemeinsamen Skala entlang der normalen Y-Achse mit Markierungen und horizontalen Gitterlinien. Darunter befindet sich eine Kriechlinie mit Zeitstempeln entlang der X-Achse als Datumsangaben.
Noch niedriger ist die sogenannte „Minikarte“ (wie in 
Xcode 11 ), 
Xcode 11 ein transparentes „Fenster“, das den Teil des Zeitraums unserer „Diagramme“ definiert, der in der oberen „Diagrammzone“ ausführlicher dargestellt wird. Diese „Minikarte“ kann nicht nur entlang der 
X Achse verschoben werden, sondern auch ihre Breite kann geändert werden, was sich auf die Zeitskala im Bereich „Diagramme“ auswirkt.
Mit Hilfe von 
checkboxs die in den Farben von „Diagrammen“ gemalt und mit ihren Namen versehen sind, können Sie die Anzeige der dieser Farbe entsprechenden „Grafiken“ in der „Diagrammzone“ ablehnen.
Es gibt viele solcher „Sätze von Graphen“. In unserem Testbeispiel gibt es beispielsweise 5 davon, und alle sollten sich auf einem Bildschirm befinden.
In der mit 
SwiftUI entwickelten 
SwiftUI ist keine Schaltfläche zum Umschalten zwischen 
SwiftUI und 
SwiftUI . Diese ist bereits in 
SwiftUI . Darüber hinaus bietet 
SwiftUI weitaus mehr Optionen zum Kombinieren von „ 
SwiftUI “ ( 
SwiftUI den oben dargestellten Bildschirmsätzen) als nur eine Bildlauftabelle nach unten, und wir werden uns einige dieser sehr interessanten Optionen ansehen.
Aber zuerst konzentrieren wir uns auf die Anzeige eines „ 
SwiftUI “, für den 
SwiftUI ChartView eine 
ChartView :

SwiftUI können Sie eine komplexe 
UI in kleinen Teilen erstellen und testen. Anschließend können Sie diese Teile ganz einfach zu einem Puzzle zusammensetzen. Wir werden es tun. Unser 
ChartView sehr gut in diese kleinen Teile 
ChartView :
- GraphsForChart- Dies sind die Diagramme selbst, die für einen bestimmten "Satz von Diagrammen" erstellt wurden. "Diagramme" werden für den vom Benutzer mithilfe der- RangeView"Minikarte"- RangeView, der unten dargestellt wird.
- YTickerViewist die- YAchse mit Höhen und dem entsprechenden horizontalen Raster.
- IndicatorViewist ein horizontal benutzergesteuerter Indikator, mit dem Sie die Werte von "Diagramme" und die Zeit für die entsprechende Indikatorposition auf der Zeitachse auf der- XAchse anzeigen können.
- TickerView- "- TickerView", die Zeitstempel auf der- XAchse als Datumsangaben- TickerView,
- RangeView- ein temporäres „Fenster“, das vom Benutzer mithilfe von Gesten angepasst werden kann, um das Zeitintervall für „- RangeView“- RangeView
- CheckMarksView- enthält "Schaltflächen" in den Farben von "- CheckMarksView", mit denen Sie das Vorhandensein von "- ChartView" in- ChartView.
ChartView Benutzer kann auf drei Arten mit 
ChartView interagieren:
1. Steuern Sie die „Minikarte“ mit der 
DragGesture Geste. Sie kann das temporäre „Fenster“ nach rechts und links verschieben und seine Größe verringern / vergrößern:

2. Bewegen Sie den Indikator in horizontaler Richtung und zeigen Sie die Werte der "Diagramme" zu einem festgelegten Zeitpunkt an:

3. Verbergen / Anzeigen bestimmter „Diagramme“ mithilfe von Schaltflächen in den Farben „ 
ChartView “ am unteren Rand der 
ChartView :

Wir können verschiedene „Diagrammsätze“ (wir haben 5 davon in Testdaten) auf unterschiedliche Weise kombinieren, indem wir sie beispielsweise alle gleichzeitig auf einem Bildschirm mithilfe der 
List (wie eine nach oben und unten scrollbare Tabelle):

oder mit 
ScrollView und einem horizontalen Stapel von 
HStack mit einem 3D-Effekt:

... oder in Form eines 
ZStack „Karten“, dessen Reihenfolge geändert werden kann: Die obere „Karte“ mit „einer Reihe von Diagrammen“ kann weit genug nach unten gezogen werden, um die nächste Karte anzuzeigen, und wenn Sie sie weiter nach unten ziehen, wird sie „ geht "bis zum letzten Platz in 
ZStack , und diese nächste" Karte "" geht weiter ":

In diesen komplexen 
UI - einer „Bildlauftabelle“, einem horizontalen Stapel mit 
3D Effekt, einem 
ZStack „Karten“ - funktionieren alle Mittel der Benutzerinteraktion vollständig: Bewegen Sie sich entlang der Zeitachse und ändern Sie den „Maßstab“ der Schaltflächen für 
mini - map , Anzeige und Ausblenden "Charts".
Weiter werden wir das Design dieser 
UI Verwendung von 
SwiftUI im Detail 
SwiftUI - von einfachen Elementen bis zu ihren komplexeren Kompositionen. Aber lassen Sie uns zuerst die Datenstruktur verstehen, die wir haben.
Die Lösung unseres Problems war also in mehrere Phasen unterteilt:
- Laden Sie Daten aus einer JSONDatei herunter und präsentieren Sie sie in einem praktischen "internen" Format
- Erstellen Sie eine UIfür einen „Satz von Diagrammen“.
- Kombinieren Sie verschiedene "Kartensätze"
Daten herunterladen
Zu unserer Verfügung stellte 
Telegram JSON- Daten zur Verfügung, die mehrere "Sätze von Diagrammen" enthielten. Jeder einzelne " 
chart " eines 
chart enthält mehrere " 
chart " (oder "Linien") von 
chart.columns . Jeder "Graph" ("Linien") hat eine Markierung an Position 
0 - 
"x" , 
"y0" , 
"y1" , 
"y2" , 
"y3" , gefolgt von beiden Zeitwerten auf der X-Achse ("x"). oder die Werte von "Grafiken" ("Linien") ( 
"y0" , 
"y1" , 
"y2" , 
"y3" ) auf der 
Y Achse:

Das Vorhandensein aller „Linien“ im „Diagrammsatz“ ist optional. Die Werte für die "Spalte" x sind UNIX-Zeitstempel in Millisekunden.
Darüber hinaus wird jeder einzelne „ 
chart “ des 
chart mit 
chart.colors Farben im Format von 6 hexadezimalen Ziffern (z. B. „#AAAAAA“) und 
chart.names .
Um das Datenmodell in der 
JSON Datei zu erstellen, habe ich den hervorragenden 
Quicktype- Dienst verwendet. Auf dieser Site fügen Sie einen Text aus einer 
JSON Datei ein und geben die Programmiersprache ( 
Swift ) an, den Namen der Struktur ( 
Chart ), die nach dem "Parsen" dieser 
JSON Daten erstellt wird.
Im zentralen Teil des Bildschirms wird ein Code generiert, den wir in einer separaten Datei mit dem Namen 
Chart.swift in unsere Anwendung 
Chart.swift . Hier platzieren wir das Datenmodell im JSON-Format. Mit dem Loader von Daten aus der 
JSON Datei in das Modell, das aus den 
SwiftUI Generic Demos ausgeliehen wurde , erhielt ich eine Reihe von 
columns: [ChartElement] , eine Sammlung von „ 
columns: [ChartElement] “ im 
Telegram .
Die 
ChartElement Datenstruktur, die Arrays heterogener Elemente enthält, eignet sich nicht besonders für die intensive interaktive Arbeit mit Diagrammen. Außerdem werden Zeitstempel im 
UNIX Format in Millisekunden (z. B. 
1542412800000, 1542499200000, 1542585600000, 1542672000000 ) und Farben im 6-Hexadezimalformat 
1542412800000, 1542499200000, 1542585600000, 1542672000000 Ziffern (zum Beispiel 
"#AAAAAA" ).
Daher werden wir in unserer Anwendung dieselben Daten verwenden, jedoch in einem anderen „internen“ und ziemlich einfachen Format 
[LinesSet] . Das Array 
[LinesSet] ist eine Sammlung von 
LinesSet , von denen jeder 
xTime Zeitstempel im Format 
"Feb 12, 2019" ( 
X Achse) und mehrere „Diagramm“ 
lines ( 
Y Achse) enthält:

Daten für jedes Liniendiagramm (Linie) werden angezeigt
- ein Array von ganzzahligen points: [Int],
- benannt "Grafik" title: String,
- Typ "Grafik" type: String?,
- color : UIColorim- Swift- UIColorFormat,
- Anzahl der Punkte countY: Int.
Darüber hinaus kann jedes „Diagramm“ je nach Wert von 
isHidden: Bool ausgeblendet oder angezeigt werden. Die 
upperBound lowerBound und 
upperBound Anpassen des Zeitbereichs nehmen Werte von 
0 bis 
1 und zeigen nicht nur die Größe des Zeitfensters „Mini Map“ ( 
upperBound - 
lowerBound ), sondern auch dessen Position auf der Zeitachse 
X :

Die 
JSON Datenstrukturen 
[ChartElement] und die Datenstrukturen der 
LinesSet „intern“ 
LinesSet und 
Line befinden sich in der Datei 
Chart.swift . Der Code zum Laden und Konvertieren von 
JSON Daten in eine interne Struktur befindet sich in der Datei 
Data.swift . Details zu diesen Transformationen finden Sie 
hier .
Als Ergebnis haben wir Daten über die „Diagrammsätze“ im internen Format als Array von 
chartsData .

Dies ist unser Datenmodell. 
SwiftUI jedoch in 
SwiftUI zu arbeiten, muss sichergestellt werden, dass alle vom Benutzer am 
chartsData Array vorgenommenen Änderungen (Ändern des temporären „Fensters“, Ausblenden / 
chartsData der „ 
chartsData “) zu automatischen Aktualisierungen unserer 
Views .
Wir werden 
@EnvironmentObject erstellen. Auf diese Weise können wir das Datenmodell überall dort verwenden, wo es benötigt wird, und außerdem unsere 
Views automatisch aktualisieren, wenn sich die Daten ändern. Dies ist so etwas wie 
Singleton oder globale Daten.
@EnvironmentObject erfordert, dass wir eine 
final class UserData der 
final class UserData , die sich in der Datei 
UserData.swift befindet , die 
chartsData Daten speichert und das 
ObservableObject Protokoll implementiert:

Das Vorhandensein von 
@Published "Wrappern" ermöglicht es Ihnen, "Nachrichten" zu veröffentlichen, dass sich diese Eigenschaften der 
charts der 
UserData Klasse geändert haben, sodass alle 
Views , die diese Nachrichten "abonniert" haben, in 
SwiftUI automatisch neue Daten auswählen und aktualisieren können.
Denken Sie daran, dass sich in der Eigenschaft " 
isHidden " die 
isHidden Werte für jedes " 
isHidden " ändern können (Sie können diese " 
isHidden " ausblenden oder 
lowerBound ) sowie die unteren 
lowerBound für die untere und obere Grenze für jeden einzelnen "Satz von 
upperBound ".
Wir möchten die 
charts der 
UserData Klasse in unserer gesamten Anwendung verwenden und müssen sie dank 
@EnvironmentObject nicht manuell mit der 
UI @EnvironmentObject .
Dazu müssen wir beim Starten der Anwendung eine Instanz der 
UserData () -Klasse erstellen, damit wir anschließend überall in unserer Anwendung darauf zugreifen können. Wir werden dies in der 
SceneDelegate.swift Datei innerhalb der 
scene (_ : , willConnectTo: , options: ) tun 
scene (_ : , willConnectTo: , options: ) -Methode. Hier wird unsere 
ContentView erstellt und gestartet. Hier müssen wir die 
ContentView jedes 
@EnvironmentObject uns 
@EnvironmentObject damit 
SwiftUI sie für jede andere 
View verfügbar machen kann:

@Published nun in einer beliebigen 
View auf die 
@Published Daten der 
UserData Klasse 
UserData , müssen Sie die Variable 
var mit dem Wrapper 
@EnvironmentObject . Wenn Sie beispielsweise den Zeitbereich in 
RangeView erstellen wir die Variable 
var userData mit dem 
UserData TYPE:

Sobald wir also 
@EnvironmentObject in die "Umgebung" der Anwendung implementiert haben, können wir sofort damit beginnen, es entweder auf der höchsten Ebene oder auf der 10. Ebene darunter zu verwenden - es spielt keine Rolle. Noch wichtiger ist jedoch, dass alle 
Views mit diesem 
@EnvironmentObject automatisch 
@EnvironmentObject , wenn eine 
View die "Umgebung" ändert, wodurch die Synchronisation mit den Daten sichergestellt wird.
Fahren wir mit dem Entwerfen der Benutzeroberfläche fort.
Benutzeroberfläche (UI) für einen „Satz von Grafiken“
SwiftUI bietet eine zusammengesetzte Technologie zum Erstellen von 
SwiftUI aus vielen kleinen 
Views , und wir haben bereits gesehen, dass unsere Anwendung sehr gut zu dieser Technologie passt, da sie sich in kleine Teile 
ChartView : das 
ChartView „ 
ChartView Charts“, „ 
GraphsForChart Charts“, 
Y Achsen- 
YTickerView - 
YTickerView , Benutzergesteuerter Indikatorwert von "Charts" 
IndicatorView , " 
TickerView " 
TickerView mit 
TickerView auf der 
X Achse, benutzergesteuertes "Zeitfenster" 
RangeView , Markierungen beim Ausblenden / 
CheckMarksView "Charts" 
CheckMarksView . Wir können nicht nur alle diese 
Views unabhängig voneinander erstellen, sondern auch sofort in 
Xcode 11 mithilfe von 
Previews (vorläufige Live-Ansichten) auf Testdaten testen. Sie werden überrascht sein, wie einfach der Code ist, um sie aus anderen grundlegenderen 
Views zu erstellen.
GraphView - "Graph" ("Linie")
Die erste 
View , mit der wir beginnen werden, ist eigentlich der „Graph“ selbst (oder die „Linie“). Wir werden es 
GraphView :

Das Erstellen einer 
GraphView beginnt wie gewohnt mit dem Erstellen einer neuen Datei in 
Xcode 11 über das Menü 
File → 
New → 
File :

Dann wählen wir den gewünschten TYP der Datei aus - dies ist die 
SwiftUI Datei:

... geben Sie unserer 
View den Namen "GraphView" und geben Sie ihren Standort an:

Klicken Sie auf die Schaltfläche 
"Create" und erhalten Sie eine Standardansicht mit 
Text ( "Hello World!") In der Mitte des Bildschirms:

Unsere Aufgabe ist es, den 
Text ("Hello World!") "Grafik" zu ersetzen. Lassen Sie uns jedoch zunächst sehen, welche Anfangsdaten wir zum Erstellen der "Grafik" haben:
- Wir haben die Werte von line.points"Graphics"line: Line,
- Zeitbereich rangeTime, ein Bereich von IndizesRangeZeitstempelnxTimeauf der X-Achse,
- Wertebereich rangeY: Range„Grafiken“ für die Y-rangeY: Range,
- Dicke der lineWidth„Grafik“.
Fügen Sie der 
GraphView Struktur diese Eigenschaften 
GraphView :

Wenn wir für unsere "Grafiken" 
Previews (Vorschauen) verwenden möchten, die nur für 
MacOS Catalyna , müssen wir eine 
GraphView mit dem 
rangeTime und den 
rangeTime der "Grafiken" selbst initiieren:

Wir haben bereits die 
chartsData Testdaten, die wir aus der 
JSON Datei 
chart.json , und wir haben sie für 
Previews .
In unserem Fall ist dies der erste " 
chartsData[0] " 
chartsData[0] und der erste "Chart" in diesem 
chartsData[0].lines[0] , den wir 
GraphView als 
line bereitstellen.
Wir werden den gesamten Bereich der Indizes 
0..<(chartsData[0].xTime.count - 1) als Zeitintervall 
rangeTime .
Die 
lineWidth rangeY und 
lineWidth können extern oder nicht festgelegt werden, da sie bereits Anfangswerte haben: 
rangeY ist 
nil und 
lineWidth ist 
1 .
Wir haben absichtlich einen TYP der 
rangeY TYPE erstellt. Wenn 
rangeY nicht extern festgelegt ist und 
rangeY = nil , berechnen wir die minimalen 
minY und maximalen 
maxY Werte der "Grafiken" direkt aus den Daten von 
line.points :

Dieser Code wird kompiliert, aber wir haben immer noch eine Standardansicht auf dem Bildschirm mit dem Text 
Text ("Hello World!") In der Mitte des Bildschirms:

Weil wir im 
body den Text 
Text ("Hello World!") Durch 
Path ersetzen müssen, wodurch unser "Graph: points: 
line.points mit dem 
addLines(_:) (fast wie in 
Core Graphics ).


Wir werden unseren 
Path Linie umkreisen 
stroke (...) , deren Dicke 
lineWidth , und die Farbe der Strichlinie entspricht der Standardfarbe ( 
lineWidth schwarz):

Wir können die schwarze Farbe für die Strichlinie durch die Farbe ersetzen, die in unserer speziellen "Linien" 
line.color "Farbe" angegeben ist:

Damit unser „Diagramm“ in Rechtecken beliebiger Größe platziert werden kann, verwenden wir den 
GeometryReader Container. In der 
Apple Dokumentation 
Apple GeometryReader eine "Container" 
View , die ihren Inhalt als Funktion ihrer eigenen 
size und ihres Koordinatenraums definiert. Im Wesentlichen ist 
GeometryReader eine andere 
View ! Denn fast ALLES in 
SwiftUI ist 
View ! 
GeometryReader können SIE im Gegensatz zu anderen 
Views auf einige zusätzliche nützliche Informationen zugreifen, die Sie beim Entwerfen Ihrer benutzerdefinierten 
View .
Wir verwenden die Container 
GeometryReader und 
Path , um 
GraphView zu erstellen, 
GraphView an jede Größe 
GraphView werden kann. Und wenn wir uns unseren Code genau ansehen, sehen wir im Abschluss des 
GeometryReader Variable namens 
geometry :

Diese Variable hat den 
GeometryProxy TYPE, der wiederum eine Strukturstruktur mit vielen "Überraschungen" ist:
 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 } 
Aus der 
GeometryProxy Definition geht hervor, dass es zwei berechnete Variablen 
var size und 
var safeAreaInsets , einen Funktionsrahmen 
frame( in:) und einen 
subscript getter . Wir brauchten nur die Größenvariable, um die Breite der 
geometry.size.width und die Höhe der 
geometry.size.height Zeichenbereichs „Grafik“ zu bestimmen.
Zusätzlich ermöglichen wir die 
animation (.linear(duration: 0.6)) unseres „Graphen“ mit dem 
animation (.linear(duration: 0.6)) .

GraphView_Previews können wir ganz einfach alle „Charts“ aus jedem „Set“ testen. Unten ist das "Diagramm" aus dem "Diagrammsatz" mit Index 4: 
chartsData[4] und Index 0 "Grafik" in diesem Satz: 
chartsData[4].lines[0] :

Wir stellen die 
height „Grafik“ mithilfe des 
frame (height: 400) auf 400 ein 
frame (height: 400) . Die Breite bleibt gleich der Breite des Bildschirms. Wenn wir keinen 
frame (height: 400) , würde der "Graph" den gesamten Bildschirm einnehmen. 
rangeY GraphView nil , , «» 
rangeTime :

Path animation (.linear(duration: 0.6)) , , , 
rangeY «». «» «» 
rangeY .
: 
SwiftUI , «» 
rangeY , 
SwiftUI , «» 
rangeY , 
SwiftUIerfüllt Protokoll Animatable.Wenn Ihre ViewFigur eine "Figur" ist View, dh ein Protokoll implementiert Shape, wurde glücklicherweise bereits ein Protokoll dafür implementiert Animatable. Dies bedeutet, dass es eine berechnete Eigenschaft gibt, animatableDatamit der wir den Animationsprozess steuern können, aber standardmäßig ist sie so eingestellt EmptyAnimatableData, dass keine Animation stattfindet.Um das Problem mit der Animation zu lösen, müssen wir zuerst unseren „Graphen“ GraphViewin verwandeln Shape. Es ist sehr einfach, wir müssen nur die Funktion implementieren, die func path (in rect:CGRect) -> Pathwir im Wesentlichen bereits haben, und mit Hilfe der berechneten Eigenschaft angeben, animatableDatawelche Daten wir animieren möchten: Beachten Sie, dass das Thema der Animationssteuerung ein fortgeschrittenes Thema ist
Beachten Sie, dass das Thema der Animationssteuerung ein fortgeschrittenes Thema istSwiftUIWeitere Informationen finden Sie im Artikel „Erweiterte SwiftUI-Animationen - Teil 1: Pfade“ . Wir können dieresultierende „Figur“ Graphin einer viel einfacheren GraphViewNew„Grafik“ mit Animation verwenden: Sie sehen, dass wir
Sie sehen, dass wir GeometryReaderunsere neue „Grafik“ nicht benötigt haben GraphViewNew, da sich Shapeunsere „Figur“ dank des Protokolls Graphan jede Größe des Elternteils anpassen kann View.Natürlich haben Previewswir das gleiche Ergebnis erhalten wie im Fall von GraphView: In den folgenden Kombinationen verwenden wir die
In den folgenden Kombinationen verwenden wir die GraphViewNewgleichen „Grafiken“, um die Werte anzuzeigen.GraphsForChart - Satz von "Graphen" ("Linien")
Die Aufgabe Viewbesteht darin, ALLE „Diagramme“ („Linien“) aus dem „Satz von Diagrammen“ chartin einem bestimmten Zeitbereich rangeTimemit einer gemeinsamen Achse anzuzeigen. YDie Breite der „Linien“ entspricht lineWidth: Für
Für GraphViewund GraphViewNewerstellen wir eine GraphsForChartneue Datei für GraphsForChart.swiftund definieren die Anfangsdaten für "Chart Set":- der "Satz von Diagrammen" selbst chart: LineSet(Werte anY),
- Bereich rangeTime: Range(X) der Indizes der Zeitstempel von „Charts“,
- Grafik Strichstrichstärke 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. Dies ermöglicht uns eine reibungslose Bewegung des Indikators.Um den Indikator IndicatorViewmit Hilfe eines Previewsbestimmten „Satzes von Diagrammen“ zu testen , können chartwir die vorgefertigte ViewKonstruktion von „Diagrammen“ verwenden GraphsForChart: Wir können einen beliebigen, aber aufeinander abgestimmten Zeitbereich
Wir können einen beliebigen, aber aufeinander abgestimmten Zeitbereich rangeTimesowohl für den Indikator IndicatorViewals auch für „Diagramme“ festlegen GraphsForChart. Auf diese Weise können wir sicherstellen, dass sich die "Kreise", die die Werte der "Diagramme" angeben, an den richtigen Stellen befinden.TickerView- Xmit Markierungen.
«» , 
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 .
Dieser Viewzeichnet Ymit digitalen Markierungen YMarkView. Die Markierungen selbst YMarkViewsind sehr einfach Viewmit einem vertikalen Stapel, VStackin dem sie platziert sind Path(horizontale Linie), und Textmit einer Nummer: Eine Reihe von Markierungen
Eine Reihe von Markierungen Yfür eine bestimmte „Gruppe von Diagrammen“ chartwird in gebildet YTickerView. Der Wertebereich wird rangeYals Vereinigung der Wertebereiche aller "Diagramme" berechnet, die in diesem "Satz von Diagrammen" enthalten sind, wobei die Funktion verwendet wird rangeOfRanges. Die ungefähre Anzahl der Markierungen auf der Y-Achse wird durch den Parameter festgelegt estimatedMarksNumber:
YTickerViewWir überwachen die Änderung des Bereichs der „Diagramme“ -Werte rangeY. Eigentlich die Y-Achse - die vertikale Linie - die wir overlayunseren Markierungen auferlegen ...
, 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.
Im Gegensatz zu den anderen oben diskutiertenViewsmüssen wir den DragGestureZeitbereich ( lowerBound, upperBound) mit einer Geste ändern und sofort seine Änderung sehen, damit der benutzerdefinierte Zeitbereich ( lowerBound, upperBound), mit dem wir arbeiten, in einer Variablenvariablen gespeichert wird @EnvironmentObject var userData: UserData: Jede Änderung der Variablen
Jede Änderung der Variablen var userDataführt zu alle Viewsvon ihm abhängig neu zeichnen .Die Hauptfigur in RangeViewist ein transparentes „Fenster“, dessen Position und Größe vom Benutzer mit einer Geste geregelt wird DragGesture:1. Wenn wir die Geste in einem transparenten „Fenster“ verwenden, Xändert sich die Position des „Fensters“ entlang und seine Größe ändert sich nicht:
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 — «» «».
CheckMarksViewEs handelt sich um einen horizontalen Stapel HStackmit einer Zeile checkBoxeszum Umschalten der Eigenschaften der isHiddeneinzelnen „Grafiken“ im „Satz von Grafiken“ chart:
CheckBoxIn unserem Projekt kann er entweder über eine reguläre Schaltfläche Buttonund einen Aufruf CheckButtonoder über eine simulierende Schaltfläche implementiert werden SimulatedButton. Die Schaltfläche
Die Schaltfläche Buttonmusste nachgeahmt werden, da sie sich beim Platzieren mehrerer dieser Schaltflächen in der Listhöheren Hierarchie „weigert“, ordnungsgemäß zu funktionieren. Dies ist ein langjähriger Fehler , der seit Beta 1 in der aktuellen Version in Xcode 11 steckt . Die aktuelle Version der Anwendung verwendet eine simulierte Schaltfläche SimulatedButton.Sowohl die simulierte SimulatedButtonals auch die reale TasteCheckButton 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- Dies ist in erster Linie eine Kombination aus verschiedenen kleinen Viewszu großen und großen Viewszu sehr großen usw., wie in einem Spiel Lego. Es SwiftUIgibt viele Mittel für eine solche Kombination Views:- Ein vertikaler Stapel VStack,
- horizontale Stapel HStack,
- „Tiefe“ des Stapels ZStack,
- Gruppe Group,
- ScrollView,
- Liste List,
- bilden Form,
- Lesezeichenbehälter TabView
- usw.
GraphsViewForChart , «» « » 
GraphsForChart Y , X, «» 
ZStack :

Previews GraphsViewForChart NavigationView , 
Dark .collorScheme(.dark) .
« » Y , X « », : «mini — map» 
RangeView CheckMarksView «».
ChartView , « » :

VStack :

3 « » ChartView:
- « » List,
- HStack3D ,
- ZStack«»
« »ListChartsView List :
 3D
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 :

Anwendung TabViewzum Kombinieren aller 3 Varianten des Kompositions- „Diagrammsatzes“ auf einem Bildschirm ChartView.
 Wir haben 3 Lesezeichen mit einem Bild
Wir haben 3 Lesezeichen mit einem Bild Imageund Text Text, ein vertikaler Stapel VStackwird für ihre gemeinsame Präsentation nicht benötigt.Sie entsprechen unseren 3 Methoden zum Kombinieren von "Diagrammsätzen" ChartViews:- "Bildlauftabelle" ListChartViews,
- horizontaler Stapel mit 3D-Effekt HStackChartViews,
- ZStack überlagerte "Karten" OverlayCardsViews.
Alle Elemente der Benutzerinteraktion: Bewegen Sie sich entlang der Zeitachse und ändern Sie die „Skala“ mit Hilfemini - map, Anzeige und Schaltflächen, um die „Diagramme“ auszublenden. in allen 3 Fällen voll arbeiten.Der Code ist auf 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 , .