
Je commencerai par la remarque que l'application décrite dans cet article nécessite
Xcode 11 et
MacOS Catalina si vous souhaitez utiliser les
Live Previews
et
Mojave
si vous utilisez le simulateur. Le code d'application est sur
Github .
Cette année, lors de la
WWDC 2019 ,
Apple
annoncé
SwiftUI
, une nouvelle façon déclarative de créer une interface utilisateur (UI) sur tous
Apple
appareils
Apple
. C'est presque un départ complet de l'
UIKit
habituel, et moi - comme beaucoup d'autres développeurs - voulais vraiment voir ce nouvel outil en action.
Cet article présente l'expérience de la résolution avec
SwiftUI
un problème dont le code dans
UIKit
incomparablement plus complexe et ne peut pas être
UIKit
à mon avis de manière lisible.
La tâche est liée au dernier concours
Telegram pour
Android
développeurs
Android
,
iOS
et
JS
, qui s'est tenu du 10 mars au 24 mars 2019. Dans ce concours, une tâche simple a été proposée pour afficher graphiquement l'intensité d'utilisation d'une certaine ressource sur Internet en fonction du temps en fonction
JSON
données
JSON
. En tant que développeur
iOS
, vous devez utiliser
Swift
pour soumettre du code écrit à partir de zéro à la concurrence sans utiliser de bibliothèques spécialisées étrangères pour le traçage.
Cette tâche exigeait des compétences pour travailler avec les capacités graphiques et d'animation d'iOS:
Core Graphics ,
Core Animation ,
Metal ,
OpenGL ES . Certains de ces outils sont des outils de programmation de bas niveau non orientés objet. Essentiellement, dans
iOS
il n'y avait pas de modèles acceptables pour résoudre des tâches graphiques aussi légères à première vue. Par conséquent, chaque candidat a inventé son propre animateur (
Render ) basé sur
Metal ,
CALayers ,
OpenGL ,
CADisplayLink . Cela a généré des tonnes de code à partir desquelles il n'a pas été possible d'emprunter et de développer quoi que ce soit, car ce sont des œuvres purement «protégées par le droit d'auteur» que seuls les auteurs peuvent réellement développer. Mais cela ne devrait pas être le cas.
Et début juin à la
WWDC 2019 ,
SwifUI
apparaît - un nouveau
framework
développé par
Apple
, écrit en
Swift
et conçu pour décrire de manière déclarative l'interface utilisateur (
UI
) dans le code. Vous déterminez quelles sous-
subviews
affichées dans votre
View
, quelles données entraînent la
subviews
ces sous-
subviews
, quels modificateurs vous devez leur appliquer, pour les positionner au bon endroit, pour avoir la bonne taille et le bon style. Un élément tout aussi important de
SwiftUI
est le contrôle du flux de données modifiables par l'utilisateur, qui à son tour met à jour l'
UI
.
Dans cet article, je veux montrer comment la tâche même du concours
Telegram sur
SwiftUI
est résolue rapidement et facilement. De plus, c'est un processus très excitant.
Tâche
L'application concurrentielle doit afficher simultanément 5 «ensembles de graphiques» à l'écran en utilisant les données fournies par
Telegram
. Pour un «ensemble de graphiques», l'
UI
la suivante:

Dans la partie supérieure, il y a une «zone de graphique» avec une échelle commune le long de l'axe Y normal avec des marques et des lignes de quadrillage horizontales. En dessous, une ligne rampante avec des horodatages le long de l'axe X comme dates.
Encore plus bas est la soi-disant «mini carte» (comme dans
Xcode 11
), c'est-à-dire une «fenêtre» transparente qui définit la partie de la période de nos «graphiques», qui est présentée plus en détail dans la «zone des graphiques» supérieure. Cette «mini-carte» peut non seulement être déplacée le long de l'axe
X
, mais également sa largeur peut être modifiée, ce qui affecte l'échelle de temps dans la «zone des graphiques».
A l'aide de
checkboxs
peintes aux couleurs des «Graphiques» et munies de leurs noms, vous pouvez refuser d'afficher les «Graphiques» correspondant à cette couleur dans la «Zone des Graphiques».
Il existe de nombreux "ensembles de graphiques", dans notre exemple de test, il y en a 5, par exemple, et ils devraient tous être situés sur un seul écran.
Dans l'
UI
conçue à l'aide de
SwiftUI
il n'est pas nécessaire d'avoir un bouton pour basculer entre les modes
Dark
et
Light
, il est déjà intégré à
SwiftUI
. En outre,
SwiftUI
beaucoup plus d'options pour combiner des «ensembles de graphiques» (c'est-à-dire les ensembles d'écrans présentés ci-dessus) que de simplement faire défiler un tableau, et nous examinerons certaines de ces options très intéressantes.
Mais d'abord, concentrons-nous sur l'affichage d'un «ensemble de
SwiftUI
» pour lequel
SwiftUI
allons créer un
ChartView
:

SwiftUI
vous permet de créer et de tester une
UI
complexe en petites pièces, puis il est très facile d'assembler ces pièces en un puzzle. Nous le ferons. Notre
ChartView
très bien en ces petits morceaux:
GraphsForChart
- ce sont les graphiques eux-mêmes, construits pour un "ensemble de graphiques" spécifique. Les «graphiques» sont affichés pour la plage de temps contrôlée par l'utilisateur à l'aide de la «mini-carte» RangeView
, qui sera présentée ci-dessous.YTickerView
est l'axe Y
avec des élévations et la grille horizontale correspondante.IndicatorView
est un indicateur piloté par l'utilisateur horizontalement qui vous permet de visualiser les valeurs des "graphiques" et le temps pour la position de l'indicateur correspondant sur l'axe du temps sur l'axe X
TickerView
- "ligne rampante" montrant les horodatages sur l'axe X
comme des dates,RangeView
- une «fenêtre» temporaire, personnalisable par l'utilisateur à l'aide de gestes, pour définir l'intervalle de temps pour les «graphiques»,CheckMarksView
- contient des «boutons» colorés aux couleurs de «Graphiques» et vous permettant de contrôler la présence de « ChartView
» sur ChartView
.
ChartView
utilisateur peut interagir avec
ChartView
de trois manières:
1. contrôler la «mini-carte» en utilisant le geste
DragGesture
- il peut déplacer la «fenêtre» temporaire vers la droite et la gauche et diminuer / augmenter sa taille:

2. déplacez l'indicateur dans le sens horizontal, en montrant les valeurs des "graphiques" à un point fixe dans le temps:

3. masquer / afficher certains «graphiques» à l'aide de boutons colorés dans les couleurs des «graphiques» et situés au bas de
ChartView
:

Nous pouvons combiner différents «ensembles de graphiques» (nous en avons 5 dans les données de test) de différentes manières, par exemple, en les plaçant tous simultanément sur un seul écran à l'aide de la liste (comme un tableau déroulant de haut en bas):

ou en utilisant
ScrollView
et une pile horizontale de
HStack
avec un effet 3D:

... ou sous la forme d'un
ZStack
"cartes" superposées les unes aux autres, dont l'ordre peut être modifié: la "carte" supérieure avec "" un ensemble de graphiques "peut être tirée suffisamment loin pour regarder la carte suivante, et si vous continuez à la faire glisser, alors elle" va "à la dernière place dans
ZStack
, et cette prochaine" carte "" va de l'avant ":

Dans ces
UI
complexes - une «table déroulante», une pile horizontale avec un effet
3D
, une
ZStack
«cartes» superposées les unes aux autres - tous les moyens d'interaction avec l'utilisateur fonctionnent à fond: se déplacer le long de la chronologie et changer l '«échelle» de la
mini - map
, des indicateurs et des boutons de masquage "Graphiques".
De plus, nous examinerons en détail la conception de cette
UI
aide de
SwiftUI
- des éléments simples à leurs compositions plus complexes. Mais d'abord, comprenons la structure de données que nous avons.
Ainsi, la solution à notre problème a été divisée en plusieurs étapes:
- Téléchargez les données d'un fichier
JSON
et présentez-les dans un format "interne" pratique - Créer une
UI
pour un «ensemble de graphiques» - Combinez différents «jeux de cartes»
Télécharger les données
À notre disposition,
Telegram a fourni
des données
JSON contenant plusieurs «ensembles de graphiques». Chaque «ensemble de
chart
» d'un
chart
contient plusieurs «graphiques» (ou «lignes») de
chart.columns
de
chart.columns
. Chaque «graphique» («lignes») a un repère à la position
0
-
"x"
,
"y0"
,
"y1"
,
"y2"
,
"y3"
, suivi de l'une ou l'autre des valeurs de temps sur l'axe X («x») ou les valeurs de "Graphics" ("Lines") (
"y0"
,
"y1"
,
"y2"
,
"y3"
) sur l'axe
Y
:

La présence de toutes les «lignes» dans le «jeu de cartes» est facultative. Les valeurs de la "colonne" x sont des horodatages UNIX en millisecondes.
De plus, chaque «jeu de
chart
» individuel du
chart
est fourni avec des couleurs de
chart.colors
au format de 6 chiffres hexadécimaux (par exemple, «#AAAAAA») et des
chart.names
.
Pour construire le modèle de données situé dans le fichier
JSON
, j'ai utilisé l'excellent service
quicktype . Sur ce site, vous insérez un morceau de texte à partir d'un fichier
JSON
et spécifiez le langage de programmation (
Swift
), le nom de la structure (
Chart
), qui sera formé après «l'analyse» de ces données
JSON
et c'est tout.
Un code est généré dans la partie centrale de l'écran, que nous copions dans notre application dans un fichier séparé nommé
Chart.swift
. C'est là que nous placerons le modèle de données au format JSON. En utilisant le chargeur de données du fichier
JSON
vers le modèle emprunté aux
démos SwiftUI Generic
, j'ai obtenu un tableau de
columns: [ChartElement]
, qui est une collection d '«ensembles de
columns: [ChartElement]
» au format
Telegram
.
La
ChartElement
données
ChartElement
, contenant des tableaux d'éléments hétérogènes, n'est pas très appropriée pour un travail interactif intensif avec des graphiques, en outre, les horodatages sont présentés au format
UNIX
en millisecondes (par exemple,
1542412800000, 1542499200000, 1542585600000, 1542672000000
) et les couleurs au format hexadécimal 6 chiffres (par exemple,
"#AAAAAA"
).
Par conséquent, à l'intérieur de notre application, nous utiliserons les mêmes données, mais dans un format «interne» différent et assez simple
[LinesSet]
. Le tableau
[LinesSet]
est une collection de «jeux de
LinesSet
»
LinesSet
, chacun contenant des horodatages
xTime
au format
"Feb 12, 2019"
(axe
X
) et plusieurs
lines
«Graphiques» (axe
Y
):

Les données de chaque graphique linéaire (ligne) sont présentées
- un tableau de
points: [Int]
entiers points: [Int]
, - nommé "Graphics"
title: String
, - type "Graphics"
type: String?
, color : UIColor
au format UIColor
,- nombre de points
countY: Int
.
De plus, tout «graphique» peut être masqué ou affiché selon la valeur de
isHidden: Bool
. Les
upperBound
lowerBound
et
upperBound
ajuster la plage de temps prennent des valeurs de
0
à
1
et affichent non seulement la taille de la fenêtre temporelle «mini-carte» (
upperBound
-
lowerBound
), mais aussi son emplacement sur l'axe des temps
X
:

Les structures de données
JSON
[ChartElement]
et les structures de données des
LinesSet
et
Line
«internes» se trouvent dans le fichier
Chart.swift . Le code pour charger
JSON
données
JSON
et les convertir en une structure interne se trouve dans le fichier
Data.swift . Des détails sur ces transformations peuvent être trouvés
ici .
En conséquence, nous avons reçu des données sur les «jeux de graphiques» au format interne sous forme de tableau de
chartsData
.

Il s'agit de notre
données, mais pour travailler dans
SwiftUI
il est nécessaire de s'assurer que toutes les modifications apportées par l'utilisateur dans le tableau
chartsData
(modification de la «fenêtre» temporaire, masquage / affichage des «graphiques») conduisent à des mises à jour automatiques de nos
Views
.
Nous allons créer
@EnvironmentObject
. Cela nous permettra d'utiliser le
données partout où cela est nécessaire et, en outre, de mettre à jour automatiquement nos
Views
si les données changent. C'est quelque chose comme
Singleton
ou des données globales.
@EnvironmentObject
nous oblige à créer une
final class UserData
, qui se trouve dans
le fichier
UserData.swift , stocke les données
chartsData
et implémente le protocole
ObservableObject
:

La présence de "wrappers"
@Published
vous permettra de publier des "nouvelles" que ces propriétés des
charts
de la classe
UserData
ont changé, de sorte que toutes les
Views
"abonnées à ces nouvelles" dans
SwiftUI
pourront sélectionner automatiquement les nouvelles données et les mettre à jour.
Rappelez-vous que dans la propriété des
charts
, les valeurs
isHidden
peuvent changer pour n'importe quel «
isHidden
» (elles vous permettent de masquer ou d'afficher ces «graphiques»), ainsi que la
lowerBound
inférieure inférieure et la
upperBound
supérieure supérieure
upperBound
l'intervalle de temps pour chaque «ensemble de graphiques» individuel.
Nous voulons utiliser la propriété
charts
de la classe
UserData
dans notre application et nous n'avons pas à les synchroniser manuellement avec l'
UI
grâce à
@EnvironmentObject
.
Pour ce faire, lors du démarrage de l'application, nous devons créer une instance de la classe
UserData ()
afin de pouvoir ensuite y accéder n'importe où dans notre application. Nous le ferons dans le fichier
SceneDelegate.swift
à l'intérieur de la méthode
scene (_ : , willConnectTo: , options: )
. C'est ici que notre
ContentView
est créé et lancé, et c'est ici que nous devons transmettre le
ContentView
n'importe quel
@EnvironmentObject
nous avons
@EnvironmentObject
afin que
SwiftUI
puisse les rendre disponibles à n'importe quelle autre
View
:

Maintenant, dans n'importe quelle
View
pour accéder aux données
@Published
de la classe
UserData
, nous devons créer la variable
var
à l'aide du wrapper
@EnvironmentObject
. Par exemple, lors de la définition de la plage de temps dans
RangeView
nous créons la variable
var userData
avec le
UserData
TYPE:

Ainsi, dès que nous avons implémenté un
@EnvironmentObject
dans «l'environnement» de l'application, nous pouvons immédiatement commencer à l'utiliser soit au plus haut niveau, soit au 10e niveau ci-dessous - cela n'a pas d'importance. Mais plus important encore, chaque fois qu'une
View
change d '"environnement", toutes les
Views
qui ont cet
@EnvironmentObject
seront automatiquement
@EnvironmentObject
à
@EnvironmentObject
, assurant ainsi la synchronisation avec les données.
Passons à la conception de l'interface utilisateur (
UI
).
Interface utilisateur (UI) pour un «ensemble de graphiques»
SwiftUI
propose une technologie composite pour créer des
SwiftUI
partir de nombreuses petites
Views
, et nous avons déjà vu que notre application correspond très bien à cette technologie, car elle se divise en petits morceaux: l'ensemble «
ChartView
Charts», «
GraphsForChart
Charts», les graduations en
Y
-
YTickerView
, indicateur de valeurs «Graphs» piloté par l'utilisateur
IndicatorView
, ligne
TickerView
«en cours d'exécution» avec
TickerView
sur l'axe
X
,
RangeView
«fenêtre temporelle»
RangeView
, marques pour masquer / afficher
CheckMarksView
«Graphiques». Nous pouvons non seulement créer toutes ces
Views
indépendamment les unes des autres, mais également tester immédiatement dans
Xcode 11
utilisant des
Previews
(vues «en direct» préliminaires) sur les données de test. Vous serez surpris de la simplicité du code pour les créer à partir d'autres
Views
plus basiques.
GraphView
- «Graphique» («Ligne»)
La première
View
, par laquelle nous allons commencer, est en fait le «graphique» lui-même (ou «ligne»). Nous l'appellerons
GraphView
:

La création d'un
GraphView
, comme d'habitude, commence par la création d'un nouveau fichier dans
Xcode 11
utilisant le menu
File
→
New
→
File
:

Ensuite, nous sélectionnons le TYPE souhaité du fichier - c'est le fichier
SwiftUI
:

... donnez le nom "GraphView" à notre
View
et indiquez son emplacement:

Cliquez sur le bouton
"Create"
et obtenez une
View
standard avec du
Text ( "Hello World!")
Au milieu de l'écran:

Notre tâche est de remplacer le
Text ("Hello World!")
«graphique», mais voyons d'abord quelles données initiales nous avons pour créer le «graphique»:
- nous avons les valeurs de
line.points
"Graphics" line: Line
, - plage temporelle
rangeTime
, qui est une plage d'index Range
xTime
sur l'axe X, - plage de valeurs
rangeY: Range
«Graphics» pour l' rangeY: Range
Y, - épaisseur de la ligne de
lineWidth
trait "Graphics" lineWidth
.
Ajoutez ces propriétés à la structure
GraphView
:

Si nous voulons utiliser pour nos
Previews
«graphiques» (aperçus), qui ne sont possibles que pour
MacOS Catalyna
, nous devons alors lancer un
GraphView
avec la plage d'index
rangeTime
et les données de
line
des «graphiques» lui-même:

Nous avons déjà les données de test
chartsData
que nous avons obtenues du fichier
chart.json
JSON
, et nous les avons utilisées pour les
Previews
.
Dans notre cas, ce sera le premier «jeu de
chartsData[0]
»
chartsData[0]
et le premier «graphique» dans ce jeu de
chartsData[0].lines[0]
, que nous fournirons
GraphView
comme paramètre de
line
.
Nous utiliserons la plage complète des indices
0..<(chartsData[0].xTime.count - 1)
comme intervalle de temps
rangeTime
.
Les
lineWidth
rangeY
et
lineWidth
peuvent être définis en externe ou non, car ils ont déjà des valeurs initiales:
rangeY
est
nil
et
lineWidth
vaut
1
.
Nous avons intentionnellement fait un TYPE de la propriété
Optional
rangeY
TYPE, car si
rangeY
pas défini en externe et
rangeY = nil
, alors nous calculons les valeurs minimum
minY
et maximum
maxY
du "Graphics" directement à partir des données
line.points
:

Ce code se compile, mais nous avons toujours une
View
standard à l'écran avec le texte
Text ("Hello World!")
Au milieu de l'écran:

Parce que dans le
body
nous devons remplacer le texte
Text ("Hello World!")
Path
, qui va construire notre "Graph: points:
line.points
utilisant la commande
addLines(_:)
(presque comme dans
Core Graphics
)


Nous encerclerons le
stroke (...)
notre
Path
ligne dont l'épaisseur est
lineWidth
, et la couleur de la ligne de trait correspondra à la couleur par défaut (c'est-à-dire le noir):

Nous pouvons remplacer la couleur noire de la ligne de trait par la couleur spécifiée dans notre
line.color
«Ligne» particulier «Couleur»:

Pour que notre «graphique» soit placé dans des rectangles de n'importe quelle taille, nous utilisons le conteneur
GeometryReader
. Dans la documentation
Apple
GeometryReader
est une vue «conteneur» qui définit son contenu en fonction de sa propre
size
et de son propre espace de coordonnées. Essentiellement,
GeometryReader
est une autre
View
! Parce que presque TOUT dans
SwiftUI
est
View
!
GeometryReader
vous permettra, contrairement à d'autres
Views
d'accéder à des informations utiles supplémentaires que vous pouvez utiliser lors de la conception de votre
View
personnalisée.
Nous utilisons les conteneurs
GeometryReader
et
Path
pour créer
GraphView
adaptable à n'importe quelle taille. Et si nous regardons attentivement notre code, nous verrons dans la fermeture du
GeometryReader
variable appelée
geometry
:

Cette variable a le
GeometryProxy
TYPE, qui à son tour est une structure
struct
avec de nombreuses "surprises":
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 }
D'après la définition de
GeometryProxy
, nous voyons qu'il existe deux variables calculées
var size
et
var safeAreaInsets
, un
frame( in:)
fonction
frame( in:)
et un
subscript getter
. Nous avions seulement besoin de la variable de
size
pour déterminer la largeur de
geometry.size.width
et la hauteur de
geometry.size.height
zone de dessin «Graphics».
De plus, nous permettons à notre "Graphique" de s'animer en utilisant le modificateur d'
animation (.linear(duration: 0.6))
.

GraphView_Previews
nous permet de tester très simplement tous les «graphiques» de n'importe quel «ensemble». Vous trouverez ci-dessous le «graphique» de «l'ensemble de graphiques» avec l'index 4:
chartsData[4]
et l'index 0 «Graphics» dans cet ensemble:
chartsData[4].lines[0]
:

Nous définissons la
height
«Graphics» à 400 à l'aide du
frame (height: 400)
, la largeur reste la même que la largeur de l'écran. Si nous n'utilisions pas de
frame (height: 400)
, le "Graphique" occuperait tout l'écran.
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 , .