Untuk membina pratonton SwiftUI yang interaktif, berikan setiap pratonton keadaan setempatnya sendiri, hantarkan model paparan yang boleh diperhatikan kepada editor, dan dedahkan sifat yang boleh diedit melalui binding. Panduan ini membina editor label macOS kecil menggunakan teks contoh sintetik.

Pilih tempat keadaan pratonton patut disimpan

Tugasnya ialah mempratonton editor desktop yang membolehkan seseorang menukar tajuk dan teks terperinci label, kemudian menogol sama ada butiran itu dipaparkan. Editor perlu memberikan respons serta-merta, dan setiap pratonton perlu bermula dengan konfigurasi contoh yang jelas.

Gunakan input paling ringkas yang menyokong interaksi tersebut. Apple mengesyorkan supaya paparan hanya menerima data yang diperlukan kerana kebergantungan yang rumit menyukarkan pembinaan pratonton. Label statik boleh menerima rentetan; editor memerlukan sambungan boleh tulis kepada keadaannya. Menambahkan pratonton pada fail antara muka anda.

Keperluan paparan Input yang sesuai Tempat keadaan contoh disimpan
Memaparkan teks tetap Rentetan tidak boleh ubah Terus dalam pratonton
Mengedit satu nilai milik induknya Sifat @Binding @State setempat dalam hos pratonton
Mengedit beberapa sifat boleh diperhatikan yang berkaitan Model boleh diperhatikan yang didedahkan melalui @Bindable Contoh model yang disimpan dalam @State hos pratonton
Membaca model yang dibekalkan melalui persekitaran Kebergantungan persekitaran Suntikan eksplisit pada akar pratonton

Untuk contoh ini, model paparan menyimpan dua rentetan, tetapan keterlihatan dan tajuk paparan terhitung. Setiap pratonton memiliki contoh yang berasingan. Ini menjadikan keadaan permulaan kelihatan di sebelah pengisytiharan pratonton dan mengelakkan contoh ini daripada bergantung pada singleton boleh ubah yang dikongsi.

Sediakan sasaran SwiftUI macOS

Gunakan Xcode 16 atau lebih baharu untuk sintaks @Previewable sebaris yang ditunjukkan di sini. Apple memperkenalkan makro itu dalam Xcode 16. Tetapkan sasaran penggunaan contoh ini kepada macOS 14 atau lebih baharu, yang menyokong Observation dalam SwiftUI; rujukan semasa Apple bagi Previewable() juga menyenaraikan ketersediaan pada macOS 14. Nota Keluaran Xcode 16, Mengurus data model dalam app anda, Previewable().

Tambahkan fail Swift bernama LabelEditor.swift pada sasaran app SwiftUI macOS sedia ada. Dua blok kod seterusnya bersama-sama membentuk contoh pratonton yang lengkap. Kekalkan titik masuk app sedia ada; fail ini mentakrifkan model, editor dan pratonton.

Kod ini menggunakan ejaan @State merentas toolchain yang disokong. Dokumentasi semasa Apple membezakan pelaksanaannya: Xcode 26 dan lebih awal menggunakan pembalut sifat State, manakala Xcode 27 dan lebih baharu menggunakan makro State(). Ingat perbezaan ini apabila memeriksa kod yang dijana atau memikirkan kos pemulaan. State, State().

Bina model boleh diperhatikan dan paparan boleh diedit

Gunakan @Observable pada model supaya SwiftUI dapat menjejak sifat yang dibaca oleh antara muka. Gunakan @Bindable dalam editor untuk mewujudkan binding sifat yang diperlukan oleh medan teks dan togolnya. Apple mendokumenkan gabungan ini untuk mengedit model boleh diperhatikan. Mengurus data model dalam app 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 pemulanya. Pemula itu hanya menetapkan sifat setempat. Pengiraan nilai sandaran mengubah perkara yang dipaparkan oleh label tanpa menimpa tajuk yang dimasukkan seseorang: tajuk yang hanya mengandungi ruang kosong kekal boleh diedit, sementara hasil paparan menunjukkan Untitled label.

Editor menerima modelnya daripada pemanggil. Pengisytiharan @Bindable menyediakan binding seperti $model.title; pemilikan keadaan terletak pada hos pratonton. Jika paparan ini hanya memaparkan nilai model, rujukan model biasa sudah memadai untuk pemerhatian. Storan Bindable berguna di sini kerana kawalan memerlukan binding sifat boleh tulis. Bindable.

Berikan keadaan contoh yang berasingan kepada setiap pratonton

Tambahkan pengisytiharan berikut pada fail yang sama. Setiap satunya mencipta model sendiri dengan nilai sintetik yang jelas. Lebar tersebut hanyalah pemeriksaan susun atur ilustratif, bukannya saiz tetingkap minimum yang disyorkan.

#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 pengisytiharan keadaan setempat sebahagian daripada paparan SwiftUI terbenam yang dijana oleh #Preview. Apple mendokumenkan bahawa pengisytiharan yang ditandai menjadi sifat pada paparan yang dijana itu. Letakkan pengisytiharan pada akar penutupan pratonton; menggunakan @Previewable di luar badan pratonton ialah ralat. Previewable().

Rantaian pemilikan yang terhasil adalah jelas: hos pratonton yang dijana menyimpan model dalam @State, LabelEditor menerima rujukan model, dan @Bindable mendedahkan sifatnya yang boleh diedit. SwiftUI menyokong penyimpanan model Observation dalam keadaan dan penghantaran rujukan itu kepada paparan anak. State.

Anggap nilai pemula sebagai data ujian permulaan. Pratonton interaktif boleh mengubahnya semasa berjalan. Apabila menghasilkan semula susun atur tertentu, kembali kepada contoh pratonton yang baharu sebelum membandingkan rupa awalnya.

Semak tingkah laku dengan matriks senario kecil

Buka LabelEditor.swift, pilih Show Canvas, dan pilih pratonton bernama daripada tab kanvas. Jika kanvas dijeda, gunakan Restart. Aktifkan mod Live untuk menguji kemasukan teks dan logik kawalan. Kawalan ini diterangkan dalam panduan kanvas semasa Apple. Berinteraksi dengan pratonton dalam kanvas.

Gunakan semakan berikut untuk membezakan masalah keadaan daripada masalah susun atur. Hasil yang dijangka berpunca daripada kod contoh; ia bukan hasil ujian yang dilaporkan.

Senario Tindakan Hasil yang dijangka
Label standard Gantikan tajuk dengan Amber circle Tajuk yang dipaparkan berubah serta-merta
Keterlihatan butiran Matikan Show detail, kemudian hidupkannya Butiran hilang dan muncul semula dengan teksnya kekal
Tajuk kosong Padam semua aksara tajuk Tajuk yang dipaparkan menjadi Untitled label
Tajuk ruang kosong Masukkan ruang sahaja Medan mengekalkan ruang itu; tajuk paparan menggunakan nilai sandaran
Editor sempit Periksa pratonton tajuk panjang Tajuk yang dipaparkan terlipat dalam kad
Data ujian berasingan Edit satu pratonton, kemudian periksa pratonton lain yang baharu Pratonton lain bermula dengan nilai model yang diisytiharkan untuknya sendiri

Jika kemasukan teks berfungsi tetapi output paparan tidak berubah, periksa sambungan antara binding medan dengan sifat yang dibaca oleh output. Di sini, kedua-dua laluan menuju ke model.title, secara langsung atau melalui displayedTitle. Jika menogol keterlihatan mengosongkan butiran, cari kod yang menetapkan nilai kosong; contoh ini hanya mengubah showsDetail.

Tiada pengkompil Swift atau masa jalan pratonton macOS tersedia dalam persekitaran penyelidikan, jadi contoh ini diperiksa berdasarkan API yang didokumenkan dan bukannya dikompil atau diuji. Bina sasaran dan lengkapkan semakan interaksi dalam Xcode sebelum mengubah suai editor.

Asingkan persediaan dikongsi daripada data ujian boleh edit

Untuk model sekecil ini, pembinaan setempat memudahkan pemahaman setiap pratonton. Jika beberapa pratonton akhirnya memerlukan persediaan boleh guna semula yang mahal, Apple menyediakan PreviewModifier: konteks kongsinya dicache dan digunakan semula merentas pratonton yang mengambil bahagian. API ini memerlukan macOS 15 atau lebih baharu. PreviewModifier.

Tingkah laku perkongsian yang didokumenkan itu membawa kepada pilihan reka bentuk praktikal: kekalkan model data ujian yang boleh diedit secara berasingan di tempat setempat, dan simpan konteks dikongsi untuk persediaan yang memang anda mahu digunakan semula oleh pratonton yang mengambil bahagian. Model boleh ubah yang dikongsi juga akan berkongsi suntingannya, lalu menyukarkan perbandingan senario berasingan.

Jika paparan sedia ada mendapatkan model mengikut jenisnya daripada persekitaran, suntik contoh yang diperlukan pada akar pratonton. Apple mendokumenkan bahawa model bukan pilihan daripada jenis yang diminta yang tiada akan menyebabkan pengecualian. Oleh itu, pratonton perlu membekalkan kebergantungan yang benar-benar dibaca oleh akar pilihannya. Mengurus data model dalam app anda.

Kekalkan sempadan AppKit dan TextKit

Editor di atas menggunakan kawalan SwiftUI. Jika editor macOS sedia ada anda mengandungi paparan teks AppKit, gunakan NSViewRepresentable untuk menyepadukan paparan itu ke dalam hierarki SwiftUI. Cipta dan konfigurasikannya melalui makeNSView, kemudian gunakan keadaan SwiftUI yang diterima melalui updateNSView. Salurkan perubahan delegat AppKit melalui koordinator apabila suntingan perlu dihantar kembali kepada SwiftUI. Biarkan SwiftUI mengawal bingkai dan sempadan paparan yang diwakilkan. NSViewRepresentable.

Apple juga menyokong pratonton langsung paparan dan pengawal paparan AppKit. Namun, @Previewable hanya tersedia untuk SwiftUI dan tidak terpakai pada pratonton AppKit langsung. Untuk menggunakan corak keadaan setempat dalam artikel ini dengan kandungan AppKit, pratontonkan hos SwiftUI yang mengandungi representable itu. Menambahkan pratonton pada fail antara muka anda.

Kekalkan pilihan enjin teks anda semasa membina NSTextView. Apple mendokumenkan NSTextView(usingTextLayoutManager: true) untuk memilih TextKit 2 semasa pemulaan. Panduan TextKitnya juga menerangkan bahawa mengakses layoutManager TextKit 1 boleh mencetuskan sandaran sehala bagi contoh paparan teks itu. Gunakan API TextKit 2 yang sesuai apabila itulah enjin yang anda kehendaki; pratonton tidak menghapuskan sempadan tersebut. Perkara baharu dalam TextKit dan paparan teks.

Diagnosis kegagalan pratonton pada sempadan pemilikan

Mulakan dengan diagnostik binaan yang pertama. Makro Swift menjana kod semasa kompilasi, jadi ralat yang diserlahkan pada panggilan makro mungkin melibatkan pengisytiharan yang dijana. Perintah Xcode Editor > Expand Macro membolehkan anda memeriksa kod tersebut. Menggunakan Makro.

Kemudian periksa mekanisme keadaan. ObservableObject berasaskan Combine yang sedia ada memerlukan corak pemilikan yang sepadan: Apple memberi amaran bahawa menyimpan objek sebegitu dalam State tidak menjejak perubahan pada sifat terbitannya dan mengarahkan pemilik supaya menggunakan StateObject. Contoh ini menggunakan @Observable, jadi pemilikan keadaannya mengikut laluan Observation. State.

Pastikan fail pratonton siap memfokuskan nilai permulaan yang jelas dan interaksi yang kelihatan. Setelah senario standard, tajuk panjang dan tajuk kosong berfungsi seperti dijangka, gunakan semula editor yang sama dengan model milik paparan induk yang sesuai dalam app anda. Data ujian pratonton kekal sebagai cara ringkas untuk menghasilkan semula keadaan antara muka tersebut.

Sumber