
Saya akan mulai dengan pernyataan bahwa aplikasi yang dibahas dalam artikel ini memerlukan
Xcode 11 dan
MacOS Catalina jika Anda ingin menggunakan
Live Previews
, dan
Mojave
jika Anda menggunakan simulator. Kode aplikasi ada di
Github .
Tahun ini di
WWDC 2019 ,
Apple
mengumumkan
SwiftUI
, cara deklaratif baru untuk membangun antarmuka pengguna (UI) di semua perangkat
Apple
. Ini hampir merupakan keberangkatan yang sepenuhnya dari
UIKit
biasa, dan saya - seperti banyak pengembang lainnya - benar-benar ingin melihat alat baru ini beraksi.
Artikel ini menyajikan pengalaman penyelesaian dengan
SwiftUI
masalah yang kode dalam
UIKit
jauh lebih kompleks dan tidak dapat
UIKit
menurut pendapat saya dengan cara yang mudah dibaca.
Tugas ini terkait dengan kontes
Telegram terakhir untuk
Android
,
iOS
dan pengembang
JS
, yang diadakan dari 10 Maret hingga 24 Maret 2019. Dalam kompetisi ini, tugas sederhana diusulkan untuk menampilkan secara grafis intensitas penggunaan sumber daya tertentu di Internet tergantung pada waktu berdasarkan data
JSON
. Sebagai pengembang
iOS
, Anda harus menggunakan
Swift
untuk mengirimkan kode yang ditulis dari awal ke kompetisi tanpa menggunakan pustaka khusus asing untuk merencanakan.
Tugas ini membutuhkan keterampilan untuk bekerja dengan kemampuan grafis dan animasi iOS:
Core Graphics ,
Core Animation ,
Metal ,
OpenGL ES . Beberapa alat ini adalah alat pemrograman tingkat rendah yang tidak berorientasi objek. Pada dasarnya, di
iOS
tidak ada templat yang dapat diterima untuk menyelesaikan tugas-tugas grafis sepintas yang terlihat sepintas lalu. Oleh karena itu, setiap kontestan menciptakan animatornya sendiri (
Render ) berdasarkan
Metal ,
CALayers ,
OpenGL ,
CADisplayLink . Ini menghasilkan banyak kode dari mana tidak mungkin untuk meminjam dan mengembangkan apa pun, karena ini adalah karya murni "hak cipta" yang hanya bisa dikembangkan oleh penulis. Namun, seharusnya tidak demikian.
Dan pada awal Juni di
WWDC 2019 ,
SwifUI
muncul -
framework
baru yang dikembangkan oleh
Apple
, ditulis dalam bahasa
Swift
dan dirancang untuk secara deskriptif menggambarkan antarmuka pengguna (
UI
) dalam kode. Anda menentukan
subviews
mana
subviews
diperlihatkan di
View
Anda, data apa yang menyebabkan
subviews
ini berubah, pengubah apa yang perlu Anda terapkan padanya, untuk menjadikannya posisi di tempat yang tepat, untuk memiliki ukuran dan gaya yang tepat. Elemen yang sama pentingnya
SwiftUI
adalah kontrol aliran data yang dapat dimodifikasi pengguna, yang pada gilirannya memperbarui
UI
.
Pada artikel ini saya ingin menunjukkan bagaimana tugas kontes
Telegram di
SwiftUI
diselesaikan dengan cepat dan mudah. Selain itu, ini adalah proses yang sangat menarik.
Tugas
Aplikasi kompetitif harus secara bersamaan menampilkan 5 "set Grafik" di layar menggunakan data yang disediakan oleh
Telegram
. Untuk satu "set Grafik",
UI
sebagai berikut:

Di bagian atas ada "Zona grafik" dengan skala umum di sepanjang sumbu Y normal dengan tanda dan garis kotak horizontal. Di bawahnya adalah garis merayap dengan cap waktu sepanjang sumbu X sebagai tanggal.
Bahkan lebih rendah adalah apa yang disebut "peta mini" (seperti dalam
Xcode 11
), yaitu, "jendela" transparan yang mendefinisikan bagian dari periode waktu "Grafik" kami, yang disajikan secara lebih rinci di "zona Charts" atas. "Peta mini" ini tidak hanya dapat dipindahkan sepanjang sumbu
X
, tetapi juga lebarnya dapat diubah, yang mempengaruhi skala waktu di "area Grafik".
Dengan bantuan
checkboxs
dilukis dengan warna "Grafik" dan diberikan namanya, Anda dapat menolak untuk menunjukkan "Grafik" yang sesuai dengan warna ini di "Zona grafik".
Ada banyak "set Grafik", dalam contoh pengujian kami ada 5, misalnya, dan semuanya harus ditempatkan pada satu layar.
Dalam
UI
dirancang menggunakan
SwiftUI
tidak perlu tombol untuk beralih antara mode
Dark
dan
Light
, ini sudah dibangun ke dalam
SwiftUI
. Selain itu,
SwiftUI
lebih banyak opsi untuk menggabungkan "set Grafik" (yaitu, set layar yang disajikan di atas) daripada sekadar menggulir ke bawah tabel, dan kita akan melihat beberapa opsi yang sangat menarik ini.
Tapi pertama-tama, mari kita fokus pada menampilkan satu "set
SwiftUI
" yang akan
SwiftUI
buat
ChartView
:

SwiftUI
memungkinkan Anda untuk membuat dan menguji
UI
kompleks dalam potongan-potongan kecil, dan kemudian sangat mudah untuk merakit potongan-potongan ini menjadi sebuah teka-teki. Kami akan melakukannya.
ChartView
kami
ChartView
sangat baik menjadi potongan-potongan kecil ini:
GraphsForChart
- ini adalah grafik itu sendiri, dibangun untuk satu "set Grafik" tertentu. "Grafik" ditampilkan untuk rentang waktu yang dikendalikan oleh pengguna menggunakan RangeView
"peta mini", yang akan disajikan di bawah ini.YTickerView
adalah sumbu Y
dengan ketinggian dan kisi horizontal yang sesuai.IndicatorView
adalah indikator yang digerakkan pengguna secara horizontal yang memungkinkan Anda untuk melihat nilai-nilai "Grafik" dan waktu untuk posisi indikator yang sesuai pada sumbu waktu pada sumbu X
TickerView
- "garis merayap" menunjukkan cap waktu pada sumbu X
sebagai tanggal,RangeView
- "jendela" sementara, disesuaikan oleh pengguna menggunakan gerakan, untuk mengatur interval waktu untuk "Grafik",CheckMarksView
- berisi "tombol" yang diwarnai dengan warna "Charts" dan memungkinkan Anda untuk mengontrol keberadaan " ChartView
" di ChartView
.
ChartView
pengguna dapat berinteraksi dengan
ChartView
dalam tiga cara:
1. mengontrol "peta mini" menggunakan gerakan
DragGesture
- itu dapat menggeser "jendela" sementara ke kanan dan kiri dan mengurangi / menambah ukurannya:

2. pindahkan indikator ke arah horisontal, menunjukkan nilai "Grafik" pada titik waktu tertentu:

3. sembunyikan / tampilkan "Charts" tertentu menggunakan tombol-tombol yang diwarnai dengan warna "Charts" dan terletak di bagian bawah
ChartView
:

Kami dapat menggabungkan berbagai "Set Bagan" (kami memiliki 5 di antaranya dalam data uji) dengan berbagai cara, misalnya, dengan menempatkan semuanya secara bersamaan di satu layar menggunakan daftar
List
(seperti tabel yang dapat digulir ke atas dan ke bawah):

atau menggunakan
ScrollView
dan tumpukan horisontal
HStack
dengan efek 3D:

... atau dalam bentuk
ZStack
"kartu" yang saling bertumpukan, urutannya dapat diubah: "kartu" atas dengan "satu set Grafik" dapat ditarik ke bawah cukup jauh untuk melihat kartu berikutnya, dan jika Anda terus menyeretnya ke bawah, maka itu " pergi "ke tempat terakhir di
ZStack
, dan" kartu "" ini selanjutnya ":

Dalam
UI
rumit ini - "tabel gulir", tumpukan horizontal dengan efek
3D
,
ZStack
"kartu" saling bertumpukan satu sama lain - semua cara interaksi pengguna bekerja sepenuhnya: bergerak sepanjang garis waktu dan mengubah "skala" dari
mini - map
, indikator dan tombol sembunyikan tombol "Grafik".
Selanjutnya kami akan mempertimbangkan secara rinci desain
UI
ini menggunakan
SwiftUI
- dari elemen sederhana hingga komposisi mereka yang lebih kompleks. Tapi pertama-tama, mari kita pahami struktur data yang kita miliki.
Jadi, solusi untuk masalah kami dibagi menjadi beberapa tahap:
- Unduh data dari file
JSON
dan sajikan dalam format "internal" yang nyaman - Buat
UI
untuk satu "set Grafik" - Gabungkan berbagai "set grafik"
Unduh data
Kami siap membantu,
Telegram menyediakan data
JSON yang berisi beberapa "set Grafik." Setiap "
chart
set" individu dari
chart
berisi beberapa "Bagan" (atau "Garis") dari
chart.columns
. Setiap "Grafik" ("Garis") memiliki tanda pada posisi
0
-
"x"
,
"y0"
,
"y1"
,
"y2"
,
"y3"
, diikuti oleh nilai waktu pada sumbu X ("x") , atau nilai "Graphics" ("Lines") (
"y0"
,
"y1"
,
"y2"
,
"y3"
) pada sumbu
Y
:

Kehadiran semua "Garis" di "set grafik" adalah opsional. Nilai untuk "kolom" x adalah cap waktu UNIX dalam milidetik.
Selain itu, masing-masing "
chart
set"
chart
dilengkapi dengan warna
chart.colors
dalam format 6 digit heksadesimal (misalnya, "#AAAAAA") dan
chart.names
.
Untuk membangun Model Data yang terletak di file
JSON
, saya menggunakan layanan
tipe cepat yang sangat baik. Di situs ini, Anda menyisipkan sepotong teks dari file
JSON
dan menentukan bahasa pemrograman (
Swift
), nama struktur (
Chart
), yang akan dibentuk setelah "parsing" data
JSON
ini dan hanya itu.
Kode dihasilkan di bagian tengah layar, yang kami salin ke aplikasi kami dalam file terpisah bernama
Chart.swift
. Di sinilah kita akan menempatkan Model Data format JSON. Dengan menggunakan Loader data dari file
JSON
ke Model yang dipinjam dari
demo SwiftUI Generic
, saya mendapatkan array
columns: [ChartElement]
, yang merupakan kumpulan "Set
columns: [ChartElement]
" dalam format
Telegram
.
ChartElement
data
ChartElement
, berisi array elemen heterogen, tidak terlalu cocok untuk pekerjaan interaktif intensif dengan grafik, selain itu, cap waktu disajikan dalam format
UNIX
dalam milidetik (misalnya,
1542412800000, 1542499200000, 1542585600000, 1542672000000
), dan warna dalam 6 format heksadesimal digit (misalnya,
"#AAAAAA"
).
Oleh karena itu, di dalam aplikasi kita akan menggunakan data yang sama, tetapi dalam format "internal" yang berbeda dan cukup sederhana
[LinesSet]
.
[LinesSet]
adalah kumpulan dari "
LinesSet
Set"
LinesSet
, yang masing-masing berisi cap waktu
xTime
dalam format
"Feb 12, 2019"
(sumbu
X
) dan beberapa
lines
"Bagan" (sumbu
Y
):

Data untuk setiap Garis Grafik (Garis) disajikan
- array
points: [Int]
integer points: [Int]
, - bernama "Graphics"
title: String
, - ketik "Graphics"
type: String?
, color : UIColor
dalam format UIColor
Swift
- UIColor
,- jumlah poin
countY: Int
.
Selain itu, "Grafik" apa pun dapat disembunyikan atau ditampilkan tergantung pada nilai
isHidden: Bool
.
upperBound
dan
upperBound
menyesuaikan rentang waktu mengambil nilai dari
0
hingga
1
dan tidak hanya menampilkan ukuran jendela waktu "mini map" (
upperBound
-
lowerBound
), tetapi juga lokasinya pada sumbu waktu
X
:

Struktur data
JSON
[ChartElement]
dan struktur data
LinesSet
dan
Line
"internal" ada di file
Chart.swift . Kode untuk memuat data
JSON
dan mengubahnya menjadi struktur internal terletak di file
Data.swift . Rincian tentang transformasi ini dapat ditemukan di
sini .
Sebagai hasilnya, kami menerima data tentang "set grafik" dalam format internal sebagai array dari
chartsData
.

Ini adalah
Data kami, tetapi untuk bekerja di
SwiftUI
, Anda perlu memastikan bahwa setiap perubahan yang dibuat oleh pengguna dalam array
chartsData
(mengubah "jendela" sementara, menyembunyikan / menampilkan "Charts") mengarah pada pembaruan otomatis
Views
kami.
Kami akan membuat
@EnvironmentObject
. Ini akan memungkinkan kami untuk menggunakan
Data di mana pun itu diperlukan, dan di samping itu, secara otomatis memperbarui
Views
kami jika data berubah. Ini adalah sesuatu seperti
Singleton
atau data global.
@EnvironmentObject
mengharuskan kita untuk membuat beberapa
final class UserData
, yang terletak di file
UserData.swift , menyimpan data
chartsData
dan mengimplementasikan protokol
ObservableObject
:

Kehadiran "pembungkus" yang telah
@Published
pada
@Published
akan memungkinkan Anda memposting "berita" bahwa sifat-sifat
charts
dari kelas
UserData
telah berubah, sehingga
Views
apa pun "berlangganan berita ini" di
SwiftUI
akan dapat secara otomatis memilih data baru dan memperbarui.
Ingatlah bahwa di properti
charts
nilai
isHidden
untuk "
isHidden
" apa pun dapat berubah (mereka memungkinkan Anda untuk menyembunyikan atau menampilkan "Charts" ini), serta batas waktu batas bawah dan batas atas yang lebih rendah untuk setiap "set Grafik" masing-masing individu.
Kami ingin menggunakan properti
charts
dari kelas
UserData
seluruh aplikasi kami dan kami tidak harus menyinkronkannya dengan
UI
secara manual berkat
@EnvironmentObject
.
Untuk melakukan ini, ketika memulai aplikasi, kita harus membuat turunan dari kelas
UserData ()
sehingga selanjutnya kita dapat mengaksesnya di mana saja di aplikasi kita. Kami akan melakukan ini dalam file
SceneDelegate.swift
di dalam
scene (_ : , willConnectTo: , options: )
metode. Di sinilah
ContentView
kami dibuat dan diluncurkan, dan di sinilah kami harus meneruskan
ContentView
@EnvironmentObject
apa pun yang kami
@EnvironmentObject
sehingga
SwiftUI
dapat membuatnya tersedia untuk
View
lain:

Sekarang, dalam
View
apa pun
View
untuk mengakses data
@Published
dari kelas
UserData
, kita perlu membuat variabel
var
menggunakan pembungkus
@EnvironmentObject
. Misalnya, ketika mengatur rentang waktu di
RangeView
kami membuat variabel
var userData
dengan TYPE
UserData
:

Jadi, segera setelah kami menerapkan beberapa
@EnvironmentObject
ke dalam "lingkungan" aplikasi, kami dapat segera mulai menggunakannya baik pada level tertinggi atau level 10 di bawah - tidak masalah. Tetapi yang lebih penting, setiap kali
View
mengubah "lingkungan", semua
Views
yang memiliki
@EnvironmentObject
ini akan secara otomatis
@EnvironmentObject
, sehingga memastikan sinkronisasi dengan data.
Mari kita beralih ke merancang antarmuka pengguna (
UI
).
User Interface (UI) untuk satu "set Grafik"
SwiftUI
menawarkan teknologi komposit untuk membuat
SwiftUI
dari banyak
Views
kecil, dan kami telah melihat bahwa aplikasi kami sangat cocok dengan teknologi ini, karena terbagi menjadi beberapa bagian:
ChartView
"
ChartView
Charts", "
GraphsForChart
Charts",
Y
-axis
YTickerView
-
YTickerView
, nilai indikator "Charts" yang digerakkan pengguna,
TickerView
"
TickerView
" dengan
TickerView
waktu pada sumbu
X
,
RangeView
"time window" yang
RangeView
, tanda pada persembunyian / menampilkan "Charts"
CheckMarksView
. Kami tidak hanya dapat membuat semua
Views
ini secara independen satu sama lain, tetapi juga segera menguji dalam
Xcode 11
menggunakan
Previews
(tampilan "hidup" awal) pada data uji. Anda akan terkejut betapa sederhana kode ini untuk membuatnya dari
Views
lain yang lebih mendasar.
GraphView
- "Graph" ("Line")
Pandangan pertama, yang akan kita mulai, sebenarnya adalah "Grafik" itu sendiri (atau "Garis"). Kami akan menyebutnya
GraphView
:

Membuat
GraphView
, seperti biasa, dimulai dengan membuat file baru di
Xcode 11
menggunakan menu
File
→
New
→
File
:

Kemudian kami memilih JENIS file yang diinginkan - ini adalah file
SwiftUI
:

... berikan nama "GraphView" ke
View
kami dan tunjukkan lokasinya:

Klik tombol
"Create"
dan dapatkan
View
standar dengan
Text ( "Hello World!")
Di tengah layar:

Tugas kita adalah mengganti
Text ("Hello World!")
Dengan "Grafik", tetapi pertama-tama, mari kita lihat data awal apa yang kita miliki untuk membuat "Grafik":
- kami memiliki nilai
line.points
"Graphics" line: Line
, - rentang waktu
rangeTime
, yang merupakan rentang indeks. Range
prangko waktu xTime
pada sumbu X, - rentang nilai
rangeY: Range
"Grafik" untuk rangeY: Range
Y, - ketebalan garis garis "Grafik" garis
lineWidth
.
Tambahkan properti ini ke struktur
GraphView
:

Jika kita ingin menggunakan untuk
Previews
"Grafik" kami (preview), yang hanya mungkin untuk
MacOS Catalyna
, maka kita harus memulai
GraphView
dengan rentang indeks
rangeTime
dan data
line
"Grafik" itu sendiri:

Kami sudah memiliki data uji
chartsData
yang kami dapatkan dari file
JSON
chart.json
, dan kami menggunakannya untuk
Previews
.
Dalam kasus kami, ini akan menjadi yang pertama "
chartsData[0]
set"
chartsData[0]
dan "Bagan" pertama dalam bagan ini
chartsData[0].lines[0]
, yang kami akan berikan
GraphView
sebagai parameter
line
.
Kami akan menggunakan rentang penuh indeks
0..<(chartsData[0].xTime.count - 1)
sebagai interval waktu intervalTime.
lineWidth
dan
lineWidth
dapat diatur secara eksternal, atau tidak diatur, karena mereka sudah memiliki nilai awal:
rangeY
adalah
nil
, dan
lineWidth
adalah
1
.
Kami sengaja membuat JENIS dari
rangeY
properti
Optional
JENIS, karena jika
rangeY
tidak diatur secara eksternal dan
rangeY = nil
, maka kami menghitung nilai
minY
minimum dan maksimum maksimum dari "Grafik" langsung dari data
line.points
:

Kode ini dikompilasi, tetapi kami masih memiliki
View
standar di layar dengan teks
Text ("Hello World!")
Di tengah layar:

Karena di dalam
body
kita harus mengganti
Text ("Hello World!")
Dengan
Path
, yang akan membangun "Grafik:


Kami akan melingkari
stroke (...)
Path
kami
Path
garis yang ketebalannya adalah garis
lineWidth
, dan warna garis garis akan sesuai dengan warna default (yaitu, hitam):

Kita dapat mengganti warna hitam untuk garis goresan dengan warna yang ditentukan dalam
line.color
"Garis" khusus "Warna":

Agar "Grafik" kami ditempatkan dalam persegi empat ukuran apa pun, kami menggunakan wadah
GeometryReader
. Dalam dokumentasi
Apple
GeometryReader
adalah tampilan “penampung” yang mendefinisikan kontennya sebagai fungsi dari ukurannya sendiri dan mengoordinasikan ruang. Pada dasarnya,
GeometryReader
adalah pandangan lain! Karena hampir SEMUA yang ada di
SwiftUI
adalah
View
!
GeometryReader
akan memungkinkan ANDA, tidak seperti
Views
lainnya
Views
untuk mengakses beberapa informasi berguna tambahan yang dapat Anda gunakan saat mendesain
View
kustom Anda.
Kami menggunakan kontainer
GeometryReader
dan
Path
untuk membuat
GraphView
dapat disesuaikan dengan ukuran apa pun. Dan jika kita perhatikan dengan teliti kode kita, kita akan melihat pada penutupan untuk
GeometryReader
variabel yang disebut
geometry
:

Variabel ini memiliki
GeometryProxy
TYPE, yang pada gilirannya adalah struktur
struct
dengan banyak "kejutan":
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 }
Dari definisi
GeometryProxy
, kita melihat bahwa ada dua variabel yang dihitung
var size
var safeAreaInsets
dan
var safeAreaInsets
, satu
frame( in:)
fungsi
frame( in:)
dan
subscript getter
. Kami hanya membutuhkan variabel
size
untuk menentukan lebar
geometry.size.width
size
Lebar dan tinggi
geometry.size.height
size
Tinggi dari area gambar "Grafik".
Selain itu, kami mengaktifkan "Grafik" kami untuk beranimasi menggunakan pengubah
animation (.linear(duration: 0.6))
.

GraphView_Previews
memungkinkan kita untuk menguji "Grafik" dari "set" apa saja dengan sangat mudah. Di bawah ini adalah "Chart" dari "chart set" dengan indeks 4:
chartsData[4]
dan indeks 0 "Graphics" di set ini:
chartsData[4].lines[0]
:

Kami mengatur
height
"Grafik" ke 400 menggunakan
frame (height: 400)
, lebarnya tetap sama dengan lebar layar. Jika kami tidak menggunakan
frame (height: 400)
, maka "Grafik" akan menempati seluruh layar.
Kami tidak menentukan rentang nilai rangeY
dan GraphView
menggunakan nilai nil
default, dalam hal ini "Bagan" mengambil nilai minimum dan maksimum dalam interval waktu rangeTime
:
Meskipun kami menggunakan Path
pengubah untuk model kami animation (.linear(duration: 0.6))
, tidak ada animasi yang akan terjadi, misalnya, ketika mengubah rentang rangeY
nilai " Grafik. " "Bagan" hanya akan "melompat" dari satu nilai rentang rangeY
ke yang lain tanpa animasi.Alasannya sederhana: kami mengajarkan SwiftUI
cara menggambar "Grafik" untuk rentang tertentu rangeY
, tetapi kami tidak mengajarkan SwiftUI
cara mereproduksi "Grafik" beberapa kali dengan nilai menengah kisaran rangeY
antara awal dan akhir, dan untuk itu dalamSwiftUI
memenuhi protokol Animatable
.Untungnya, jika milik Anda View
adalah "figur", yaitu View
, yang mengimplementasikan protokol Shape
, maka protokol sudah diterapkan untuk itu Animatable
. Ini berarti bahwa ada properti yang dihitung animatableData
dengan mana kita dapat mengontrol proses animasi, tetapi secara default diatur ke EmptyAnimatableData
, yaitu, tidak ada animasi terjadi.Untuk menyelesaikan masalah dengan animasi, pertama-tama kita perlu mengubah "Grafik" kita GraphView
menjadi Shape
. Ini sangat sederhana, kita hanya perlu mengimplementasikan fungsi func path (in rect:CGRect) -> Path
yang pada dasarnya sudah kita miliki dan tunjukkan dengan bantuan properti yang dihitung animatableData
data apa yang ingin kita animasikan:
Perhatikan bahwa tema kontrol animasi adalah topik lanjutan dalamSwiftUI
dan Anda dapat mempelajari lebih lanjut di artikel “Animasi SwiftUI Tingkat Lanjut - Bagian 1: Jalur” . Kita dapat menggunakan"gambar" yang dihasilkan Graph
dalam GraphViewNew
"Grafik" yang jauh lebih sederhana dengan animasi:
Anda tahu bahwa kita tidak perlu GeometryReader
untuk "Grafik" baru kami GraphViewNew
, karena berkat protokol Shape
"angka" kami Graph
akan dapat beradaptasi dengan ukuran induknya View
.Secara alami, Previews
kami mendapatkan hasil yang sama seperti dalam kasus dengan GraphView
:
Dalam kombinasi berikut, kami akan menggunakan GraphViewNew
"Grafik" yang sama untuk menampilkan nilai.GraphsForChart
- set "Grafik" ("Garis")
Tugas ini View
adalah untuk menampilkan SEMUA "Grafik" ("Garis") dari "set Grafik" chart
dalam rentang waktu tertentu rangeTime
dengan sumbu umum Y
, dan lebar "Garis" sama dengan lineWidth
:
Seperti untuk GraphView
dan GraphViewNew
, kami akan membuat GraphsForChart
file baru untuk GraphsForChart.swift
dan menentukan data awal untuk "Set Grafik":- "set Charts" sendiri
chart: LineSet
(nilai aktif Y
), - range
rangeTime: Range
( X
) dari indeks cap waktu "Charts", - ketebalan garis garis grafik
lineWidth
Rentang nilai rangeY: Range
untuk "set grafik" ( Y
) dihitung sebagai gabungan dari rentang individu yang tidak ditunggangi ( isHidden = false
) "Bagan" yang termasuk dalam "set" ini:
Untuk ini, kami menggunakan fungsi rangeOfRanges
: Kami menampilkan
semua NOT charts tersembunyi ( isHidden = false
) dalam ZStack
konstruksi ForEach
, memberikan setiap "Grafik" kemungkinan muncul di layar dan meninggalkan layar "menggunakan pengubah" pindah " transition(.move(edge: .top))
:
Berkat pengubah ini, proses menyembunyikan dan mengembalikan" Grafik " ChartView
akan dilakukan di layar dengan animasi dan akan menjelaskan kepada pengguna mengapa skala telah berubah Y
.Gunakan drawingGroup()
berarti gunakanMetal
untuk menggambar bentuk grafik. Pada data pengujian kami dan pada simulator, Anda tidak akan merasakan perbedaan dalam kecepatan menggambar dengan Metal
dan Metal
, tetapi jika Anda mereproduksi banyak grafik yang agak besar iPhone
, maka Anda akan melihat perbedaan ini. Untuk pengantar yang lebih rinci, kapan menggunakannya drawingGroup()
, Anda dapat melihat artikel "Animasi SwiftUI Tingkat Lanjut - Bagian 1: Jalur" atau menonton sesi video 237 WWDC 2019 ( Membangun Tampilan Kustom dengan SwiftUI ).Seperti halnya GraphViewNew
pengujian GraphsForChart
menggunakan pratinjau, Previews
kita dapat atur "set Grafik" apa pun, misalnya, dengan indeks 0
:
IndicatorView
- Indikator "Grafik" yang dipindahkan secara horizontal.
Indikator ini memungkinkan Anda untuk mendapatkan nilai yang tepat dari "Grafik" dan waktu untuk titik yang sesuai pada waktu di X
:
Indikator ini dibuat untuk "set Grafik" tertentu chart
dan terdiri dari pergerakan sepanjang X
LINE vertikal dengan MARK di atasnya dalam bentuk "lingkaran" di tempat nilai "Grafik". "POSTER" kecil terlampir di bagian atas garis vertikal ini, yang berisi nilai numerik dari "Grafik" dan waktu.
Indikator meluncur oleh pengguna menggunakan gerakan DragGesture
:
Kami menggunakan apa yang disebut eksekusi gerakan "inkremental". Alih-alih jarak terus menerus dari titik awal value.translation.width
, kami akan onChanged
terus menerima jarak dari tempat kami terakhir kali kami melakukan gerakan di pawang :value.translation.width - self.prevTranslation
. Ini akan memberi kita pergerakan indikator yang mulus.Untuk menguji indikator IndicatorView
dengan bantuan Previews
"set Grafik" yang diberikan, chart
kita dapat menarik View
konstruksi "Grafik" yang sudah jadi GraphsForChart
:
Kita dapat mengatur rentang waktu yang lain, tetapi terkoordinasi satu sama lain untuk rangeTime
indikator IndicatorView
dan "Grafik" GraphsForChart
. Ini akan memungkinkan kami untuk memastikan bahwa "lingkaran" yang menunjukkan nilai-nilai "Grafik" berada di tempat yang tepat.TickerView
- X
dengan tanda.
«» ,
X Y
.
X
TickerMarkView
.
TickerMarkView
View
VStack
,
Path
Text
:

« »
chart : LineSet
TickerView
rangeTime
estimatedMarksNumber
, :

«»
ScrollView
HStack
, yang akan bergeser seiring perubahan rentang waktu rangeTime
.Pada TickerView
kami membentuk langkah step
dengan mana prangko waktu muncul TimeMarkView
, berdasarkan rentang waktu tertentu rangeTime
dan lebar layar widthRange
...
... lalu pilih perangko waktu secara bertahap step
dari array chart.xTime
menggunakan indeks indexes
.Sebenarnya X
- garis horizontal - kita akan meletakkan overlay
...
... pada tumpukan horizontal HStack
, dengan cap waktu TimeMarkView
, yang kita maju dengan offset
:
Selain itu, kita dapat mengatur warna X
- itu sendiri colorXAxis
, dan tanda - colorXMark
:
YTickerView
- Y
dengan tanda dan kisi.
Yang ini View
menggambar Y
dengan tanda digital YMarkView
. Tanda itu sendiri YMarkView
sangat sederhana View
dengan tumpukan vertikal VStack
di mana mereka ditempatkan Path
(garis horizontal) dan Text
dengan nomor:
Satu set tanda menyala Y
untuk "set Grafik" tertentu chart
terbentuk di YTickerView
. Rentang nilai rangeY
dihitung sebagai gabungan dari rentang nilai semua "Bagan" yang termasuk dalam "set Bagan" ini menggunakan fungsi rangeOfRanges
. Perkiraan jumlah tanda pada sumbu Y diatur oleh parameter estimatedMarksNumber
:
YTickerView
kami memantau perubahan dalam kisaran nilai "Grafik" rangeY
. Sebenarnya sumbu Y - garis vertikal - kita memaksakan overlay
pada tanda kita ...
Selain itu, kita dapat mengatur warna sumbu Y itu sendiri colorYAxis
, dan tanda - colorYMark
:
RangeView
- mengatur rentang waktu menggunakan "mini-map".
Bagian yang paling mengharukan dari antarmuka pengguna kami adalah mengatur rentang waktu ( lowerBound
, upperBound
) untuk menampilkan "set Charts":
RangeView
- ini mini - map
menyoroti bagian waktu tertentu untuk tujuan pertimbangan yang lebih terinci dari "set Charts" pada yang lain Views
.Seperti pada yang sebelumnya View
, data awal RangeView
adalah:
- "bagan set" itu sendiri
chart: LineSet
(nilai Y
), - tinggi
height
"mini-map"
RangeView
, - lebar
widthRange
"mini-map"
RangeView
, - indentasi
indent
"mini-map"
RangeView
.
Tidak seperti yang lain yang dibahas di atas Views
, kita harus mengubah DragGesture
rentang waktu ( lowerBound
, upperBound
) dengan isyarat dan segera melihat perubahannya, sehingga rentang waktu yang ditentukan pengguna ( lowerBound
, upperBound
) yang dengannya kita akan bekerja disimpan dalam variabel variabel @EnvironmentObject var userData: UserData
:
Setiap perubahan pada variabel var userData
akan menyebabkan menggambar ulang semua Views
tergantung padanya.Karakter utama dalam RangeView
adalah "jendela" transparan, posisi dan ukurannya dikontrol oleh pengguna dengan gerakan DragGesture
:1. jika kita menggunakan gerakan di dalam "jendela" transparan, POSISI "jendela" berubah X
, dan ukurannya tidak berubah:
2. jika kita menggunakan gerakan di bagian gelap yang gelap, maka hanya BORDER KIRI dari "jendela" yang berubah lowerBound
, yang memungkinkan kita untuk mengurangi atau menambah lebar "jendela" transparan:
3. jika kita menggunakan gerakan di bagian yang gelap gelap, hanya BATU YANG TEPAT dari "jendela" yang berubah upperBound
, memungkinkan Anda untuk mengurangi atau menambah lebar "jendela" transparan:
RangeView
terdiri dari 3 elemen dasar yang sangat sederhana: dua persegi panjang Rectangle ()
dan gambar Image
, yang perbatasannya ditentukan oleh properti lowerBound
dan upperBound
dari @EnvironmentObject var userData: UserData
dan disesuaikan menggunakan gerakan DragGesture
:
Kami "overlay" ( overlay
) yang akrab dengan konstruksi ini ( ) kami GraphsForChartView
dengan "Charts" dari "set Charts" yang diberikan chart
:
, «» «».
«» ( ),
lowerBound
upperBound
userData
onChanged
DragGesture
Rectangle ()
Image
...

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

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

CheckMarksView
— «» «».
CheckMarksView
itu adalah tumpukan horisontal HStack
dengan baris checkBoxes
untuk mengalihkan properti dari isHidden
masing-masing "Grafik" di "set Grafik" chart
:
CheckBox
dalam proyek kami dapat diimplementasikan baik menggunakan tombol biasa Button
dan dipanggil CheckButton
, atau menggunakan tombol simulasi SimulatedButton
.
Tombol Button
harus ditiru karena ketika menempatkan beberapa tombol ini di List
salah satu yang terletak lebih tinggi dalam hierarki, mereka "menolak" untuk bekerja dengan benar. Ini adalah bug lama yang terjebak di Xcode 11 sejak beta 1 ke versi saat ini . Versi aplikasi saat ini menggunakan tombol simulasi SimulatedButton
.Baik tombol simulasi SimulatedButton
dan tombol nyataCheckButton
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
sebuah variabel var line: Line
. Oleh karena itu, penggunaannya harus diterapkan $
pada CheckMarksView
variabel beralih userData.charts[self.chartIndex].lines[self.lineIndex(line: line)].isHidden
, yang disimpan dalam sebuah variabel global variabel @EnvironmentObject var userData
:
Kami telah terus terpakai dalam proyek saat ini CheckButton
pada kasus ini, jika Anda tiba-tiba Apple
akan memperbaiki kesalahan ini. Selain itu, Anda dapat mencoba menggunakan CheckButton
di CheckMarksView
sebaliknya SimulatedButton
dan pastikan bahwa itu tidak bekerja dalam kasus komposisi set, "satu set grafik ChartView
menggunakan List
di ListChartsView
.Karena kita View
berisi variabel @EnvironmentObject var userData: UserData
, untuk pratinjau Previews
, kita harus menetapkan nilai awalnya dengan .environmentObject(UserData())
:
Kombinasi berbagai macam Views
.
SwiftUI
- ini terutama kombinasi dari berbagai yang kecil Views
menjadi yang besar, dan yang besar Views
menjadi yang sangat besar, dll, seperti dalam permainan Lego
. Sebagai SwiftUI
ada berbagai cara kombinasi seperti Views
:- tumpukan vertikal
VStack
, - tumpukan horisontal
HStack
, - "Kedalaman" dari stack
ZStack
, - kelompok
Group
, ScrollView
,- daftar
List
, - membentuk
Form
, - wadah penunjuk
TabView
- dll.
Kami memulai kombinasi kami dengan yang paling sederhana GraphsViewForChart
, yang memberikan "set grafik" "faceless" GraphsForChart
AXIS Y dan indikator bergerak sepanjang sumbu X menggunakan tumpukan "dalam" ZStack
:
Kami menambahkan sebuah wadah ke wadah Previews
baru kami untuk menampilkannya dalam mode menggunakan modifier . Kami melanjutkan kombinasi dan melampirkan ke "set grafik" yang diperoleh di atas dengan AXIS Y dan indikator, AXIS X dalam bentuk "garis yang berjalan", serta kontrol: rentang waktu dari "peta mini" dan tampilan "Grafik" beralih . Hasilnya, kami mendapatkan yang dinyatakan di atas , yang menampilkan "set Grafik" dan memungkinkan Anda untuk mengontrol tampilannya pada sumbu waktu:GraphsViewForChart
NavigationView
Dark
.collorScheme(.dark)
RangeView
CheckMarksView
ChartView
Dalam hal ini, kami melakukan kombinasi menggunakan tumpukan vertikal VStack
:
Sekarang kami akan mempertimbangkan 3 opsi untuk menggabungkan set ChartView "Chart Sets" yang sudah diterima:- "Tabel yang dapat digulir"
List
, - tumpukan horizontal
HStack
dengan efek 3D, ZStack
"kartu" ditumpangkan
"Tabel yang dapat digulir"ListChartsView
disusun menggunakan daftar List
:
Tumpukan horizontal dengan efek 3D disusun menggunakan ScrollView
tumpukan horizontal HStack
dan daftar dalam bentuk ForEach
:
Dalam tampilan ini, semua cara interaksi pengguna bekerja sepenuhnya: bergerak di sepanjang garis waktu dan mengubah "skala" mini- map
, indikator dan tombol sembunyikan tombol "Grafik".ZStack
"kartu" ditumpangkan.
CardView
«»- « » X Y, : «mini — map» / .
CardView
ChartView
, «» , , ,
ZStack
« »
cardBackgroundColor
. , «» :

«»
VStack
,
ZStack
ForEach
:

«», «3D-c»
CardViewScalable
,
indexChat
dan mereka bergeser sedikit secara vertikal.Urutan “kartu berskala 3D” dapat diubah menggunakan urutan ( sequenced
) gerakan LongPressGesture
dan DragGesture
, yang hanya bertindak pada “kartu” paling atas dengan indexChat == 0
:
Anda dapat mengklik ( LongPress
) di “kartu” atas dengan “set Charts”, dan kemudian menariknya ( Drag
) ke bawah cukup jauh untuk melihat kartu berikutnya, dan jika Anda terus menyeretnya ke bawah, itu "pergi" ke tempat terakhir ZStack
, dan "kartu" berikutnya muncul ke depan:
Selain itu, untuk "kartu" atas yang dapat kita terapkan TapGesture
, yang akan bertindak bersama dengan gerakan LongPressGesture
dan DragGesture
:
Tap
isyarat akan menunjukkan modal yang "set grafis" ChartView
dengan e manajemen ementami RangeView
danCheckMarksView
:
Aplikasi TabView
untuk menggabungkan pada satu layar ketiga varian dari "set grafik" komposisi ChartView
.
Kami memiliki 3 penanda dengan gambar Image
dan teks Text
, tumpukan vertikal VStack
tidak diperlukan untuk presentasi bersama mereka.Mereka sesuai dengan 3 metode kami untuk menggabungkan "set Grafik" ChartViews
:- "Tabel yang dapat digulir"
ListChartViews
, - tumpukan horizontal dengan efek 3D
HStackChartViews
, - ZStack melapiskan "kartu"
OverlayCardsViews
.
Semua elemen interaksi pengguna: bergerak di sepanjang garis waktu dan mengubah "skala" dengan bantuan mini - map
, indikator, dan tombol untuk menyembunyikan "Grafik". sepenuhnya bekerja dalam semua 3 kasus.Kode ada di Github .SwiftUI
...
Anda harus berkenalan dengan tutorial video, buku, dan blog:Mang To , Mari Bangun Aplikasi Itu , serta deskripsi beberapa aplikasi SwiftUI ,- buku gratis "SwiftUI dengan contoh" dan video www.hackingwithswift.com/quick-start/swiftui- buku berbayar tetapi separuhnya dapat diunduh secara gratis www.bigmountainstudio.com/swiftui-views-book- kursus 100 hari dengan SwiftUI www.hackingwithswift.com/articles/201/start-the-100-days-of-swiftui , yang dimulai sekarang dan akan berakhir pada 31 Desember 2019,- hal-hal mengesankan di SwiftUI dilakukan di swiftui-lab.com- Majid blog ,- di pointFree.cowww.pointfree.co "marathon" dari posting tentang penggunaan Reducers di SwiftUI (super menarik)adalah aplikasi MovieSwiftUI yang hebat yang telah meminjam beberapa ide.