
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.YTickerView ist die Y Achse mit Höhen und dem entsprechenden horizontalen Raster.IndicatorView ist ein horizontal benutzergesteuerter Indikator, mit dem Sie die Werte von "Diagramme" und die Zeit für die entsprechende Indikatorposition auf der Zeitachse auf der X Achse anzeigen können.TickerView - " TickerView ", die Zeitstempel auf der X Achse als Datumsangaben TickerView ,RangeView - ein temporäres „Fenster“, das vom Benutzer mithilfe von Gesten angepasst werden kann, um das Zeitintervall für „ RangeView “ RangeViewCheckMarksView - 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
JSON Datei herunter und präsentieren Sie sie in einem praktischen "internen" Format - Erstellen Sie eine
UI fü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 : UIColor im Swift UIColor Format,- 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 Indizes Range Zeitstempeln xTime auf 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 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 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 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 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 an Y), - 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 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 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 diskutierten Viewsmü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 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 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 , 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 :

Anwendung TabViewzum Kombinieren aller 3 Varianten des Kompositions- „Diagrammsatzes“ auf einem Bildschirm ChartView.
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 Hilfe mini - 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 , .