Untuk membuat pratinjau SwiftUI interaktif, beri setiap pratinjau state lokalnya sendiri, teruskan model tampilan yang dapat diamati ke editor, dan sediakan properti yang dapat diedit melalui binding. Panduan ini membuat editor label macOS sederhana menggunakan teks contoh sintetis.

Tentukan lokasi state pratinjau

Tugasnya adalah membuat pratinjau editor desktop tempat seseorang mengubah judul dan teks detail label, lalu mengaktifkan atau menonaktifkan tampilan detail. Editor harus merespons seketika, dan setiap pratinjau harus dimulai dengan konfigurasi contoh yang ditentukan secara eksplisit.

Gunakan input terkecil yang mendukung interaksi. Apple menyarankan agar tampilan hanya menerima data yang dibutuhkannya karena dependensi yang rumit membuat pratinjau lebih sulit dibuat. Label statis dapat menerima string; editor memerlukan koneksi yang dapat ditulis ke state-nya. Menambahkan pratinjau ke file antarmuka Anda.

Kebutuhan tampilan Input yang sesuai Lokasi state contoh
Menampilkan teks tetap String yang tidak dapat diubah Langsung di pratinjau
Mengedit satu nilai yang dimiliki induknya Properti @Binding @State lokal di host pratinjau
Mengedit beberapa properti terkait yang dapat diamati Model yang dapat diamati dan diekspos melalui @Bindable Instans model yang disimpan dalam @State milik host pratinjau
Membaca model yang disediakan melalui environment Dependensi environment Injeksi eksplisit di akar pratinjau

Untuk contoh ini, model tampilan menampung dua string, pengaturan visibilitas, dan judul tampilan yang dihitung. Setiap pratinjau memiliki instans terpisah. Dengan begitu, kondisi awal terlihat di samping deklarasi pratinjau dan contoh tidak bergantung pada singleton mutable bersama.

Siapkan target SwiftUI macOS

Gunakan Xcode 16 atau yang lebih baru untuk sintaks @Previewable sebaris yang ditampilkan di sini. Apple memperkenalkan makro tersebut di Xcode 16. Tetapkan target deployment contoh ini ke macOS 14 atau yang lebih baru, yang mendukung Observation di SwiftUI; referensi Previewable() Apple saat ini juga mencantumkan ketersediaan macOS 14. Catatan Rilis Xcode 16, Mengelola data model di aplikasi Anda, Previewable().

Tambahkan file Swift bernama LabelEditor.swift ke target aplikasi SwiftUI macOS yang sudah ada. Dua blok kode berikut membentuk contoh pratinjau lengkap. Pertahankan titik masuk aplikasi yang sudah ada; file ini mendefinisikan model, editor, dan pratinjau.

Kode ini menggunakan ejaan @State di berbagai toolchain yang didukung. Dokumentasi Apple saat ini membedakan implementasinya: Xcode 26 dan versi sebelumnya menggunakan property wrapper State, sedangkan Xcode 27 dan versi yang lebih baru menggunakan makro State(). Ingat perbedaan ini saat memeriksa kode yang dihasilkan atau mempertimbangkan biaya inisialisasi. State, State().

Buat model yang dapat diamati dan tampilan yang dapat diedit

Terapkan @Observable ke model agar SwiftUI dapat melacak properti yang dibaca antarmuka. Gunakan @Bindable di editor untuk membuat binding properti yang diperlukan oleh kolom teks dan toggle. Apple mendokumentasikan kombinasi ini untuk mengedit model yang dapat diamati. Mengelola data model di aplikasi Anda, Bindable.

import Foundation
import Observation
import SwiftUI

@MainActor
@Observable
final class LabelViewModel {
    var title: String
    var detail: String
    var showsDetail: Bool

    init(
        title: String,
        detail: String,
        showsDetail: Bool = true
    ) {
        self.title = title
        self.detail = detail
        self.showsDetail = showsDetail
    }

    var displayedTitle: String {
        let trimmed = title.trimmingCharacters(
            in: .whitespacesAndNewlines
        )
        return trimmed.isEmpty ? "Untitled label" : trimmed
    }
}

@MainActor
struct LabelEditor: View {
    @Bindable var model: LabelViewModel

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            VStack(alignment: .leading, spacing: 6) {
                Text("Title")
                TextField("Label title", text: $model.title)
            }

            VStack(alignment: .leading, spacing: 6) {
                Text("Detail")
                TextField("Label detail", text: $model.detail)
            }

            Toggle("Show detail", isOn: $model.showsDetail)

            Divider()

            Text("Rendered label")
                .font(.caption)
                .foregroundStyle(.secondary)

            VStack(alignment: .leading, spacing: 8) {
                Text(model.displayedTitle)
                    .font(.title2)
                    .fixedSize(horizontal: false, vertical: true)

                if model.showsDetail {
                    Text(model.detail)
                        .foregroundStyle(.secondary)
                        .fixedSize(horizontal: false, vertical: true)
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            .background(
                .quaternary,
                in: RoundedRectangle(cornerRadius: 10)
            )
        }
        .textFieldStyle(.roundedBorder)
        .padding(20)
    }
}

Model ini sengaja menerima semua nilai contoh melalui initializer-nya. Initializer hanya menetapkan properti lokal. Perhitungan nilai cadangan mengubah teks label yang dirender tanpa menimpa judul yang dimasukkan seseorang: judul yang hanya berisi spasi tetap dapat diedit, sementara hasil render menampilkan Untitled label.

Editor menerima model dari pemanggil. Deklarasi @Bindable menyediakan binding seperti $model.title; kepemilikan state berada di host pratinjau. Jika tampilan ini hanya menampilkan nilai model, referensi model biasa sudah cukup untuk observasi. Penyimpanan Bindable berguna di sini karena kontrol memerlukan binding properti yang dapat ditulis. Bindable.

Beri setiap pratinjau state contoh yang mandiri

Tambahkan deklarasi berikut ke file yang sama. Masing-masing membuat model sendiri dengan nilai sintetis yang ditentukan secara eksplisit. Lebar yang dicantumkan hanya contoh untuk memeriksa tata letak, bukan ukuran jendela minimum yang direkomendasikan.

#Preview("Standard label") {
    @Previewable @State var model = LabelViewModel(
        title: "Sample tile",
        detail: "Blue square, medium size"
    )

    LabelEditor(model: model)
        .frame(width: 420)
        .preferredColorScheme(.light)
}

#Preview("Long label in a narrow editor") {
    @Previewable @State var model = LabelViewModel(
        title: "Sample collection with a longer descriptive label",
        detail: "Round tiles arranged in three columns"
    )

    LabelEditor(model: model)
        .frame(width: 300)
        .preferredColorScheme(.dark)
}

#Preview("Blank title and hidden detail") {
    @Previewable @State var model = LabelViewModel(
        title: "   ",
        detail: "An optional detail line",
        showsDetail: false
    )

    LabelEditor(model: model)
        .frame(width: 420)
}

@Previewable menjadikan deklarasi state lokal bagian dari tampilan SwiftUI tersemat yang dihasilkan oleh #Preview. Apple mendokumentasikan bahwa deklarasi bertanda tersebut menjadi properti pada tampilan yang dihasilkan. Letakkan deklarasi di akar closure pratinjau; penggunaan @Previewable di luar badan pratinjau akan menghasilkan kesalahan. Previewable().

Rantai kepemilikannya kini eksplisit: host pratinjau yang dihasilkan menyimpan model dalam @State, LabelEditor menerima referensi model, dan @Bindable menyediakan properti yang dapat diedit. SwiftUI mendukung penyimpanan model Observation dalam state dan meneruskan referensi itu ke tampilan anak. State.

Anggap nilai initializer sebagai fixture awal. Pratinjau interaktif dapat mengubahnya saat berjalan. Saat mereproduksi tata letak tertentu, kembali ke instans pratinjau yang baru sebelum membandingkan tampilan awalnya.

Periksa perilaku dengan matriks skenario sederhana

Buka LabelEditor.swift, pilih Show Canvas, lalu pilih pratinjau bernama dari tab canvas. Jika canvas dijeda, pilih Restart. Aktifkan mode Live untuk mencoba masukan teks dan logika kontrol. Kontrol ini dijelaskan dalam panduan canvas Apple saat ini. Berinteraksi dengan pratinjau di canvas.

Gunakan pemeriksaan berikut untuk membedakan masalah state dari masalah tata letak. Hasil yang diharapkan berasal dari kode contoh; ini bukan hasil pengujian yang dilaporkan.

Skenario Tindakan Hasil yang diharapkan
Label standar Ganti judul dengan Amber circle Judul yang dirender langsung berubah
Visibilitas detail Nonaktifkan Show detail, lalu aktifkan kembali Detail menghilang lalu muncul kembali dengan teksnya tetap tersimpan
Judul kosong Hapus semua karakter judul Judul yang dirender menjadi Untitled label
Judul berisi spasi Masukkan hanya spasi Kolom tetap menyimpan spasi tersebut; judul yang dirender menggunakan nilai cadangan
Editor sempit Periksa pratinjau judul panjang Judul yang dirender membungkus baris di dalam kartu
Fixture terpisah Edit satu pratinjau, lalu periksa pratinjau lain yang masih baru Pratinjau lainnya dimulai dengan nilai model yang dideklarasikannya sendiri

Jika masukan teks berfungsi tetapi hasil render tidak berubah, periksa hubungan antara binding kolom dan properti yang dibaca oleh hasil render. Di sini, kedua jalur mengarah ke model.title, secara langsung atau melalui displayedTitle. Jika menonaktifkan visibilitas menghapus detail, cari kode yang menetapkan nilai kosong; contoh ini hanya mengubah showsDetail.

Tidak ada compiler Swift atau runtime pratinjau macOS yang tersedia di lingkungan riset, jadi contoh ini diperiksa berdasarkan API yang terdokumentasi, bukan dikompilasi atau dijalankan. Bangun target dan selesaikan pemeriksaan interaksi di Xcode sebelum mengadaptasi editor.

Pisahkan penyiapan bersama dari fixture yang dapat diedit

Untuk model sekecil ini, konstruksi lokal membuat setiap pratinjau mudah dipahami. Jika beberapa pratinjau nantinya memerlukan penyiapan bersama yang mahal, Apple menyediakan PreviewModifier: konteks bersamanya disimpan dalam cache dan digunakan kembali oleh pratinjau yang berpartisipasi. API ini memerlukan macOS 15 atau yang lebih baru. PreviewModifier.

Perilaku berbagi yang terdokumentasi itu mengarah pada pilihan desain praktis: simpan model fixture yang dapat diedit secara independen di lokasi lokal, dan gunakan konteks bersama untuk penyiapan yang memang ingin digunakan kembali oleh pratinjau yang berpartisipasi. Model mutable bersama juga akan membagikan perubahan yang dibuat, sehingga skenario terpisah lebih sulit dibandingkan.

Jika tampilan yang sudah ada mengambil model berdasarkan tipenya dari environment, injeksikan instans yang diperlukan di akar pratinjau. Apple mendokumentasikan bahwa model nonopsional dengan tipe yang diminta tetapi tidak tersedia akan menyebabkan exception. Karena itu, pratinjau perlu menyediakan dependensi yang benar-benar dibaca oleh akar yang dipilih. Mengelola data model di aplikasi Anda.

Pertahankan batas AppKit dan TextKit

Editor di atas menggunakan kontrol SwiftUI. Jika editor macOS yang sudah ada memuat tampilan teks AppKit, gunakan NSViewRepresentable untuk mengintegrasikan tampilan tersebut ke dalam hierarki SwiftUI. Buat dan konfigurasikan melalui makeNSView, lalu terapkan state SwiftUI yang masuk melalui updateNSView. Teruskan perubahan dari delegate AppKit melalui coordinator jika pengeditan harus kembali ke SwiftUI. Biarkan SwiftUI mengendalikan frame dan bounds tampilan yang direpresentasikan. NSViewRepresentable.

Apple juga mendukung pratinjau langsung untuk tampilan dan pengontrol tampilan AppKit. Namun, @Previewable hanya tersedia untuk SwiftUI dan tidak berlaku pada pratinjau AppKit langsung. Untuk menggunakan pola state lokal dalam artikel ini dengan konten AppKit, buat pratinjau host SwiftUI yang memuat representable tersebut. Menambahkan pratinjau ke file antarmuka Anda.

Pertahankan pilihan mesin teks saat membuat NSTextView. Apple mendokumentasikan NSTextView(usingTextLayoutManager: true) untuk memilih TextKit 2 saat inisialisasi. Panduan TextKit-nya juga menjelaskan bahwa mengakses layoutManager dari TextKit 1 dapat memicu fallback satu arah untuk instans text view tersebut. Gunakan API TextKit 2 yang sesuai jika memang itu mesin yang Anda inginkan; pratinjau tidak menghapus batasan tersebut. Yang baru di TextKit dan tampilan teks.

Diagnosis kegagalan pratinjau pada batas kepemilikan

Mulailah dari diagnostik build pertama. Makro Swift menghasilkan kode saat kompilasi, jadi kesalahan yang ditandai pada pemanggilan makro bisa melibatkan deklarasi yang dihasilkan. Perintah Xcode Editor > Expand Macro memungkinkan Anda memeriksa kode tersebut. Menerapkan Makro.

Lalu periksa mekanisme state. ObservableObject berbasis Combine yang sudah ada memerlukan pola kepemilikan yang sesuai: Apple memperingatkan bahwa menyimpan objek seperti itu di State tidak melacak perubahan pada properti yang dipublikasikannya dan menyarankan pemiliknya menggunakan StateObject. Contoh ini menggunakan @Observable, jadi kepemilikan state-nya mengikuti jalur Observation. State.

Pastikan file pratinjau yang selesai berfokus pada nilai awal yang eksplisit dan interaksi yang terlihat. Setelah skenario standar, judul panjang, dan judul kosong berfungsi sesuai harapan, gunakan kembali editor yang sama dengan model yang dimiliki tampilan induk aplikasi Anda. Fixture pratinjau tetap menjadi cara ringkas untuk mereproduksi kondisi antarmuka tersebut.

Sumber