एक वैकल्पिक चयनित आइटम के साथ sheet(item:onDismiss:content:) का उपयोग करें और शीट को उस आइटम से बनाएँ जो उसके क्लोज़र को मिलता है। इससे Mac ऐप को एक ऐसा मान मिलता है जो तय करता है कि शीट दिखानी है या नहीं और उसमें क्या दिखेगा।

यह ट्यूटोरियल डेस्कटॉप का एक छोटा काम लागू करता है: किसी स्थानीय नमूना आइटम पर क्लिक करें और तुरंत उसका विवरण देखें। यह उस शीट के लिए समस्या-निवारण का क्रम भी देता है जो खाली खुलती है, पिछला चयन दिखाती है या AppKit व्यू के भीतर पुराना टेक्स्ट दिखाती है।

आइटम-आधारित शीट क्यों इस्तेमाल करें?

SwiftUI में प्रेज़ेंटेशन के दो प्रासंगिक तरीके हैं। Boolean ओवरलोड तब शीट दिखाता है जब उसकी बाइंडिंग true हो जाती है। Item ओवरलोड एक वैकल्पिक पहचान योग्य मान लेता है और उस मान को कंटेंट क्लोज़र तक पहुँचाता है। किसी स्थिर पैनल के लिए Boolean वाला रूप चुनें; जब पैनल की सामग्री चयन पर निर्भर हो, तो Item वाला रूप चुनें। Apple का दस्तावेज़ देखें: Boolean-आधारित शीट और आइटम-आधारित शीट।

चयन और प्रेज़ेंटेशन के लिए अलग-अलग प्रॉपर्टी रखने वाले डिज़ाइन में ऐसे संयोजन संभव हैं जैसे शीट दिखाने का अनुरोध हो लेकिन कोई चयन न हो। दोनों को एक वैकल्पिक मान में मिलाने से आपके अपने स्टेट मॉडल में यह संयोजन संभव नहीं रहता: nil का अर्थ है कि कोई आइटम अनुरोधित नहीं है, और भरा हुआ मान उस आइटम को दर्शाता है जिसका निरीक्षण करना है। यह दोनों API से निकला डिज़ाइन संबंधी निष्कर्ष है, SwiftUI की किसी अघोषित शेड्यूलिंग त्रुटि के बारे में दावा नहीं।

अगर मौजूदा इम्प्लीमेंटेशन में चयन असाइन करने के बाद Boolean टॉगल किया जाता है, तो केवल ये दो स्टेटमेंट यह साबित नहीं करते कि पहली बार शीट दिखाने में विफलता क्यों होती है। लक्षण का कारण टाइमिंग को बताने से पहले कंटेंट के डेटा पथ की जाँच करें। उपयोगी सवाल यह है कि शीट अपने दिए गए इनपुट को रेंडर करती है, किसी दूसरी प्रॉपर्टी को पढ़ती है या अलग से स्टोर की गई कॉपी दिखाती है।

macOS का पूरा उदाहरण बनाएँ

Xcode में SwiftUI इंटरफ़ेस वाला macOS ऐप प्रोजेक्ट बनाएँ। इस उदाहरण के लिए macOS 12 या बाद का डिप्लॉयमेंट टार्गेट रखें। Item-आधारित शीट API macOS 10.15 से उपलब्ध है, जबकि नीचे इस्तेमाल किया गया environment dismissal action macOS 12 या बाद का संस्करण माँगता है। Apple ने इन उपलब्धता सीमाओं को आइटम-आधारित शीट और dismissal environment value के दस्तावेज़ में बताया है।

बनाए गए ऐप डिक्लेरेशन और कंटेंट व्यू को इस पूरे प्रोग्राम से बदलें। टार्गेट में केवल एक ऐप एंट्री पॉइंट रखें। नीचे दिए गए सभी लेबल और विवरण सिंथेटिक स्थानीय नमूना डेटा हैं।

import SwiftUI

struct SampleItem: Identifiable {
    let id: String
    let title: String
    let detail: String
}

@main
struct FirstSheetDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ItemListView()
        }
    }
}

struct ItemListView: View {
    private let items = [
        SampleItem(
            id: "tile-a",
            title: "Amber Tile",
            detail: "A square sample with an amber finish."
        ),
        SampleItem(
            id: "tile-b",
            title: "Blue Tile",
            detail: "A rectangular sample with a blue finish."
        )
    ]

    @State private var presentedItem: SampleItem? = nil

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text("Sample Items")
                .font(.title2)

            ForEach(items) { item in
                Button("Inspect \(item.title)") {
                    presentedItem = item
                }
            }
        }
        .padding(24)
        .frame(minWidth: 360, minHeight: 200)
        .sheet(item: $presentedItem) { item in
            ItemDetailView(item: item)
        }
    }
}

struct ItemDetailView: View {
    let item: SampleItem
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text(item.title)
                .font(.title)

            Text(item.detail)

            Text("Item ID: \(item.id)")
                .font(.caption)

            HStack {
                Spacer()
                Button("Done") {
                    dismiss()
                }
            }
        }
        .padding(24)
        .frame(width: 380, height: 220)
    }
}

बटन क्लिक किए गए मान को presentedItem में असाइन करता है। फिर प्रेज़ेंटेशन क्लोज़र अपना item पैरामीटर ItemDetailView को देता है। ध्यान दें कि डिटेल व्यू में साधारण इनपुट प्रॉपर्टी let item है, दूसरी चयनित-आइटम स्टेट प्रॉपर्टी नहीं। Apple एक ही स्रोत को सत्य बनाए रखने और रीड-ओनली मानों को प्रॉपर्टी के ज़रिए साझा करने की सलाह देता है; जब चाइल्ड को लिखने की अनुमति चाहिए, तब बाइंडिंग का उपयोग करें। State देखें।

Dismissal action को ItemDetailView के भीतर होना चाहिए, जहाँ यह शीट के environment को पढ़ता है। Apple स्पष्ट रूप से चेतावनी देता है कि इसे प्रस्तुत करने वाले व्यू में पढ़कर शीट कंटेंट से उस action को चलाने पर प्रस्तुत करने वाले व्यू का environment इस्तेमाल होता है। जैसा दिखाया गया है, इसकी घोषणा शीट के अपने कंटेंट के पास रखें। dismiss देखें।

प्रोग्राम के इनपुट और संदर्भों की संगति जाँची गई है; यहाँ इसे कंपाइल या रन नहीं किया गया, क्योंकि इस environment में Swift टूलचेन उपलब्ध नहीं है। अपने टार्गेट macOS संस्करण पर Xcode में ये जाँचें करें।

सोच-समझकर किए गए क्लिक क्रम से पहली प्रेज़ेंटेशन जाँचें

पहली प्रेज़ेंटेशन की हर जाँच एक नई लॉन्च से शुरू करें। किसी दूसरे आइटम को खोलने के बाद ही जाँच करने से मूल लक्षण छिप सकता है। नीचे दी गई अपेक्षाएँ नमूने के स्पष्ट लेबल और डेटा असाइनमेंट पर आधारित हैं; ये स्वीकृति मानदंड हैं, रिपोर्ट किए गए परीक्षण परिणाम नहीं।

कार्रवाई शीट में अपेक्षित सामग्री यह क्या जाँचती है
लॉन्च करें और सबसे पहले Inspect Blue Tile पर क्लिक करें Blue Tile, उसका आयताकार विवरण और tile-b पहली प्रेज़ेंटेशन में क्लिक किया गया आइटम इस्तेमाल होता है
Done पर क्लिक करें, फिर Amber Tile देखें Amber Tile, उसका चौकोर विवरण और tile-a अगला चयन कंटेंट तक पहुँचता है
शीट बंद करें और Amber Tile को फिर से देखें वही एंबर विवरण आइटम दोबारा खोलने पर भी सामग्री एक जैसी रहती है
फिर से लॉन्च करें और सबसे पहले Amber Tile देखें तुरंत एंबर विवरण सही परिणाम इस पर निर्भर नहीं करता कि कौन-सा नमूना पहले खोला गया

शीर्षक के साथ ID दिखाने से यह जाँच केवल शीर्षक जाँचने की तुलना में अधिक उपयोगी होती है। अगर आपके असली इंटरफ़ेस में एक जैसे डिस्प्ले नाम हो सकते हैं, तो डेवलपमेंट के दौरान एक ही शीर्षक लेकिन अलग ID और विवरण वाले दो सिंथेटिक आइटम इस्तेमाल करें। इससे डिस्प्ले टेक्स्ट के आधार पर अनजाने में चयन होने का पता चलता है।

बटन से अंतिम व्यू तक पुराने कंटेंट की समस्या का निदान करें

इन जाँचों को इसी क्रम में करें। हर चरण अंतर की जगह को सीमित करता है, ताकि आप असंबंधित प्रेज़ेंटेशन उपाय जोड़े बिना संबंधित लेयर बदल सकें।

  1. एक्शन का इनपुट जाँचें। क्लिक किए गए बटन के एक्शन में ब्रेकपॉइंट लगाएँ और item.id देखें। पुष्टि करें कि एक्शन को इच्छित रिकॉर्ड मिल रहा है।
  2. प्रेज़ेंटेशन का इनपुट जाँचें। शीट क्लोज़र के भीतर item पैरामीटर देखें। अलग चयन को फिर से देखने के बजाय उस पैरामीटर को डिटेल व्यू में दें।
  3. अंत में दिखने वाली प्रॉपर्टी जाँचें। पुष्टि करें कि शीर्षक और विवरण डिटेल व्यू के इनपुट से पढ़े जा रहे हैं। ऐसी कैश की गई प्रॉपर्टी या स्थानीय ड्राफ़्ट देखें जिसमें पहले का मान हो।
  4. किसी भी AppKit ब्रिज को जाँचें। अगर सामान्य SwiftUI टेक्स्ट सही है लेकिन रैप किया गया टेक्स्ट पुराना है, तो representable के update मेथड को देखें।

पहचान को स्थिर रखें

Item-आधारित API के लिए Identifiable आवश्यक है। Swift इस प्रोटोकॉल को स्थिर पहचान के आधार पर परिभाषित करता है और उसकी अवधि तथा दायरा conforming type और उसके उपभोक्ता पर छोड़ता है। इस नमूने में स्टोर की गई string ID हैं जो दोनों रिकॉर्ड को अलग पहचानती हैं। Identifiable देखें।

किसी असली सूची के लिए ऐसा identifier चुनें जो आपके इंटरफ़ेस द्वारा समर्थित अपडेट के दौरान उसी रिकॉर्ड की पहचान करता रहे। ऐसे शीर्षक से पहचान तय न करें जो बदल सकता हो या कई आइटम साझा कर सकते हों। साथ ही, ऐसा computed id न रखें जो पढ़े जाने पर हर बार नया UUID बनाए; इससे स्थिर पहचान नहीं मिलेगी।

दिखाए जाने वाले इनपुट को संपादन योग्य ड्राफ़्ट से अलग रखें

इंस्पेक्टर item.title सीधे दिखा सकता है। एडिटर जान-बूझकर स्थानीय ड्राफ़्ट बना सकता है, लेकिन ड्राफ़्ट एक और मान जोड़ता है जिसकी अवधि तय करनी होगी। तय करें कि वह कब बनता है, बाद के मॉडल बदलाव उसे बदलेंगे या नहीं और उपयोगकर्ता के पूरा करने या रद्द करने पर क्या होगा।

Apple बताता है कि SwiftUI किसी व्यू की पहचान के लिए स्टेट स्टोरेज सुरक्षित रखता है और उस पहचान की अवधि की शुरुआत में उसे आरंभ करता है। व्यू का मान फिर से बनाने से अपने-आप नया स्टेट नहीं बनता। इसलिए, इनपुट से स्थानीय स्टेट आरंभ करना बाद के इनपुट के साथ उस स्टेट को सिंक में रखने का सामान्य तरीका नहीं है। Demystify SwiftUI देखें।

इस रीड-ओनली काम के लिए साधारण इनपुट प्रॉपर्टी रखें। अगर इसे एडिटिंग तक बढ़ाते हैं, तो ड्राफ़्ट और कमिट का स्पष्ट डिज़ाइन चुनें या इच्छित मॉडल मान की बाइंडिंग इस्तेमाल करें। केवल टेक्स्ट रीफ़्रेश करने के लिए बदलती हुई व्यू पहचान न जोड़ें: पहचान बदलने से उससे जुड़ा स्टेट बदल जाता है और अधूरा ड्राफ़्ट खो सकता है। यह परिणाम Apple के दस्तावेज़ित स्टेट-अवधि मॉडल से निकलता है।

शीट में AppKit टेक्स्ट व्यू होने पर क्या बदलता है?

NSTextView वाली शीट को भी चयनित आइटम का वही डेटा पथ चाहिए। इसके साथ सही तरीके से लागू किया गया ब्रिज भी चाहिए। Apple का NSViewRepresentable प्रोटोकॉल शुरुआती निर्माण को makeNSView(context:) में और बाद के कॉन्फ़िगरेशन को updateNSView(_:context:) में अलग करता है। ऐसा रैपर जो टेक्स्ट केवल निर्माण के समय असाइन करता है, बाद के टेक्स्ट इनपुट लागू करने का कोई तरीका नहीं रखता। NSViewRepresentable देखें।

रीड-ओनली रैपर के लिए native view को दिए गए टेक्स्ट से आरंभ करें और ज़रूरत पड़ने पर उसके update मेथड में दिखाए गए टेक्स्ट को मौजूदा इनपुट से मेल करवाएँ। Apple दस्तावेज़ित करता है कि संबंधित AppKit view को प्रभावित करने वाले बदलावों पर SwiftUI update मेथड को कॉल करता है। updateNSView(_:context:) देखें।

संपादन योग्य रैपर के लिए एक अतिरिक्त नीति चाहिए: हर अपडेट पर उसका टेक्स्ट बिना जाँचे न बदलें, ताकि चल रहे संपादन मिट न जाएँ। Representable के दस्तावेज़ में Apple के बताए अनुसार, native delegate या target-action बदलावों को SwiftUI तक पहुँचाने के लिए coordinator इस्तेमाल करें। SwiftUI को प्रदर्शित view का frame और bounds नियंत्रित करने दें; Apple इन लेआउट प्रॉपर्टी को सीधे सेट करने के विरुद्ध चेतावनी देता है।

TextKit टेक्स्ट स्टोरेज, लेआउट और रेंडरिंग संभालता है। Apple NSTextView में उपलब्ध आधुनिक NSTextLayoutManager और पुराने NSLayoutManager—दोनों रास्तों का दस्तावेज़ देता है। ये सुविधाएँ शीट को चयनित रिकॉर्ड नहीं देतीं। टेक्स्ट लेआउट की जाँच से पहले यह तय करें कि ब्रिज को सही आइटम का टेक्स्ट मिल रहा है, और अतिरिक्त TextKit API शामिल करने से पहले उसकी उपलब्धता अलग से जाँचें। TextKit देखें।

काम के अनुरूप प्रेज़ेंटेशन मॉडल चुनें

जब कंटेंट स्थिर हो और बदलने वाला एकमात्र इनपुट उसकी दृश्यता हो, तो Boolean ओवरलोड इस्तेमाल करें। इस इंस्पेक्टर काम के लिए Item ओवरलोड इस्तेमाल करें। Apple दस्तावेज़ करता है कि दिए गए आइटम को बदलने से मौजूदा शीट बंद होकर उसकी जगह दूसरी शीट आ सकती है, इसलिए आइटम बदलने को उसी प्रेज़ेंटेशन में बिना रुकावट एडिटिंग जारी रहने की गारंटी न मानें। sheet(item:onDismiss:content:) देखें।

पहली प्रेज़ेंटेशन सही रखने के लिए चयनित रिकॉर्ड का पता लगाना आसान बनाएँ: बटन एक्शन में एक असाइनमेंट, शीट क्लोज़र में एक आइटम पैरामीटर और डिटेल व्यू में एक इनपुट। नई लॉन्च से दोनों नमूना आइटम जाँचें। अगर परिणाम फिर भी अलग हो, तो निदान क्रम से पता चलेगा कि अंतर चयन, सुरक्षित रखे गए स्टेट या native-view अपडेट पथ से आ रहा है।

स्रोत