Untuk memastikan penindihan sejajar dengan rajah SwiftUI yang diskalakan agar muat, ukur rajah yang telah diskalakan itu sendiri dan letakkan penanda dalam ruang koordinat setempatnya. Lampirkan penindihan pengukuran sebelum sebarang bingkai luar yang menambah ruang kosong.
Tutorial ini membina aplikasi macOS kecil dengan rajah sintetik dan dua penanda bernombor. Tujuannya ialah memastikan setiap penanda kekal di tengah ciri rajahnya apabila bentuk tetingkap berubah. Pelaksanaan ini menggunakan koordinat ternormal, jadi kedudukan anotasi kekal bebas daripada dimensi tetingkap.
Mengapakah penindihan beralih daripada rajah yang diskalakan agar muat?
Penskalaan agar muat mengekalkan perkadaran rajah sambil memuatkannya dalam ruang yang tersedia. Bergantung pada perkadaran bekas, ruang kosong mungkin tertinggal di atas dan di bawah rajah, atau di sisi-sisinya. Apple menerangkan tingkah laku ini dalam Memuatkan imej ke dalam ruang yang tersedia.
Anjakan berlaku apabila karya seni dan penandanya menggunakan segi empat tepat yang berbeza. Contohnya, karya seni mungkin memenuhi segi empat tepat berpusat berukuran 600 kali 375 mata dalam bekas berukuran 600 kali 700 mata. Penanda yang diletakkan menggunakan pecahan ketinggian bekas mengambil kira ruang kosong di sekelilingnya.
Susunan pengubah menentukan segi empat tepat yang anda ukur. Apple mendokumenkan bahawa pengubah paparan membalut paparan yang menerimanya, dan bingkai meletakkan anak paparannya dalam segi empat tepat yang lain. Oleh itu, penindihan yang dilampirkan selepas bingkai yang mengembangkan saiz mengukur sempadan susun atur yang berbeza daripada penindihan yang dilampirkan terus pada karya seni yang telah diskalakan agar muat. Lihat dokumentasi View dan dokumentasi bingkai fleksibel Apple.
Untuk tugasan ini, gunakan susunan berikut:
- Lukis karya seni fleksibel.
- Gunakan nisbah aspek eksplisitnya dengan
.fit. - Lampirkan penindihan yang mengandungi
GeometryReader. - Letakkan penanda menggunakan saiz pembaca itu.
- Tambahkan bingkai luar dan ruang tepi di sekelilingnya.
Apple mengesyorkan penindihan apabila paparan yang diubah suai patut menguasai susun atur. Dengan itu, rajah menjadi pemilik susun atur yang sesuai, sementara anotasi dilapiskan di atasnya. Susunan sebaris dalam ZStack mempunyai tingkah laku susun atur yang berbeza dan memerlukan perhatian lebih teliti tentang paparan yang menentukan saiz. Lihat overlay(alignment:content:).
Bina contoh macOS yang lengkap
Cipta projek aplikasi macOS dalam Xcode menggunakan antara muka SwiftUI. Tetapkan sasaran penggunaan kepada macOS 12 atau lebih baharu, kerana API penindihan berasaskan penutupan yang digunakan di sini tersedia mulai macOS 12. Gantikan fail titik masuk aplikasi yang dijana dengan program lengkap berikut, dan pastikan hanya ada satu pengisytiharan aplikasi @main dalam sasaran. Ketersediaan API ini didokumenkan pada overlay(alignment:content:).
Karya seni sintetik ini mewakili ruang reka bentuk berukuran 800 kali 500, dengan nisbah aspek 1.6. Koordinat penanda ialah pecahan ruang itu: sifar mendatar ialah tepi kiri, sifar menegak ialah tepi atas, dan satu mewakili tepi bertentangan. Tiada fail aset atau pakej luaran diperlukan.
import SwiftUI
@main
struct DiagramAlignmentApp: App {
var body: some Scene {
WindowGroup {
DiagramExample()
.frame(minWidth: 360, minHeight: 260)
}
}
}
struct DiagramMarker: Identifiable {
let id: Int
let label: String
let x: CGFloat
let y: CGFloat
}
struct DiagramExample: View {
let markers = [
DiagramMarker(id: 1, label: "1", x: 0.25, y: 0.40),
DiagramMarker(id: 2, label: "2", x: 0.75, y: 0.60)
]
var body: some View {
Rectangle()
.fill(Color.white)
.overlay {
DiagramArtwork()
.stroke(Color.gray, lineWidth: 2)
}
.aspectRatio(800.0 / 500.0, contentMode: .fit)
.overlay {
GeometryReader { geometry in
ForEach(markers) { marker in
Circle()
.fill(Color.blue)
.frame(width: 28, height: 28)
.overlay {
Text(marker.label)
.font(.caption.bold())
.foregroundColor(.white)
}
.position(
x: marker.x * geometry.size.width,
y: marker.y * geometry.size.height
)
}
}
}
.border(Color.gray)
.frame(
maxWidth: .infinity,
maxHeight: .infinity,
alignment: .center
)
.padding(24)
.background(Color.gray.opacity(0.12))
}
}
struct DiagramArtwork: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.addRect(CGRect(
x: rect.minX + 0.10 * rect.width,
y: rect.minY + 0.20 * rect.height,
width: 0.30 * rect.width,
height: 0.40 * rect.height
))
path.addRect(CGRect(
x: rect.minX + 0.60 * rect.width,
y: rect.minY + 0.40 * rect.height,
width: 0.30 * rect.width,
height: 0.40 * rect.height
))
path.move(to: CGPoint(
x: rect.minX + 0.25 * rect.width,
y: rect.minY + 0.40 * rect.height
))
path.addLine(to: CGPoint(
x: rect.minX + 0.75 * rect.width,
y: rect.minY + 0.60 * rect.height
))
return path
}
}
Jalankan aplikasi dan ubah saiz tetingkap secara mendatar serta menegak. Penanda 1 sepatutnya kekal di tengah segi empat tepat pertama, dan penanda 2 di tengah segi empat tepat kedua. Sempadan nipis menunjukkan sempadan rajah yang diskalakan agar muat; kawasan kelabu di sekelilingnya ialah sebahagian daripada susun atur luar.
Mengapa susunan pengubah ini berfungsi
Segi empat tepat asas menyediakan karya seni fleksibel, dan .aspectRatio(800.0 / 500.0, contentMode: .fit) mengekalkan perkadarannya. Apple mentakrifkan argumen nisbah aspek sebagai lebar dibahagi tinggi dan menetapkan bahawa hasil penskalaan agar muat digunakan pada kandungan yang boleh diubah saiznya. Lihat aspectRatio(_:contentMode:).
Penindihan pengukuran datang serta-merta selepas kekangan itu. GeometryReader menentukan kandungannya berdasarkan saiz dan ruang koordinatnya sendiri, manakala geometry.size melaporkan saiz bekas. Dalam contoh ini, bekas itu meliputi segi empat tepat yang telah diskalakan agar muat. Oleh itu, mendarabkan koordinat ternormal dengan lebar dan tingginya menghasilkan kedudukan penanda setempat. Lihat GeometryReader dan GeometryProxy.size.
Bingkai pengembang terakhir memusatkan gubahan lengkap rajah dan penanda dalam kawasan yang tersedia. Oleh sebab penanda sudah dilampirkan pada segi empat tepat dalam, pengiraan margin luar tidak diperlukan.
Kekalkan kontrak lukisan dan kedudukan
DiagramArtwork mematuhi Shape, dan kaedah path(in:) mengembalikan Path yang dinyatakan dalam segi empat tepat yang dibekalkan. Kaedah itu tidak mengembalikan paparan atau melakukan perubahan susun atur. Ini selaras dengan kontrak path(in:) yang didokumenkan oleh Apple.
Kaedah .position(x:y:) bagi setiap penanda meletakkan pusatnya pada titik yang dikira. Kaedah itu tidak meletakkan penjuru kiri atasnya di situ. Contoh ini sengaja menjajarkan pusat setiap bulatan bernombor dengan satu ciri, selaras dengan takrif position(x:y:) oleh Apple.
Bulatan kekal selebar 28 mata apabila saiz tetingkap berubah, sementara pusatnya mengikuti karya seni. Ini ialah pilihan persembahan yang disengajakan supaya anotasi mudah dibaca. Jika dimensi penanda mewakili jarak dalam lukisan, hitung dimensi itu daripada saiz yang telah diskalakan agar muat juga.
Semak penjajaran melalui pengiraan koordinat
Semakan berangka membantu membezakan kesilapan koordinat daripada masalah gaya. Untuk penskalaan agar muat berpusat, anggap dimensi sumber positif ialah sourceWidth dan sourceHeight, manakala dimensi tersedia yang terhingga ialah containerWidth dan containerHeight. Berikut ialah pseudokod matematik, bukannya program Swift yang berasingan:
scale = min(containerWidth / sourceWidth,
containerHeight / sourceHeight)
fittedWidth = sourceWidth * scale
fittedHeight = sourceHeight * scale
originX = (containerWidth - fittedWidth) / 2
originY = (containerHeight - fittedHeight) / 2
containerX = originX + normalizedX * fittedWidth
containerY = originY + normalizedY * fittedHeight
Persamaan ini menerbitkan penskalaan seragam dan peletakan berpusat daripada tingkah laku penskalaan yang didokumenkan. Dengan sumber contoh berukuran 800 kali 500 dan penanda 1 pada (0.25, 0.40), hasilnya adalah seperti berikut. Dimensi bekas di sini merujuk kepada kawasan yang tersedia selepas ruang tepi di sekelilingnya diambil kira.
| Bekas tersedia | Rajah yang diskalakan agar muat | Asal rajah dalam bekas | Pusat penanda dalam bekas |
|---|---|---|---|
| 1000 × 700 | 1000 × 625 | (0, 37.5) | (250, 287.5) |
| 600 × 700 | 600 × 375 | (0, 162.5) | (150, 312.5) |
| 1000 × 400 | 640 × 400 | (180, 0) | (340, 160) |
Dalam baris tengah, mendarabkan ketinggian bekas penuh dengan 0.40 menghasilkan 280, iaitu tersasar daripada kedudukan menegak sebenar 312.5. Dalam penindihan setempat contoh ini, koordinat menegak hanyalah 0.40 × 375 = 150; susun atur yang mengelilinginya menyediakan margin 162.5 mata.
Aritmetik dalam jadual disemak menggunakan pengiraan Python kecil yang deterministik. Program SwiftUI telah diperiksa tetapi tidak dikompil dalam persekitaran ini; sahkan paparannya dalam Xcode pada Mac sasaran.
Sesuaikan corak ini dengan rajah sedia ada
Untuk rajah berasaskan imej, jadikan Image boleh diubah saiznya sebelum menggunakan pengubah penskalaan agar muat dan penindihan. Panduan pemuatan imej Apple menerangkan bahawa imej sebaliknya dipaparkan pada saiz asalnya, dan .resizable() sahaja boleh menskalakan paksi secara berasingan. Kekalkan nisbah aspek sebenar imej itu, kemudian lampirkan penindihan penanda sebelum bingkai luar. Lihat Memuatkan imej ke dalam ruang yang tersedia.
Jika anotasi bermula sebagai koordinat sumber, normalkan koordinatnya berdasarkan sempadan karya seni penuh yang sama: bahagikan koordinat mendatar sumber dengan lebar sumber dan koordinat menegak sumber dengan tinggi sumber. Margin lutsinar dalam imej kekal sebahagian daripada sempadan tersebut. Jika koordinat diukur berdasarkan kawasan yang dipangkas, tukarkannya kepada sistem koordinat imej penuh sebelum menggunakan corak ini.
Kekalkan pengubah khusus imej pada Image sebelum pengubah paparan umum membalutnya. Begitu juga, kekalkan var body: some View untuk paparan gubahan dan jenis pulangan Path untuk kaedah lukisan bentuk. Apple mendokumenkan gubahan paparan tersuai dan pembalutan pengubah dalam View.
Pengiraan ini mengandaikan penskalaan seragam tanpa pemangkasan tambahan, putaran atau transformasi sorot dan zum yang berasingan. Jika anda memperkenalkan operasi tersebut, gunakan transformasi yang sama pada karya seni dan anotasi, atau lanjutkan pemetaan koordinat dengan jelas. Beralih kepada .fill mengubah syarat penskalaan dan boleh memaparkan kandungan melepasi bingkai; Apple mendokumenkan pemotongan sebagai operasi berasingan dalam Memuatkan imej ke dalam ruang yang tersedia.
Diagnosis masalah penjajaran yang masih berlaku
Semak perkara ini mengikut turutan:
- Sempadan pengukuran: Kekalkan penindihan penanda sebelum bingkai luar yang mengembang. Sempadannya sepatutnya mengikuti karya seni, bukannya kawasan kosong di sekelilingnya.
- Had koordinat: Pastikan koordinat ternormal menggunakan segi empat tepat sumber penuh dan asal kiri atas yang dimaksudkan. Untuk contoh ini, nilai dalam sempadan yang dimaksudkan berjulat dari sifar hingga satu; tolak nilai yang tidak dijangka dan bukannya mengehadkannya secara senyap.
- Pilihan titik sauh: Ingat bahawa
.position(x:y:)menjadikan pusat penanda sebagai titik sauh. Label yang penjuruannya sepatutnya menyentuh satu ciri memerlukan ofset tambahan yang jelas atau strategi penambatan yang berbeza. - Susun atur kecil: Penanda boleh kekal berpusat dengan betul sambil bertindih dengan penanda lain. Tentukan peraturan berasingan untuk menyembunyikan atau menyusun semula label pada saiz kecil.
- Tingkah laku di tepi: Penanda yang berpusat pada tepi karya seni akan terjulur melepasi tepi itu. Tentukan sama ada lebihan itu boleh diterima sebelum menambah pemotongan.
Pastikan kedudukan diterbitkan daripada geometri semasa dan bukannya menyimpan saiz pertama yang diukur dalam .onAppear. Apple mentakrifkan panggilan semula itu berdasarkan kemunculan, dengan masa yang bergantung pada jenis paparan; ia bukan panggilan semula apabila saiz tetingkap berubah. Membaca geometri secara terus memastikan pengiraan kedudukan contoh ini terikat pada susun atur semasa. Lihat onAppear(perform:).
Untuk rajah yang diskalakan agar muat dalam tetingkap Mac yang boleh diubah saiznya, titik permulaan yang boleh dipercayai ialah satu segi empat tepat yang dikongsi: biarkan karya seni menentukan sempadan penskalaan yang muat, ukur sempadan tersebut dan terbitkan setiap pusat anotasi daripadanya. Tambahkan susun atur sekeliling hanya selepas hubungan itu ditetapkan.
Sumber
- aspectRatio(_:contentMode:) — Dokumentasi Pembangun Apple.
- overlay(alignment:content:) — Dokumentasi Pembangun Apple.
- GeometryReader — Dokumentasi Pembangun Apple.
- size — Dokumentasi Pembangun Apple.
- position(x:y:) — Dokumentasi Pembangun Apple.
- Memuatkan imej ke dalam ruang yang tersedia — Dokumentasi Pembangun Apple.
- frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) — Dokumentasi Pembangun Apple.
- path(in:) — Dokumentasi Pembangun Apple.
- View — Dokumentasi Pembangun Apple.
- onAppear(perform:) — Dokumentasi Pembangun Apple.