Untuk memadankan kawalan sisi dengan pratonton SwiftUI nisbah aspek, kira saiz pratonton yang muat berdasarkan ruang tetingkap yang tersedia, kemudian tetapkan ketinggian yang sama kepada kawalan. Contoh macOS ini mengekalkan perkadaran pratonton sintetik dan membolehkan kawalan yang melimpah digulir.

Tugas ini melibatkan editor desktop boleh ubah saiz dengan pratonton di sebelah kiri dan panel kawalan lebar tetap di sebelah kanan. Di sini, “pratonton” bermaksud kandungan di dalam aplikasi yang sedang berjalan. Matlamatnya ialah menjajarkan tepi atas dan bawah susun atur kedua-dua panel apabila saiz tetingkap berubah.

Ketinggian manakah yang patut diikuti oleh kawalan?

Mulakan dengan mentakrifkan segi empat tepat yang ingin dipadankan. Pratonton yang dilaraskan mengikut nisbah aspek boleh mengambil ruang yang lebih kecil daripada bekas di sekelilingnya. Jika kawalan mengikut ketinggian bekas, kawalan mungkin memanjang melepasi pratonton yang kelihatan walaupun kedua-dua paparan disusun dengan betul.

Apple mentakrifkan nisbah aspek sebagai lebar dibahagi tinggi. Untuk kandungan boleh ubah saiz, aspectRatio(_:contentMode:) dengan .fit mengekalkan nisbah itu sambil memuatkan kandungan dalam ruang yang dicadangkan. Contoh elips dalam dokumentasi menunjukkan sebab bentuk yang telah dilaraskan dan bingkai segi empat sama yang mengelilinginya boleh mempunyai dimensi berbeza. Dokumentasi nisbah aspek Apple.

Pelaksanaan ini menjadikan segi empat tepat terlaras itu eksplisit. Ia memperuntukkan lebar tetap untuk kawalan serta jarak antara panel, kemudian memuatkan pratonton dalam baki lebar dan ketinggian yang tersedia. Kedua-dua panel menerima ketinggian pratonton yang dikira.

Itulah dasar susun atur untuk contoh ini: pratonton menentukan ketinggian ruang paparan kawalan. Senarai kawalan yang panjang digulir di dalam ruang paparan itu dan bukannya menjadikan pratonton lebih tinggi. Apple mendokumentasikan ScrollView sebagai paparan yang menunjukkan bahagian kandungan asas yang boleh dialihkan dalam kawasan boleh gulir. Dokumentasi paparan gulir Apple.

Kira segi empat tepat terlaras sekali sahaja

Anggap W dan H sebagai dimensi kandungan yang tersedia selepas padding luar, C sebagai lebar kawalan, G sebagai jarak, dan R sebagai nisbah lebar kepada tinggi pratonton. Pengiraannya ialah:

remainingWidth = max(0, W - C - G)
previewWidth = min(remainingWidth, H * R)
previewHeight = previewWidth / R
controlsHeight = previewHeight

Had pertama menghalang pratonton daripada menggunakan ruang mendatar kawalan. Had kedua menghalang tetingkap yang lebar daripada menghasilkan pratonton yang lebih tinggi daripada kawasan yang tersedia. Mengira ketinggian daripada lebar yang dipilih mengekalkan nisbah dalam kedua-dua keadaan.

Sebagai contoh, katakan kawasan kandungan sebelum padding berukuran 960 kali 600 mata. Dengan padding 16 mata pada setiap sisi, panel kawalan 220 mata, jarak 16 mata dan pratonton 16:9, kawasan yang tersedia berukuran 928 kali 568 mata. Baki lebar pratonton ialah 692 mata, menghasilkan ketinggian pratonton 389.25 mata. Maka ruang paparan kawalan menjadi 220 kali 389.25 mata.

Nilai ini ialah input ilustrasi, bukan hasil pengukuran aplikasi. Nilai ini juga menunjukkan sebab memadankan kawalan dengan keseluruhan ketinggian tersedia 568 mata akan menghasilkan penjajaran yang salah.

Gunakan CGFloat untuk dimensi susun atur dan nisbah yang positif serta terhingga. Dalam Swift, let ratio: CGFloat = 16.0 / 9.0 menjadikan pengiraan titik terapung yang dimaksudkan lebih jelas. Jika aplikasi anda kelak menerima nisbah yang boleh dikonfigurasi, sahkannya sebelum pembahagian.

Bina contoh macOS yang lengkap

Cipta aplikasi macOS SwiftUI dalam Xcode dan tetapkan sasaran penggunaan kepada macOS 13 atau lebih baharu. Gantikan deklarasi aplikasi dan paparan kandungan yang dijana dengan fail tunggal ini, dengan memastikan hanya terdapat satu deklarasi @main dalam sasaran. Contoh ini tidak memerlukan aset atau pakej luaran.

Keperluan penggunaan itu berpunca daripada API penentuan saiz tetingkap yang digunakan di sini. Adegan ini menggunakan .contentMinSize, yang menurut dokumentasi Apple menentukan saiz minimum tetingkap berdasarkan kandungannya tanpa mengenakan saiz maksimum. Dokumentasi kebolehubahan saiz tetingkap Apple, Dokumentasi penentuan saiz minimum kandungan Apple.

import SwiftUI

@main
struct MatchedPreviewApp: App {
    var body: some Scene {
        WindowGroup {
            PreviewEditor()
                .frame(minWidth: 640, minHeight: 360)
        }
        .windowResizability(.contentMinSize)
    }
}

struct PreviewEditor: View {
    @State private var intensity: Double = 0.5
    @State private var showsLabel = true

    private let ratio: CGFloat = 16.0 / 9.0
    private let controlsWidth: CGFloat = 220
    private let gap: CGFloat = 16

    var body: some View {
        GeometryReader { geometry in
            let availableHeight = max(0, geometry.size.height)
            let remainingWidth = max(
                0,
                geometry.size.width - controlsWidth - gap
            )
            let previewWidth = min(
                remainingWidth,
                availableHeight * ratio
            )
            let previewHeight = previewWidth / ratio

            HStack(alignment: .top, spacing: gap) {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.blue.opacity(0.2 + intensity * 0.7))
                    .aspectRatio(ratio, contentMode: .fit)
                    .overlay {
                        Text("Sample composition")
                            .font(.headline)
                            .opacity(showsLabel ? 1 : 0)
                    }
                    .frame(
                        width: previewWidth,
                        height: previewHeight
                    )

                ScrollView(.vertical) {
                    VStack(alignment: .leading, spacing: 16) {
                        Text("Preview controls")
                            .font(.headline)

                        Toggle("Show label", isOn: $showsLabel)

                        VStack(alignment: .leading, spacing: 8) {
                            Text("Color intensity")
                            Slider(value: $intensity, in: 0...1)
                        }

                        ForEach(1...8, id: \.self) { index in
                            Text("Sample setting \(index)")
                                .font(.caption)
                        }
                    }
                    .padding(16)
                    .frame(maxWidth: .infinity, alignment: .leading)
                }
                .frame(
                    width: controlsWidth,
                    height: previewHeight
                )
                .background(Color.gray.opacity(0.08))
            }
            .frame(
                width: geometry.size.width,
                height: geometry.size.height,
                alignment: .center
            )
        }
        .padding(16)
    }
}

GeometryReader memberikan saiz kawasan yang diperuntukkan kepada editor. Apple menerangkannya sebagai pembentuk kandungan berdasarkan saiz dan ruang koordinatnya sendiri, dengan saiz pilihan yang fleksibel. Meletakkan padding di luar pembaca bermakna pengiraan menggunakan kawasan kandungan selepas inset. Dokumentasi pembaca geometri Apple.

Dua bingkai tetap itu menetapkan ketinggian yang sama. Apple mendokumentasikan frame(width:height:alignment:) sebagai pencipta bingkai tidak kelihatan dengan dimensi yang ditentukan. Latar belakang kawalan diletakkan selepas bingkai itu, jadi ia meliputi ruang paparan kawalan, termasuk ruang di bawah senarai yang pendek. Dokumentasi bingkai tetap Apple.

Bingkai terakhir memusatkan seluruh baris dalam pembaca. Bingkai itu tidak membesarkan mana-mana panel. Oleh itu, tetingkap yang tinggi boleh mempunyai ruang tidak terpakai di atas dan di bawah baris, sementara kawalan masih sepadan dengan pratonton.

Kekalkan tingkah laku keadaan dan kitar hayat semasa perubahan saiz

Asingkan nilai yang boleh diedit pengguna daripada geometri yang dikira. Dalam contoh ini, intensity dan showsLabel ialah keadaan; lebar dan ketinggian pratonton ialah nilai setempat yang diterbitkan daripada saiz tersedia semasa.

Apple mendokumentasikan keadaan sebagai storan yang diurus SwiftUI, yang perubahannya mengemas kini bahagian hierarki paparan yang bergantung padanya. Ini menyokong penyimpanan nilai peluncur dan togol dalam editor yang memilikinya. Dokumentasi keadaan Apple.

Pengiraan susun atur ini tidak mengandungi penetapan keadaan, panggil balik kemunculan, kerja tak segerak atau maklum balas pengukuran. Perubahan saiz mengubah dimensi yang digunakan oleh struktur paparan yang sama. Label kekal dipaparkan dan kelegapannya berubah apabila togol berubah.

Apabila menyesuaikan pendekatan ini kepada editor sedia ada, kekalkan pemilikan model dan jenis ikatannya. Gunakan dasar penentuan saiz di sekeliling pratonton dan kawalan; jangan pindahkan penciptaan model ke dalam pengiraan geometri. Aritmetik contoh ini hanya memerlukan dimensi tersedia dan nisbah yang dipilih.

Periksa perubahan saiz, limpahan dan tertib pengubah

Gunakan dimensi berikut sebagai semakan pengiraan. Dimensi ini menerangkan kawasan kandungan sebelum padding luar contoh, bukan bingkai luar tetingkap. Nilai jangkaan dibundarkan kepada dua tempat perpuluhan.

Kawasan kandungan Saiz pratonton Ruang paparan kawalan
960 × 600 mata 692 × 389.25 220 × 389.25
640 × 360 mata 372 × 209.25 220 × 209.25
1200 × 400 mata 654.22 × 368 220 × 368
700 × 900 mata 432 × 243 220 × 243

Aritmetik ini disemak dengan pengiraan Python ringkas, termasuk pernyataan bahawa pratonton terlaras kekal dalam ketinggian tersedia dan kedua-dua panel berserta jaraknya muat secara mendatar. Swift dan Xcode tidak tersedia dalam persekitaran penyelidikan, jadi program SwiftUI lengkap telah diperiksa tetapi tidak dikompil atau dijalankan.

Dalam Xcode, jalankan aplikasi dan ubah saiznya melalui bentuk yang lebar, pendek dan tinggi. Pastikan tepi panel kekal sejajar, kawalan boleh digulir apabila kandungannya melebihi ruang paparan, dan peluncur serta togol mengekalkan nilainya semasa perubahan saiz.

Jika penjajaran masih berbeza selepas anda menyesuaikan contoh ini, periksa keadaan khusus berikut:

  • Padding panel tambahan: Padding yang digunakan di luar bingkai tetap pada satu panel sahaja menambah saiz keseluruhan panel itu. Kekalkan padding kawalan di dalam paparan gulir seperti yang ditunjukkan.
  • Pembalut pratonton yang lebih besar: Jika anda menambah bingkai yang lebih besar di sekeliling pratonton, tentukan sama ada kawalan patut sepadan dengan pembalut itu atau kandungan yang telah dilaraskan. Pengiraan di sini memadankan segi empat tepat terlaras.
  • Kandungan tersuai yang melimpah: Bingkai menetapkan dimensi susun atur tetapi tidak menyembunyikan lukisan yang berada di luarnya secara automatik. Apple mendokumentasikan .clipped() untuk pemotongan segi empat tepat; gunakan apabila limpahan pratonton yang disengajakan perlu disembunyikan. Pastikan kawalan boleh digulir supaya semua tetapan boleh dicapai. Dokumentasi pemotongan Apple.
  • Ruang hos tidak mencukupi: Contoh ini bergantung pada saiz kandungan minimumnya. Jika dibenamkan dalam hos yang lebih kecil, lebar kawalan tetapnya mungkin tidak meninggalkan lebar pratonton yang berguna. Pilih panel yang lebih kecil, susun atur menegak atau saiz minimum yang jelas untuk hos itu.

Bilakah anda patut menggunakan susun atur tersuai?

Pendekatan ini sesuai untuk editor yang menerima kawasan tetingkap terhad dan menggunakan nisbah aspek yang diketahui. Dasar penentuan saiznya jelas dan tidak bergantung pada pengukuran kandungan dalaman pratonton.

Jika komponen boleh guna semula anda perlu menentukan saiz pilihan daripada anak paparannya, pertimbangkan Layout tersuai. Kontrak yang didokumentasikan Apple memisahkan pelaporan saiz komposit melalui sizeThatFits(proposal:subviews:cache:) daripada penentuan kedudukan anak melalui placeSubviews(in:proposal:subviews:cache:). Proksi subpaparan menyediakan operasi pengukuran dan penempatan. Dokumentasi susun atur Apple.

Untuk editor desktop terhad yang ditunjukkan di sini, segi empat tepat terlaras yang dikongsi sudah memadai: peruntukkan ruang mendatar untuk kawalan, laraskan pratonton berdasarkan kedua-dua dimensi tersedia, dan tetapkan ketinggiannya kepada ruang paparan kawalan. Ini memberikan peraturan perubahan saiz yang jelas sambil membolehkan kandungan kawalan digulir.

Sumber