Untuk membetulkan kandungan SwiftUI yang teranjak keluar skrin semasa tetingkap Mac diubah saiz, kenal pasti dahulu sama ada lukisan bergerak melepasi sempadan susun aturnya, kandungan enggan mengecil, atau paparan AppKit terbenam berhenti menjejaki bekasnya. Kemudian baiki sempadan yang berkenaan.
Panduan ini ditujukan kepada pembangun yang membetulkan antara muka boleh ubah saiz dalam app Mac mereka sendiri: memastikan label mudah dibaca, kawalan boleh dicapai dan kandungan yang lebih panjang boleh ditatal. Urutan diagnostik di bawah menggunakan label sintetik dan membezakan susun atur SwiftUI, saiz tetingkap dan pembalutan teks AppKit.
Kenal pasti sempadan yang gagal semasa saiz diubah
Susun atur SwiftUI bermula dengan cadangan saiz daripada induk. Anak memilih saiznya sebagai tindak balas; cadangan itu bukan arahan untuk memangkas semuanya kepada segi empat tepat tersebut. Apple mendokumenkan cadangan saiz minimum, maksimum dan ideal dalam ProposedViewSize. Ini menjelaskan sebab mengecilkan tetingkap tidak semestinya mengecilkan setiap paparan turunannya.
Mulakan dengan ujian yang boleh diulang: buka tetingkap app anda, kecilkannya sehingga kegagalan pertama muncul, kemudian besarkannya semula. Perhatikan elemen pertama yang terjejas, bukan susunan akhir. Adakah label melepasi bekasnya? Adakah seluruh panel beralih? Adakah teks mengekalkan lebar pembalutan sebelumnya?
Buat sementara waktu, tambahkan .border(Color.orange) pada anak yang disyaki dan .border(Color.blue) pada bingkai atau bekas yang mengelilinginya. Letakkan pengubah suai pada aras yang berbeza supaya anda boleh membandingkan segi empat tepatnya. Dokumentasi offset Apple menunjukkan teknik sempadan ini untuk membezakan kedudukan susun atur asal daripada lukisan yang dialihkan.
| Perkara yang diperhatikan | Periksa dahulu | Tindakan seterusnya yang berguna |
|---|---|---|
| Lukisan bergerak ke luar segi empat tepat yang sebaliknya stabil | offset, position, pengiraan koordinat |
Alih keluar pengubah suai kedudukan satu demi satu |
| Label kekal lebar apabila bekasnya mengecil | fixedSize mendatar, lebar eksplisit |
Benarkan label menerima lebar terkekang |
| Kawalan tidak muat dalam baris mendatar | Jumlah lebar kawalan dan jarak | Sediakan susunan menegak sebagai pilihan |
| Kandungan bawah hilang apabila ketinggian berkurang | Tingkah laku limpahan tiada | Jadikan rantau kandungan boleh ditatal |
| Panel SwiftUI yang dihoskan beralih dalam AppKit | Sempadan paparan hos dan kekangan saiz | Bandingkan saiz hos dengan keperluan kandungan |
| Paparan teks AppKit mengecil tetapi baris kekal lebar | Penjejakan lebar bekas teks | Periksa konfigurasi paparan teks dan bekas teks |
Ini ialah hipotesis diagnostik berdasarkan tingkah laku yang didokumenkan, bukannya jaminan bahawa satu simptom tertentu mempunyai satu punca. Ubah satu perkara yang disyaki pada satu masa dan ulang gerakan mengubah saiz yang sama. Jika menggantikan anak yang kompleks dengan Text("Sample panel") menghapuskan kegagalan, pulihkan lapisan anak itu secara beransur-ansur.
Alih keluar peraturan kedudukan dan saiz yang menentang perubahan saiz tetingkap
Periksa offset dan kedudukan berasaskan pusat
offset mengalihkan kandungan yang dipaparkan tanpa mengubah dimensi asal paparan. Oleh itu, offset mendatar positif boleh menolak lukisan melepasi tepi kanan walaupun induk masih memperuntukkan ruang asal. Alih keluar offset buat sementara waktu; jika lukisan kembali ke dalam bekas, gantikan pelarasan itu dengan susunan yang dinyatakan melalui tindanan, padding atau penjajaran bingkai. Lihat tingkah laku offset Apple.
position(x:y:) meletakkan pusat paparan pada koordinat dalam ruang koordinat induknya. Menganggap koordinat itu sebagai penjuru kiri atas akan menghasilkan ralat yang berkaitan dengan saiz anak. Kedudukan yang dikira sekali juga mungkin tidak lagi sesuai apabila saiz induknya berubah. Untuk panel label dan kawalan biasa, utamakan penjajaran yang menyatakan hubungan yang dikehendaki dengan bekas. Apple mentakrifkan tingkah laku berasaskan pusat dalam position(x:y:).
Contohnya, .frame(maxWidth: .infinity, alignment: .leading) menyatakan penjajaran leading dalam bingkai fleksibel. Ia tidak menghapuskan keperluan saiz anak turunannya yang terlalu besar. Dokumentasi bingkai fleksibel Apple menerangkan cara bingkai itu mengehadkan cadangan dan meletakkan anaknya.
Periksa lebar tetap dan saiz ideal teks
Periksa tetapan lebar eksplisit dan penggunaan mendatar fixedSize pada cabang yang gagal. Apple secara khusus mendokumenkan bahawa .fixedSize(horizontal: true, vertical: false) menjadikan teks mengekalkan lebar ideal yang diperlukan untuk keseluruhan rentetan, dan ini berpotensi melebihi sempadan induknya. Untuk teks isi yang perlu membalut baris, buang keperluan mendatar itu. Jika mengekalkan ketinggian penuh teks yang telah membalut baris adalah sesuai, pertimbangkan .fixedSize(horizontal: false, vertical: true). Lihat fixedSize(horizontal:vertical:).
Mengekalkan saiz menegak memerlukan ruang untuk ketinggian itu. Perenggan yang membesar secara menegak dalam bekas pendek tanpa tatal masih boleh menjadi tidak dapat dicapai. Gunakan rantau tatal menegak apabila tugasan memerlukan akses kepada semua teks; Apple mendokumenkan paksi tatal yang disokong dalam ScrollView.
Semak semula pengiraan berasaskan geometri
GeometryReader mentakrifkan kandungan menggunakan saiz dan ruang koordinatnya sendiri, serta mengembalikan saiz pilihan yang fleksibel kepada induknya. Menambahnya di sekeliling paparan mengubah struktur susun atur, jadi ia bukan pembalut pengukuran yang neutral. Jika anjakan bermula selepas pengiraan geometri diperkenalkan, gantikan cabang itu buat sementara waktu dengan tindanan dan bingkai biasa. Apple menerangkan sifat ini dalam GeometryReader.
Untuk susun atur berkadar yang benar-benar memerlukan geometri, terbitkan dimensi daripada bekas semasa yang berkaitan. Periksa sebarang keadaan yang menyimpan lebar yang ditangkap semasa paparan mula muncul. Periksa juga sama ada dimensi yang diukur digunakan untuk mengubah saiz rantau yang sama sedang diukur; membuang kebergantungan itu ialah ujian pengasingan yang berguna.
Bina susun atur padat dengan tingkah laku limpahan yang jelas
Contoh lengkap berikut menunjukkan tiga pilihan berasingan: teks membalut baris, susunan kawalan berubah apabila diperlukan, dan limpahan menegak kekal boleh dicapai. Dimensinya ialah input reka bentuk ilustratif, bukannya nilai yang ditetapkan Apple.
Cipta app SwiftUI macOS dalam Xcode, tetapkan sasaran penggunaan kepada macOS 13 atau lebih baharu, dan gantikan pengisytiharan app serta paparan kandungan yang dijana dengan satu fail ini. Kekalkan hanya satu pengisytiharan @main. Contoh ini menggunakan API ViewThatFits dan kebolehubahan saiz tetingkap yang tersedia pada macOS 13. Apple mendokumenkan tingkah lakunya dalam ViewThatFits dan windowResizability(_:).
import SwiftUI
@main
struct ResizeExampleApp: App {
var body: some Scene {
WindowGroup {
ResizeExampleView()
.frame(minWidth: 280, minHeight: 220)
}
.windowResizability(.contentMinSize)
}
}
struct ResizeExampleView: View {
@State private var selection = "Sample A"
private let details = Array(
repeating: "Sample notes wrap as the panel becomes narrower.",
count: 12
).joined(separator: " ")
var body: some View {
ScrollView(.vertical) {
VStack(alignment: .leading, spacing: 16) {
Text("Sample panel")
.font(.title2)
Text(details)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
ViewThatFits(in: .horizontal) {
HStack(spacing: 12) {
Button("Choose Sample A") {
selection = "Sample A"
}
Button("Choose Sample B") {
selection = "Sample B"
}
}
.fixedSize(horizontal: true, vertical: false)
VStack(alignment: .leading, spacing: 12) {
Button("Choose Sample A") {
selection = "Sample A"
}
Button("Choose Sample B") {
selection = "Sample B"
}
}
}
Text("Selected: \(selection)")
}
.padding(20)
.frame(
minWidth: 0,
maxWidth: .infinity,
alignment: .leading
)
}
}
}
ViewThatFits menilai alternatif mengikut susunan yang diisytiharkan dan memilih yang pertama dengan saiz ideal yang muat pada paksi terkekang. Di sini, pemadanan mendatar memilih antara baris butang dengan tindanan menegak. Baris itu sengaja mengekalkan saiz ideal mendatarnya supaya baris yang dimampatkan tidak menjadi hasil pilihan. Pastikan kedua-dua alternatif menawarkan tindakan yang setara. Lihat peraturan pemilihan Apple.
Bingkai luar membolehkan lajur kandungan mengikut lebar yang tersedia, manakala paparan tatal menegak menyediakan akses apabila pembalutan menambah ketinggian. Susunan ini mengandaikan setiap kawalan muat pada lebar minimum yang dipilih. Label yang lebih panjang atau fon yang lebih besar mungkin memerlukan minimum lain atau susunan padat yang berbeza.
Contoh ini diperiksa untuk memastikan pengisytiharan konsisten dan API tersedia; ia tidak dikompil di sini kerana pengkompil Swift tidak tersedia. Jalankannya pada macOS untuk mengesahkan tingkah laku tetingkap dan teks sebenar.
Tetapkan had tetingkap selepas membetulkan susun atur kandungan
Saiz tetingkap minimum yang sesuai menghalang antara muka daripada menjadi lebih kecil daripada susunan yang disokongnya. Dengan .contentMinSize, SwiftUI memperoleh saiz minimum tetingkap daripada kandungannya dan tidak mengenakan saiz maksimum. Dalam contoh itu, bingkai akar menyediakan input lebar dan ketinggian minimum. Lihat contentMinSize.
Pilih saiz minimum itu dengan menguji susunan padat anda menggunakan label paling panjang yang disokong. Menaikkan saiz minimum sehingga paparan yang tersalah letak kelihatan boleh menyembunyikan ralat susun atur asal. Buang dahulu anjakan yang tidak disengajakan dan anak yang terlalu besar; kemudian tetapkan dimensi terkecil yang membolehkan tugasan yang dimaksudkan kekal mudah digunakan.
Jika AppKit mengurus tetingkap dan mengehos SwiftUI, periksa sempadan pengehosan secara berasingan. Pada macOS 13 dan lebih baharu, NSHostingView.sizingOptions mengawal kekangan yang diperoleh daripada kandungan SwiftUI. Apabila hos ialah contentView tetingkap, ia turut mengemas kini saiz kandungan minimum dan maksimum tetingkap. Apple juga mendokumenkan bahawa kandungan dipusatkan apabila bingkai hos berbeza daripada saiz yang diperlukan oleh kandungan SwiftUI-nya. Periksa hubungan ini sebelum menambah offset pampasan. Lihat NSHostingView sizingOptions.
Periksa AppKit dan TextKit hanya apabila paparan yang gagal menggunakannya
Biarkan SwiftUI menentukan saiz paparan akar yang diwakilkan
Untuk NSViewRepresentable, SwiftUI mengawal frame dan bounds paparan AppKit yang diwakilkan. Apple secara jelas memberi amaran bahawa menetapkan sifat tersebut sendiri bercanggah dengan SwiftUI dan menghasilkan tingkah laku yang tidak ditakrifkan. Jika pengendali perubahan saiz anda berulang kali menetapkan bingkai akar yang diwakilkan, buang pemilikan yang bersaing itu dan nyatakan saiz melalui sempadan SwiftUI. Lihat NSViewRepresentable.
Apabila pengukuran tersuai diperlukan, macOS 13 dan lebih baharu menyediakan sizeThatFits(_:nsView:context:). Ia boleh menerima beberapa cadangan dalam satu laluan susun atur; pemulangan nil meminta saiz lalai. Jadikan hasilnya bergantung pada cadangan semasa dan kandungan, bukannya lebar tetingkap sebelumnya. Dimensi cadangan yang tidak ditentukan memerlukan dasar pengukuran yang disengajakan, bukannya dianggap secara automatik sebagai sifar. Lihat saiz representable.
Pengikatan pilihan Swift membolehkan anda menyemak dan mengambil dimensi yang dicadangkan sebelum menggunakannya. Ini berguna apabila melaksanakan dasar saiz tanpa membuka pilihan lebar atau ketinggian secara paksa. Tingkah laku bahasa ini didokumenkan dalam Asas: Pengikatan Pilihan.
Asingkan lebar pembalutan teks daripada lebar paparan teks
Jika anak yang gagal ialah NSTextView, bandingkan lebar semasanya dengan lebar bekas teksnya. Apple mendokumenkan bahawa NSTextContainer.widthTracksTextView lalainya ialah false; menetapkannya kepada true menjadikan bekas itu melaraskan lebarnya apabila lebar paparan teks berubah. Inilah kawalan yang berkaitan apabila baris sepatutnya membalut semula mengikut paparan teks. Lihat widthTracksTextView.
Periksa juga isHorizontallyResizable. Ia mengawal sama ada paparan teks mengubah lebarnya supaya muat dengan teks, iaitu keperluan yang berbeza daripada membalut teks dalam lebar yang ditetapkan. Untuk rantau teks boleh balut yang menatal secara menegak, konfigurasikan kedua-dua dasar itu secara konsisten dan pastikan susun atur bekas benar-benar mengubah saiz paparan teks. Lihat isHorizontallyResizable.
Kekalkan enjin teks yang digunakan oleh pelaksanaan anda. Apple mendokumenkan bahawa mengakses layoutManager pada NSTextView atau bekas teksnya boleh menukarnya kepada mod keserasian yang menggunakan NSLayoutManager. Jangan tambah akses itu secara sambil lewa semasa mendiagnosis paparan TextKit 2. Gunakan API yang sesuai untuk enjin sedia ada dan sasaran penggunaan anda. Lihat NSTextView.
Sahkan pembaikan semasa perubahan lebar dan ketinggian
Gunakan urutan akhir ini dalam app anda sendiri:
- Kecilkan tetingkap perlahan-lahan melalui titik asal kegagalan, kemudian besarkannya semula.
- Kurangkan ketinggian secara berasingan dan pastikan semua kandungan yang diperlukan kekal boleh dicapai.
- Ulangi dengan label sintetik yang panjang dan teks berbilang baris.
- Aktifkan setiap kawalan dalam susunan lebar dan padat.
- Untuk paparan teks AppKit, pastikan pembalutan mengikut lebar semasa dalam kedua-dua arah perubahan saiz.
Pembaikan yang berjaya memastikan kedudukan berpandukan bekas semasa, memberikan susunan padat yang boleh digunakan kepada kandungan, dan menyediakan akses kepada limpahan. Setelah tingkah laku ini kekal semasa perubahan saiz berulang, buang sempadan sementara dan kekalkan saiz tetingkap minimum yang diperlukan oleh antara muka.
Sumber
- ProposedViewSize — Apple
- offset(x:y:) — Apple
- position(x:y:) — Apple
- frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) — Apple
- fixedSize(horizontal:vertical:) — Apple
- ScrollView — Apple
- GeometryReader — Apple
- ViewThatFits — Apple
- windowResizability(_:) — Apple
- contentMinSize — Apple
- sizingOptions — Apple
- NSViewRepresentable — Apple
- sizeThatFits(_:nsView:context:) — Apple
- Asas — Swift.org
- widthTracksTextView — Apple
- isHorizontallyResizable — Apple
- NSTextView — Apple