अपने Mac ऐप में कस्टम, एकल-कॉलम संग्रह के लिए, स्थिर आइटम ID के सेट से समर्थित चयन बाइंडिंग के साथ SwiftUI के List का उपयोग करें। पंक्तियों को अनुकूलित करें और रेंज चयन तथा Command-क्लिक चयन नेटिव कंट्रोल को संभालने दें।

यह ट्यूटोरियल Tile 01 से Tile 12 तक कृत्रिम लेबलों का संग्रह बनाता है। पाठक का काम पास-पास के आइटम चुनना, अलग-अलग आइटम जोड़ना या हटाना, और फ़िल्टर लगाने या प्रदर्शन क्रम उलटने पर चयन को समझने योग्य बनाए रखना है। बाद के एक अनुभाग में ऐसे संग्रह के लिए AppKit का तरीका बताया गया है जिसे ग्रिड या किसी अन्य कस्टम लेआउट की आवश्यकता हो।

रेंज और Command-क्लिक चयन से क्या होना चाहिए?

Apple की Mac उपयोगकर्ता मार्गदर्शिका इस परिचित तरीके का वर्णन करती है: पहला आइटम क्लिक करें और अंतिम आइटम पर Shift-क्लिक करके बीच के आइटम सहित पास-पास के आइटम चुनें। Command-क्लिक से अलग-अलग आइटम चुने जाते हैं, जिनका पास-पास होना ज़रूरी नहीं; किसी बहु-चयन में चुने हुए आइटम पर Command-क्लिक करने से वह चयन से हट जाता है। इस संग्रह में बनाए रखने योग्य बुनियादी तरीके यही हैं। अपने Mac स्क्रीन पर आइटम चुनें।

शुरुआती कार्यान्वयन के लिए एक ही क्रमबद्ध सूची का उपयोग करें। इससे “बीच” का अर्थ स्पष्ट रहता है: मौजूदा प्रदर्शन क्रम में दो सिरों के बीच की पंक्तियाँ। स्थान-आधारित संग्रह में एक और निर्णय लेना पड़ता है—रेंज डेटा क्रम का अनुसरण करे या लेआउट के किसी क्षेत्र का—जिसकी अलग से जाँच करना उचित है।

उपयोगकर्ता के चयन के काम के अनुसार कंट्रोल चुनें:

पाठक का काम उपयुक्त शुरुआती विकल्प चयन का निरूपण
पूरी कस्टम पंक्तियाँ चुनना SwiftUI List आइटम ID का सेट
कस्टम लेआउट में पूरी टाइलें चुनना AppKit NSCollectionView चयनित इंडेक्स पाथ, जिन्हें मॉडल ID से मैप किया गया हो
लेबल के भीतर अक्षर चुनना SwiftUI टेक्स्ट चयन टेक्स्ट व्यू द्वारा दिया गया टेक्स्ट चयन
टेक्स्ट दस्तावेज़ के भीतर रेंज चुनना AppKit टेक्स्ट सिस्टम अक्षर या टेक्स्ट रेंज

Apple List को वैकल्पिक चयन वाले एकल-कॉलम कंटेनर के रूप में और NSCollectionView को अनुकूलन योग्य लेआउट वाले क्रमबद्ध संग्रह के रूप में दस्तावेज़ित करता है। आइटम चयन के लिए ये समर्थित शुरुआती विकल्प हैं। List, NSCollectionView।

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

SwiftUI का उपयोग करके Xcode में macOS ऐप प्रोजेक्ट बनाएँ। इस उदाहरण के लिए इसका डिप्लॉयमेंट लक्ष्य macOS 14 या बाद का रखें: नीचे उपयोग किया गया दो-पैरामीटर वाला onChange क्लोज़र macOS 14 के लिए आवश्यक है। जनरेट की गई ऐप घोषणा को इस कोड से बदलें और लक्ष्य में ठीक एक @main एंट्री पॉइंट रहने दें। onChange(of:initial:_:), App।

नीचे कृत्रिम डेटा और विंडो सहित एक छोटा, पूर्ण ऐप दिया गया है। इसमें बाहरी एसेट का उपयोग नहीं है। दस्तावेज़ीकृत API के संदर्भ में इसकी जाँच की गई है, लेकिन यहाँ macOS पर इसे कंपाइल या चलाया नहीं गया है।

import Foundation
import SwiftUI

struct Tile: Identifiable {
    let id: Int
    let title: String
}

@main
struct CollectionSelectionDemoApp: App {
    var body: some Scene {
        WindowGroup {
            TileCollectionView()
        }
    }
}

struct TileCollectionView: View {
    private let tiles: [Tile] = (1...12).map {
        Tile(id: $0, title: String(format: "Tile %02d", $0))
    }

    @State private var selectedIDs: Set<Int> = []
    @State private var query = ""
    @State private var reversed = false

    private var visibleTiles: [Tile] {
        let filtered = tiles.filter {
            query.isEmpty ||
            $0.title.localizedCaseInsensitiveContains(query)
        }
        return reversed ? Array(filtered.reversed()) : filtered
    }

    private var selectionDescription: String {
        let names = visibleTiles
            .filter { selectedIDs.contains($0.id) }
            .map(\.title)
        return names.isEmpty ? "None selected" : names.joined(separator: ", ")
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            TextField("Filter tiles", text: $query)
                .textFieldStyle(.roundedBorder)

            HStack {
                Button("Reverse order") {
                    reversed.toggle()
                }
                Button("Clear selection") {
                    selectedIDs.removeAll()
                }
                .disabled(selectedIDs.isEmpty)
            }

            List(visibleTiles, selection: $selectedIDs) { tile in
                HStack(spacing: 12) {
                    RoundedRectangle(cornerRadius: 4)
                        .fill(Color.gray.opacity(0.2))
                        .frame(width: 32, height: 32)
                    VStack(alignment: .leading) {
                        Text(tile.title)
                        Text("Sample item \(tile.id)")
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }
                    Spacer()
                }
                .padding(.vertical, 4)
                .tag(tile.id)
            }

            Text("\(selectedIDs.count) selected")
            Text(selectionDescription)
                .font(.caption)
        }
        .padding()
        .frame(minWidth: 440, minHeight: 480)
        .onChange(of: query) { _, _ in
            let visibleIDs = Set(visibleTiles.map(\.id))
            selectedIDs.formIntersection(visibleIDs)
        }
    }
}

चयन बाइंडिंग का एलिमेंट प्रकार Tile.ID से मेल खाता है: दोनों Int का उपयोग करते हैं। Apple दस्तावेज़ित करता है कि सूची के चयन को उसके डेटा के ID प्रकार वाले सेट से बाँधने पर बहु-चयन सक्षम होता है। Mac पर बहु-चयन के लिए एडिट मोड में जाना आवश्यक नहीं है। सूचियों में चयन का समर्थन।

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

आइटम की पहचान को प्रदर्शन क्रम से अलग रखें

यह उदाहरण जानबूझकर तीन अलग-अलग अवधारणाओं का उपयोग करता है: आइटमों की क्रमबद्ध ऐरे, चुने गए ID का सेट और उस ऐरे का फ़िल्टर किया गया दृश्य। Swift ऐरे को क्रमबद्ध संग्रह और सेट को बिना किसी निर्धारित क्रम वाले, विशिष्ट मानों के संग्रह के रूप में परिभाषित करता है। इसलिए सदस्यता के लिए सेट उपयुक्त है, जबकि प्रदर्शन के लिए उपयोग होने वाला क्रम ऐरे देती है। संग्रह के प्रकार।

इस कृत्रिम डेटा सेट में पूर्णांक ID निश्चित हैं। ऐरे को उलटने से आइटम का स्थान बदलता है, उसकी पहचान नहीं। वास्तविक मॉडल में, हर आइटम बनाते या लोड करते समय उसे एक स्थिर पहचानकर्ता दें और बाद के व्यू अपडेट में उस पहचानकर्ता को बनाए रखें। हर बार पंक्ति का मूल्यांकन होने पर नई पहचान न बनाएँ।

चयन सारांश visibleTiles का अनुसरण करता है और selectedIDs में सदस्यता जाँचता है। यह संग्रह के इन गुणों से निकला एक कार्यान्वयन विकल्प है: इससे सेट के पुनरावृत्ति क्रम पर निर्भर रहने के बजाय प्रदर्शन क्रम में सारांश बनता है।

फ़िल्टर करने के लिए उत्पाद संबंधी एक स्पष्ट निर्णय चाहिए। यह उदाहरण केवल दिखाई देने वाले आइटमों की नीति अपनाता है: फ़िल्टर बदलने पर चयन को उन ID के साथ प्रतिच्छेदित किया जाता है जो अब भी दिखाई दे रहे हैं। छिपे हुए आइटमों का चयन हट जाता है और फ़िल्टर हटाने पर उनका पुराना चयन वापस नहीं आता। लेबल और स्रोत ऐरे अपरिवर्तित रहते हैं।

उदाहरण के लिए, पहले Tile 02, Tile 03 और Tile 04 चुनें। Filter tiles में 04 दर्ज करें। इस उदाहरण की नीति के अनुसार, केवल Tile 04 चयनित रहता है। फ़िल्टर हटाएँ, तो बाकी दो आइटम बिना चयन के फिर दिखाई देते हैं। यह कोड को समझाने वाला उदाहरण है, चलाने के बाद का बताया गया नतीजा नहीं।

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

AppKit कलेक्शन व्यू का उपयोग कब करना चाहिए?

जब संग्रह में कई कॉलम वाली टाइलें या एकल सूची से आगे का लेआउट चाहिए, तब NSCollectionView का उपयोग करें। Apple संग्रह के डेटा स्रोत, आइटम व्यू कंट्रोलर, डेलीगेट और लेआउट ऑब्जेक्ट को अलग रखता है। इससे संग्रह कंट्रोल को क्रिया का स्वामी बनाए रखते हुए रूप-रंग और स्थान को अनुकूलित किया जा सकता है। NSCollectionView।

AppKit कार्यान्वयन को इस क्रम में कॉन्फ़िगर करें:

  1. कलेक्शन व्यू बनाएँ और उसे कोई आधुनिक लेआउट दें, जैसे NSCollectionViewFlowLayout।
  2. उसका डेटा स्रोत दें और सेल बनाने के लिए उपयोग होने वाली आइटम क्लास या nib रजिस्टर करें।
  3. isSelectable को true और allowsMultipleSelection को true पर सेट करें।
  4. allowsEmptySelection को इस आधार पर सेट करें कि कार्य में कुछ भी चयनित न करने की अनुमति है या नहीं।
  5. selectionIndexPaths पढ़ें, फिर हर मान्य इंडेक्स पाथ को संबंधित मॉडल ID से मैप करें।
  6. आइटम व्यू में उसकी isSelected स्थिति का उपयोग करके चयन दिखाएँ।

डिफ़ॉल्ट रूप से बहु-चयन बंद रहता है। इसलिए इसे सक्षम करना सेटअप का स्पष्ट चरण है; केवल चुनने योग्य दिखने वाली टाइलें बनाने से यह अपने आप चालू नहीं होता। आइटम की isSelected प्रॉपर्टी बताती है कि वह आइटम चुना गया है या नहीं। allowsMultipleSelection, isSelected।

इंडेक्स पाथ को मौजूदा डेटा स्रोत में स्थान मानें। Apple दस्तावेज़ित करता है कि selectionIndexPaths इंटरैक्टिव चयन के दौरान अपडेट होता है और इसे प्रोग्राम के ज़रिए भी असाइन किया जा सकता है। ऐसा इंडेक्स पाथ असाइन करने पर अपवाद आता है जो किसी मान्य डेटा-स्रोत आइटम की पहचान नहीं करता। क्रम बदलने या फ़िल्टर करने के बाद, पुराने स्थानों का दोबारा उपयोग करने के बजाय स्थिर ID से चयन स्थान फिर बनाएँ। selectionIndexPaths।

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

जिस SwiftUI ऐप को AppKit कंट्रोल चाहिए, उसमें इसे NSViewRepresentable के माध्यम से एम्बेड करें। makeNSView में AppKit पदानुक्रम बनाएँ, updateNSView में मॉडल बदलावों का समन्वय करें और डेलीगेट इवेंट को SwiftUI स्थिति तक पहुँचाने के लिए कोऑर्डिनेटर का उपयोग करें। SwiftUI प्रस्तुत किए गए व्यू का फ़्रेम और बाउंड नियंत्रित करता है। NSViewRepresentable।

प्रोग्राम के ज़रिए किए गए अपडेट के लिए भी स्पष्ट समन्वय पथ चाहिए। Apple बताता है कि प्रोग्राम के ज़रिए चयन सेट करने पर collectionView(_:shouldSelectItemsAt:) और collectionView(_:didSelectItemsAt:) दोनों छोड़ दिए जाते हैं। हर असाइनमेंट के बाद इन कॉलबैक के चलने पर निर्भर रहने के बजाय, मॉडल अपडेट को एक साझा कार्रवाई में रखें। collectionView(_:shouldSelectItemsAt:), collectionView(_:didSelectItemsAt:)।

क्या टेक्स्ट चयन यही सुविधा देता है?

SwiftUI का textSelection(.enabled) व्यू के भीतर टेक्स्ट चयन को नियंत्रित करता है। इसे किसी कंटेनर पर लगाने से उसमें मौजूद हर टेक्स्ट व्यू चयन योग्य हो जाता है। यह कलेक्शन-आइटम चयन बाइंडिंग या मनमाने कार्डों में साझा रेंज-चयन मॉडल का दस्तावेज़ीकरण नहीं करता। इसका उपयोग तब करें जब काम लेबल से अक्षर चुनना हो। textSelection(_:)।

टेक्स्ट-दस्तावेज़ इंटरफ़ेस के लिए AppKit, NSTextView.selectedRanges के माध्यम से अक्षर चयन उपलब्ध कराता है। इसकी रेंज क्रमबद्ध, एक-दूसरे से न टकराने वाली और गैर-सन्निहित होती हैं। TextKit का NSTextSelection.textRanges भी तार्किक क्रम में व्यवस्थित टेक्स्ट रेंज दर्शाता है और macOS 12 से उपलब्ध है। ये API टेक्स्ट की स्थितियाँ दर्शाते हैं, इसलिए इन्हें टाइल ID में बदलना ऐप-विशिष्ट डिज़ाइन का विषय होगा। selectedRanges, textRanges।

टेक्स्ट चयन सक्षम करने से पहले तय करें कि पंक्ति के लेबल पर किया गया जेस्चर किस काम के लिए होगा। इस ट्यूटोरियल में क्लिक करने पर पूरी पंक्ति चयनित होनी चाहिए, इसलिए उदाहरण में टेक्स्ट चयन कॉन्फ़िगर नहीं किया गया है।

छोटे चयन मैट्रिक्स से इंटरैक्शन की जाँच करें

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

कार्रवाई अपेक्षित नतीजा
Tile 02 पर क्लिक करें एक आइटम चयनित हो
Tile 05 पर Shift-क्लिक करें Tile 02 से Tile 05 तक चयनित हों
Tile 08 पर Command-क्लिक करें Tile 08 चयन में जुड़ जाए
चुने हुए Tile 03 पर Command-क्लिक करें Tile 03 चयन से हट जाए
Clear selection पर क्लिक करें चयन खाली हो और सारांश में None selected दिखे
Tile 04 चुनें, फिर क्रम उलटें चयनित ID अब भी Tile 04 की पहचान करे
कई पंक्तियाँ चुनें, फिर 04 तक फ़िल्टर करें केवल पहले से चुना हुआ, अब भी दिखाई देने वाला आइटम चयनित रहे
फ़िल्टर हटाएँ छिपी पंक्तियाँ लौटें, लेकिन हटाए गए चयन वापस न आएँ

सूची पर क्लिक करने के बाद कीबोर्ड नेविगेशन भी जाँचें, चुनी हुई पंक्तियों को स्क्रॉल करके दृश्य से बाहर और वापस लाएँ, और फ़िल्टर करने के बाद नई रेंज शुरू करें। AppKit ग्रिड के लिए ऐसी जाँचें जोड़ें जो पंक्ति की सीमा पार करें। यदि नतीजा इच्छित काम से अलग हो, तो पहले इंटरफ़ेस को सादे नेटिव कंट्रोल तक सीमित करें, फिर कस्टम पंक्ति सामग्री और जेस्चर हैंडलर एक-एक करके वापस जोड़ें।

इंटरैक्शन की ज़िम्मेदारी नेटिव कंट्रोल को, सदस्यता की ज़िम्मेदारी स्थिर ID को और क्रम की ज़िम्मेदारी प्रदर्शित ऐरे को दें। इन ज़िम्मेदारियों के स्पष्ट होने पर कस्टम पंक्ति शैली, फ़िल्टरिंग और क्रमबद्धता उसी समझने योग्य चयन कार्य को सहारा दे सकती हैं।

स्रोत