SwiftUI pour la dernière tâche compétitive de Telegram Charts (mars 2019): tout est simple



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 FileNewFile :



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 :



YTickerViewY .


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



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



YTickerView «» rangeY . Y - — overlay



, Y — colorYAxis , — colorYMark :



RangeView - «mini-map».


( lowerBound , upperBound ) « »:



RangeViewmini - map " " Views .

View , RangeView :



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

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



var userData Views , .

RangeView «», DragGesture :

1. «», «» X , :



2. , «» lowerBound , «»:



3. , «» upperBound , «»:



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



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



, «» «».

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



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



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



CheckMarksView — «» «».


CheckMarksView HStack checkBoxes isHidden «» « » chart :



CheckBox Button CheckButton , SimulatedButton .



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

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



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




CheckBoView SimulatedButton CheckButton $ line :




isHidden line SimulatedButton onTapGesture



CheckButtonaction Button :



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



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

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



Views .


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

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

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



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

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

ChartView , « » :



VStack :



3 « » ChartView:

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

« » ListChartsView List :



3D ScrollView , HStack ForEach :



: «» mini- map , «».

ZStack «».


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



«» VStack , ZStack ForEach :



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

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



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



«» TapGesture , LongPressGesture DragGesture :



Tap « » ChartView RangeView CheckMarksView :



TabView 3- « » ChartView .





3 c Image Text , VStack .

3- « » ChartViews :

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

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

Github .

SwiftUI ...


, :

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

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


All Articles