Untuk mendedahkan bar kemajuan gradien SwiftUI tanpa memampatkan warnanya, kekalkan lebar gradien sama dengan keseluruhan trek dan animasikan topeng yang dijajarkan ke tepi hadapan. Contoh macOS ini melaksanakan tingkah laku tersebut dalam ProgressViewStyle tersuai.
Tugasnya ialah membina penunjuk kemajuan desktop boleh ubah saiz untuk aktiviti sintetik. Apabila kemajuan mencapai 25%, suku pertama jujukan warna tetap patut kelihatan. Warna seterusnya patut muncul apabila kemajuan bertambah, sementara warna yang sedia kelihatan kekal pada kedudukannya.
Mengapakah warna termampat apabila bar kemajuan menjadi lebih pendek?
Apple mendokumenkan bahawa LinearGradient memetakan titik mula dan akhirnya ke dalam segi empat sempadan bentuk yang diisinya. Oleh itu, memberikan gradien lebar yang berkadar dengan kemajuan turut mengubah ruang tempat warnanya diagihkan. Gradien selebar suku trek masih mengandungi keseluruhan jujukan warnanya. LinearGradient — Dokumentasi Pembangun Apple
Untuk kesan pendedahan, asingkan dua dimensi: lebar gradien ialah lebar penuh trek, manakala lebar yang kelihatan ialah lebar trek didarab dengan pecahan yang telah selesai. Reka bentuk ini berdasarkan tingkah laku gradien yang didokumenkan, bukannya API bar kemajuan yang berasingan.
Sebagai contoh, pertimbangkan trek selebar 320 mata dengan hentian biru, ungu dan jingga pada kedudukan ternormal 0, 0.5 dan 1. Titik tengah ungu kekal pada 160 mata sepanjang pendedahan:
| Kemajuan | Lebar gradien penuh | Lebar topeng | Hasil yang kelihatan |
|---|---|---|---|
| 0% | 320 mata | 0 mata | Trek sahaja |
| 25% | 320 mata | 80 mata | Suku pertama gradien |
| 50% | 320 mata | 160 mata | Gradien hingga titik tengah ungunya |
| 100% | 320 mata | 320 mata | Keseluruhan jujukan warna |
Ini memberikan petunjuk diagnostik yang berguna: pada 25%, hentian jingga di hujung patut kekal tersembunyi. Jika ia sudah kelihatan di tepi yang bergerak, periksa sama ada lebar yang bergantung pada kemajuan dikenakan pada gradien.
Bina demo bar kemajuan gradien macOS yang lengkap
Gunakan sasaran app SwiftUI macOS dengan sasaran penggunaan macOS 12 atau lebih baharu. Overload mask(alignment:_:) yang mengambil kira penjajaran dan digunakan di bawah tersedia mulai macOS 12. Apple menerangkannya sebagai penggunaan saluran alfa paparan lain pada paparan bertopeng. mask(alignment:_:) — Dokumentasi Pembangun Apple
Gantikan fail titik masuk app yang dijana dengan atur cara lengkap ini. Fail paparan kandungan yang dijana boleh dibiarkan tanpa digunakan. Pastikan hanya ada satu pengisytiharan @main dalam sasaran, seperti yang dikehendaki oleh kontrak titik masuk app Apple. App — Dokumentasi Pembangun Apple
import SwiftUI
@main
struct GradientRevealDemoApp: App {
var body: some Scene {
WindowGroup {
GradientRevealDemo()
}
}
}
struct GradientRevealDemo: View {
@State private var progress = 0.25
@State private var isIndeterminate = false
var body: some View {
VStack(alignment: .leading, spacing: 20) {
ProgressView(
value: isIndeterminate ? nil : progress,
total: 1.0
) {
Text("Sample activity")
} currentValueLabel: {
if isIndeterminate {
Text("In progress")
} else {
Text("\(Int((progress * 100).rounded()))%")
}
}
.progressViewStyle(GradientRevealStyle())
Slider(value: $progress, in: 0...1, step: 0.01) {
Text("Sample completion")
}
.disabled(isIndeterminate)
HStack {
Button("Reset") { progress = 0 }
Button("25%") { progress = 0.25 }
Button("50%") { progress = 0.5 }
Button("Complete") { progress = 1 }
}
.disabled(isIndeterminate)
Toggle("Indeterminate", isOn: $isIndeterminate)
}
.padding(24)
.frame(minWidth: 360, idealWidth: 520, minHeight: 240)
}
}
@MainActor
struct GradientRevealStyle: ProgressViewStyle {
func makeBody(configuration: Configuration) -> some View {
let fraction = configuration.fractionCompleted.flatMap {
$0.isFinite ? min(max($0, 0), 1) : nil
}
VStack(alignment: .leading, spacing: 8) {
configuration.label
if let fraction = fraction {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.fill(Color.gray.opacity(0.2))
LinearGradient(
stops: [
.init(color: .blue, location: 0),
.init(color: .purple, location: 0.5),
.init(color: .orange, location: 1)
],
startPoint: .leading,
endPoint: .trailing
)
.frame(width: geometry.size.width, height: 14)
.mask(alignment: .leading) {
Rectangle()
.frame(
width: geometry.size.width
* CGFloat(fraction),
height: 14
)
.animation(
.easeInOut(duration: 0.25),
value: fraction
)
}
}
.frame(width: geometry.size.width, height: 14)
.clipShape(Capsule())
}
.frame(height: 14)
} else {
ProgressView()
.progressViewStyle(.circular)
}
configuration.currentValueLabel
}
}
}
Gaya tersuai ini melaksanakan makeBody(configuration:), titik sambungan yang didokumenkan untuk rupa paparan kemajuan. Ia memaparkan kedua-dua label yang dibekalkan dan membaca pecahan penyelesaian daripada konfigurasi. Mengekalkan penggayaan dalam protokol ini membolehkan antara muka ProgressView pemanggil dikekalkan. ProgressViewStyle — Dokumentasi Pembangun Apple
Contoh ini bermula pada 25%. Gunakan butang untuk titik semakan yang boleh diulang, seret peluncur untuk memeriksa nilai pertengahan, dan ubah saiz tetingkap untuk memeriksa reka letak. Kawalan ini mengubah keadaan setempat sintetik; ia tidak mewakili kerja yang diukur. Sumber telah diperiksa dan pengiraan lebar telah disemak dengan Python, tetapi atur cara SwiftUI ini tidak dikompil atau dijalankan dalam persekitaran penyelidikan.
Cara reka letak mengekalkan kestabilan gradien
GeometryReader mengukur ruang yang diperuntukkan kepada penunjuk. Apple menerangkannya sebagai bekas yang kandungannya bergantung pada saiz dan ruang koordinatnya sendiri, dengan saiz pilihan yang fleksibel. Menggunakan .frame(height: 14) di luarnya memberikan bekas yang selainnya fleksibel ini ketinggian yang jelas dalam tindanan menegak. GeometryReader — Dokumentasi Pembangun Apple
Di dalam bekas itu, gradien menerima keseluruhan lebar yang diukur. Hanya segi empat tepat di dalam topeng menerima lebar yang didarabkan. Apabila kemajuan berubah, bingkai gradien kekal tetap bagi saiz tetingkap tertentu.
Penjajaran .leading pada topeng adalah penting: ia meletakkan topeng yang lebih kecil pada tepi hadapan gradien. Parameter penjajaran bingkai meletakkan kandungan di dalam bingkai itu; ia tidak menggantikan penjajaran antara topeng dengan paparan yang ditopeng. Apple mendokumenkan kedua-duanya sebagai tanggungjawab penjajaran yang berasingan. mask(alignment:_:) — Dokumentasi Pembangun Apple, frame(width:height:alignment:) — Dokumentasi Pembangun Apple
Segi empat tepat legap itu mendedahkan gradien di tempat segi empat tepat tersebut berada. Warnanya tidak menentukan warna yang didedahkan kerana topeng menggunakan alfa. Menggantikannya dengan segi empat tepat lut sinar akan menyembunyikan gradien. Kekalkan trek kelabu di luar topeng supaya ia kekal kelihatan apabila kemajuan sifar.
Kapsul terakhir memangkas gabungan trek dan gradien yang didedahkan kepada siluet luar yang sama. Apple mendokumenkan pemangkasan sebagai pengekalan bahagian yang diliputi oleh bentuk pemangkas. Contoh ini sengaja mengekalkan tepi hadapan yang lurus sehingga sampai ke hujung kapsul yang membulat; topeng pendedahan berbentuk kapsul akan menghasilkan tepi hadapan yang membulat. clipShape(_:style:) — Dokumentasi Pembangun Apple
Mengubah saiz tetingkap mengubah lebar penuh trek, jadi gradien dikira semula merentasi lebar baharu itu. “Warna stabil” di sini bermaksud kedudukan yang stabil relatif kepada trek semasa kemajuan dikemas kini, bukannya koordinat skrin tetap semasa tetingkap diubah saiz.
Kekalkan nilai, jenis dan tingkah laku kitar hayat kemajuan
Gaya menerima fractionCompleted sebagai Double pilihan. Apple mentakrifkan julat berangkanya dari 0 hingga 1 dan menggunakan nil untuk kemajuan tidak tentu atau kemajuan relatif kepada selang tarikh. Menukar pecahan itu kepada CGFloat dalam pengiraan lebar menjelaskan sempadan angka tersebut. fractionCompleted — Dokumentasi Pembangun Apple
Contoh ini mengehadkan pecahan terhingga secara defensif dan mengarahkan pecahan yang tiada atau bukan terhingga kepada penunjuk tidak tentu standard. Pengendalian nilai bukan terhingga itu ialah dasar yang dinyatakan secara jelas dalam contoh ini. Cara ini mengelakkan pecahan yang tidak tersedia ditafsirkan sebagai kemajuan sifar. Penunjuk dalaman memilih .circular secara jelas supaya gaya terbina dalam digunakan.
Pada tempat panggilan, berikan jumlah selesai dan jumlah keseluruhan yang terhingga. Pemula berasaskan nilai menerima nil untuk kemajuan tidak tentu dan mendokumenkan bahawa nilai di luar julat dihadkan kepada sempadannya. Contoh ini menggunakan jumlah keseluruhan 1 dan mengehadkan peluncurnya kepada selang yang sama. init(value:total:label:currentValueLabel:) — Dokumentasi Pembangun Apple
Paparan induk memiliki keadaan kemajuan, manakala gaya memperoleh paparannya daripada konfigurasi. SwiftUI mengemas kini paparan yang bergantung apabila keadaan berubah. Oleh itu, contoh ini tidak memerlukan pemasa, panggilan balik kemunculan atau tugas latar untuk mengekalkan keadaan visualnya. State — Dokumentasi Pembangun Apple
Animasi digunakan pada segi empat tepat pendedahan. Pengubah suai yang skopnya ditentukan oleh nilai menggunakan animasi apabila pecahan yang dipantau berubah; tempoh suku saat yang dipilih ialah pilihan persembahan ilustratif. Ia tidak mengira kemajuan tugas. Buang pengubah suai itu jika kemas kini serta-merta lebih sesuai dengan antara muka anda. animation(_:value:) — Dokumentasi Pembangun Apple
Terdapat had yang didokumenkan untuk kemajuan berasaskan pemasa: Apple menyatakan bahawa paparan kemajuan relatif kepada tarikh tidak menyokong gaya tersuai. Untuk rupa gradien ini, gunakan pemula kemajuan berangka dan minta pemilik aktiviti membekalkan pecahan yang telah selesai. Jika anda memerlukan kemas kini automatik relatif kepada tarikh daripada Apple, gunakan pemula itu dengan persembahan yang disokongnya. init(timerInterval:countsDown:label:currentValueLabel:) — Dokumentasi Pembangun Apple
Semak pendedahan dengan urutan diagnostik ringkas
Lakukan semakan ini selepas membina sasaran macOS. Semakan ini membezakan masalah pemetaan warna daripada masalah penjajaran dan saiz:
- Pilih
Reset. Trek kelabu patut kekal tanpa gradien yang kelihatan. - Pilih
25%. Hanya warna awal gradien patut muncul. Hujung jingga yang kelihatan menunjukkan bahawa gradien itu sendiri mungkin dipendekkan. - Pilih
50%. Dengan hentian yang dibekalkan, sempadan pendedahan patut sampai ke titik tengah ungu. - Pilih
Complete. Gradien patut memenuhi trek hingga ke warna jingga. - Ubah saiz tetingkap pada 50%. Bahagian yang kelihatan patut kekal separuh daripada trek yang saiznya baharu.
- Aktifkan
Indeterminate. Isian berangka patut digantikan dengan penunjuk terbina dalam, sementara label tugas dan nilai semasa kekal dipaparkan.
Jika pendedahan berkembang keluar dari tengah, periksa .mask(alignment: .leading). Jika penunjuk menggunakan ruang menegak yang berlebihan, periksa bingkai di luar GeometryReader. Jika setiap hentian warna bergerak apabila kemajuan berubah, pastikan hanya topeng menggunakan pengganda kemajuan.
Peraturan yang boleh digunakan semula adalah mudah: tetapkan saiz gradien untuk keseluruhan trek, kemudian gunakan topeng berasingan yang saiznya ditentukan untuk mendedahkan kemajuan. Mengasingkan tanggungjawab ini memudahkan penaakulan tentang penyelesaian separa, perubahan saiz dan animasi, sambil mengekalkan konfigurasi paparan kemajuan yang didokumenkan.
Sumber
- LinearGradient — Apple
- mask(alignment:_:) — Apple
- App — Apple
- ProgressViewStyle — Apple
- GeometryReader — Apple
- frame(width:height:alignment:) — Apple
- clipShape(_:style:) — Apple
- fractionCompleted — Apple
- init(value:total:label:currentValueLabel:) — Apple
- State — Apple
- animation(_:value:) — Apple
- init(timerInterval:countsDown:label:currentValueLabel:) — Apple