SwiftUI स्क्रॉल व्यू को छोटी सामग्री के अनुसार ढालने के लिए, उसकी भीतरी सामग्री का माप लें और व्यूपोर्ट की ऊँचाई को उस माप और चुनी गई सीमा में से छोटी वाली ऊँचाई पर सेट करें। लंबी सामग्री फिर उस सीमा के भीतर स्क्रॉल होती है। इस चरण-दर-चरण मार्गदर्शिका में आपके अपने Mac ऐप के लिए एक छोटा लेबल पैनल बनाया गया है।
छोटा स्क्रॉल व्यू खाली जगह क्यों छोड़ता है?
पहले दो आयामों को अलग-अलग समझें: सामग्री की ऊँचाई और उसे दिखाने वाले व्यूपोर्ट की ऊँचाई। Apple ScrollView को ऐसा व्यू बताता है जो अपनी अंतर्निहित सामग्री का एक हिस्सा दिखाता है और स्क्रॉल करने पर दिखने वाले हिस्से को बदलता है। लेबल का छोटा समूह अपने आप आपकी पसंदीदा व्यूपोर्ट ऊँचाई तय नहीं करता। Apple का ScrollView दस्तावेज़ यह अंतर समझाता है।
छोटे डेस्कटॉप पैनल के लिए आकार तय करने का स्पष्ट नियम बनाएँ: viewportHeight = min(contentHeight, maximumHeight)। सामग्री का माप उस चौड़ाई पर लें जो उसे वास्तव में मिलती है, और उसमें वह पैडिंग भी शामिल करें जिसे उसके साथ स्क्रॉल होना चाहिए। यह माप बाहरी स्क्रॉल व्यू पर लागू करें। Swift का min(_:_:) छोटी वैल्यू लौटाता है, जबकि SwiftUI का frame(width:height:alignment:) ऊँचाई वाला आर्ग्युमेंट दिए जाने पर निश्चित ऊँचाई तय करता है। Swift min(_:_:), SwiftUI का निश्चित फ़्रेम।
उदाहरण के लिए, मान लें कि अधिकतम ऊँचाई 240 पॉइंट है। 86 पॉइंट ऊँची सामग्री से 86 पॉइंट का व्यूपोर्ट बनेगा; 410 पॉइंट ऊँची सामग्री से 240 पॉइंट का व्यूपोर्ट बनेगा और बाकी सामग्री स्क्रॉल करके देखी जा सकेगी। ये केवल उदाहरण के लिए दिए गए इनपुट हैं, नीचे दिए गए उदाहरण के माप नहीं।
| बदलाव | यह किसे नियंत्रित करता है | क्या इससे वांछित छोटी ऊँचाई तय होती है? |
|---|---|---|
| सामग्री की मापी गई ऊँचाई को सीमा में बाँधकर स्क्रॉल व्यू पर लागू करना | व्यूपोर्ट की ऊँचाई | हाँ, नीचे दिखाए गए सीमित लेआउट में |
.scrollBounceBehavior(.basedOnSize) |
सामग्री के आकार के अनुसार बाउंस व्यवहार | नहीं |
लेबल पर .fixedSize(horizontal: false, vertical: true) |
सामग्री का आदर्श ऊर्ध्वाधर आकार | यह माप लेने में मदद करता है; बाहरी व्यूपोर्ट को फिर भी अपना ऊँचाई नियम चाहिए |
Apple .basedOnSize को बाउंस कॉन्फ़िगरेशन और fixedSize(horizontal:vertical:) को चुने हुए आयामों पर व्यू को उसके आदर्श आकार पर स्थिर करने के रूप में बताता है। इनमें से कोई भी वर्णन सामग्री से निकली व्यूपोर्ट ऊँचाई तय नहीं करता। स्क्रॉल बाउंस व्यवहार, अक्ष के अनुसार निश्चित आकार।
अधिकतम स्क्रॉल ऊँचाई वाला छोटा पैनल बनाएँ
Xcode का ऐसा मौजूदा इंस्टॉलेशन इस्तेमाल करें जिसके macOS SDK में एकल वैल्यू वाला onGeometryChange(for:of:action:) ओवरलोड उपलब्ध हो। Apple के मौजूदा दस्तावेज़ के अनुसार, यह ओवरलोड macOS 13.0 से उपलब्ध है। नीचे दिया गया पूरा उदाहरण macOS 13.3 या उसके बाद के संस्करण को लक्ष्य बनाता है, क्योंकि इसमें scrollBounceBehavior(_:axes:) भी इस्तेमाल होता है, जो macOS 13.3 से उपलब्ध है। ज्यामिति परिवर्तन कॉलबैक, बाउंस मॉडिफ़ायर की उपलब्धता।
SwiftUI का उपयोग करके macOS ऐप प्रोजेक्ट बनाएँ। जेनरेट किए गए ऐप डिक्लेरेशन और कंटेंट व्यू को इस एकल-फ़ाइल प्रोग्राम से बदलें; टार्गेट में केवल एक @main डिक्लेरेशन रखें। सभी लेबल स्थानीय और कृत्रिम स्ट्रिंग हैं। Show more labels टॉगल छोटी और लंबी सामग्री के बीच स्विच करता है।
import SwiftUI
@main
struct CompactLabelsApp: App {
var body: some Scene {
WindowGroup {
CompactLabelsView()
}
}
}
struct CompactLabelsView: View {
@State private var showMore = false
@State private var viewportHeight: CGFloat = 240
private var labels: [String] {
if showMore {
return (1...18).map { number in
"Sample label \(number): a longer description that can wrap when the panel becomes narrower."
}
}
return ["Sample label A", "Sample label B"]
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Toggle("Show more labels", isOn: $showMore)
ScrollView(.vertical) {
VStack(alignment: .leading, spacing: 8) {
if labels.isEmpty {
Text("No labels to display")
} else {
ForEach(labels.indices, id: \.self) { index in
Text(labels[index])
}
}
}
.font(.body)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(12)
.onGeometryChange(for: CGFloat.self) { proxy in
min(proxy.size.height.rounded(.up), 240)
} action: { newHeight in
viewportHeight = newHeight
}
}
.frame(height: viewportHeight)
.scrollBounceBehavior(.basedOnSize, axes: .vertical)
.border(Color.secondary.opacity(0.4))
Text("The label panel grows up to 240 points.")
.font(.caption)
}
.padding(20)
.frame(
maxWidth: .infinity,
maxHeight: .infinity,
alignment: .topLeading
)
.frame(minWidth: 300, minHeight: 360)
}
}
प्रोग्राम का निरीक्षण किया गया है, लेकिन इस परिवेश में इसे कंपाइल या रन नहीं किया गया, क्योंकि यहाँ Swift कंपाइलर या macOS रनटाइम उपलब्ध नहीं है। इसे अपने मौजूदा इंटरफ़ेस में शामिल करने से पहले Xcode में बिल्ड करें और नीचे दिए गए परीक्षण करें।
माप स्क्रॉल व्यू के भीतर क्यों होना चाहिए
ज्यामिति कॉलबैक पैडिंग वाले भीतरी स्टैक को देखता है। बाहरी स्क्रॉल व्यू का माप लेने पर वही व्यूपोर्ट रिपोर्ट होगा जिसकी ऊँचाई आपने पहले ही तय की है। इससे सामग्री की आवश्यक ऊँचाई पता करने के बजाय आकार तय करने का नियम चक्रीय हो जाएगा।
ट्रांसफ़ॉर्म केवल पूर्णांक में ऊपर की ओर राउंड की गई, सीमा में बँधी ऊँचाई लौटाता है। Apple ज्यामिति को अपनी ज़रूरत की वैल्यू में बदलने और बार-बार होने वाले ज्यामिति परिवर्तनों पर व्यापक अपडेट से बचने की सलाह देता है। यहाँ स्क्रॉल की स्थिति से गणना पर कोई फ़र्क नहीं पड़ता, और सीमा से ऊँची सामग्री भी लगातार वही 240-पॉइंट परिणाम देती है। ज्यामिति परिवर्तन पर Apple के दिशानिर्देश।
ऊपर की ओर राउंड करने से जानबूझकर ऐसा व्यूपोर्ट तय नहीं होता जो मापी गई सामग्री से थोड़ा छोटा हो। शुरुआती 240-पॉइंट स्थिति, माप आने से पहले उपयोगी व्यूपोर्ट देती है; यह शुरुआती मान है, सामग्री की मापी गई ऊँचाई नहीं। कॉलबैक बाद में इसकी जगह मापी गई वैल्यू रख देता है।
चौड़ाई और मॉडिफ़ायर का क्रम क्यों महत्वपूर्ण है
क्षैतिज आकार को लचीला रखें। उदाहरण में .fixedSize(horizontal: false, vertical: true) का उपयोग किया गया है, ताकि लेबल उपलब्ध चौड़ाई का उपयोग करते हुए अपना आदर्श ऊर्ध्वाधर आकार बनाए रखें। Apple चेतावनी देता है कि किसी आदर्श आयाम को निश्चित करने से व्यू अपने पैरेंट की सीमाओं से बाहर जा सकता है; इसलिए संकरे पैनल में पंक्तियों में लिपटने वाले लेबल के लिए दोनों आयामों को निश्चित करना उपयुक्त डिफ़ॉल्ट नहीं है। Apple का fixedSize(horizontal:vertical:) दस्तावेज़।
12-पॉइंट पैडिंग माप मॉडिफ़ायर से पहले आती है, इसलिए वह सामग्री की मापी गई ऊँचाई में शामिल होती है। बॉर्डर बाहरी ऊँचाई तय करने के बाद आता है और व्यूपोर्ट की सीमा दिखाता है। इस क्रम से यह देखना आसान होता है कि खाली जगह स्क्रॉलिंग पैनल का हिस्सा है या आसपास की विंडो का।
छोटी सामग्री, टेक्स्ट रैपिंग और सिकुड़ने की जाँच करें
उदाहरण में व्यवहार जाँचने के लिए और पैनल को अपने ऐप के वास्तविक कंटेनर में रखने के बाद भी इस क्रम का उपयोग करें:
Show more labelsको बंद रखकर ऐप लॉन्च करें। बॉर्डर में दो लेबल और उनकी पैडिंग घिरे होने चाहिए, और कैप्शन ठीक नीचे दिखना चाहिए।Show more labelsचालू करें। पैनल बढ़कर 240-पॉइंट की सीमा तक पहुँचना चाहिए। स्क्रॉल करके पुष्टि करें कि आखिरी लेबल तक पहुँचा जा सकता है।- विंडो को संकरा और चौड़ा करें। पंक्तियों में लिपटे लेबल की ऊँचाई बदलनी चाहिए और उनकी आखिरी पंक्तियाँ दिखाई देती रहनी चाहिए।
- टॉगल फिर बंद करें। पैनल वापस छोटी सामग्री की ऊँचाई तक सिकुड़ना चाहिए।
- छोटी ऐरे को अस्थायी रूप से
[]में बदलें। स्पष्टNo labels to displayसंदेश से सामग्री की ऊँचाई तय होनी चाहिए। - अपने वास्तविक पैनल में इस्तेमाल होने वाली टेक्स्ट शैलियों और आसपास के कंट्रोल के साथ फिर जाँचें।
खाली स्थिति जानबूझकर चुना गया इंटरफ़ेस व्यवहार है: ऐरे खाली होने पर भी यह उदाहरण संदेश और पैडिंग बनाए रखता है। अगर पैनल को पूरी तरह हटाना है, तो लेबल न होने पर पूरे स्क्रॉल व्यू को शर्त के आधार पर हटाएँ। यह गैर-खाली पैनल का माप लेने से अलग उत्पाद निर्णय है।
आसपास की उदाहरण विंडो अपनी न्यूनतम ऊँचाई बनाए रखती है। कोड लेबल के व्यूपोर्ट का आकार तय करता है; लेबल हटने पर यह Mac विंडो को सिकुड़ने के लिए नहीं कहता। कैप्शन के नीचे की कोई भी जगह बाहरी लेआउट की है, बॉर्डर वाले स्क्रॉल व्यू की नहीं।
बची हुई खाली जगह या कटे हुए लेबल की वजह पता करें
अगर पैनल लंबा बना रहता है, तो पहले देखें कि माप मॉडिफ़ायर कहाँ लगाया गया है। इसे भीतरी पैडिंग के बाद कंटेंट स्टैक को देखना चाहिए। फिर पैरेंट व्यू में कोई निश्चित ऊँचाई या न्यूनतम ऊँचाई देखें, जो पैनल के आसपास जगह आरक्षित करती हो। Apple के दस्तावेज़ के अनुसार, फ़्रेम की दी गई ऊँचाई एक निश्चित आयाम होती है, इसलिए अलग बाहरी फ़्रेम भीतरी व्यूपोर्ट के सिकुड़ने पर भी जगह बनाए रख सकता है। SwiftUI फ़्रेम का आकार तय करना।
अगर सामग्री खुद अनपेक्षित रूप से लंबी बनी रहती है, तो मापे जा रहे व्यू समूह से ऊर्ध्वाधर विस्तार हटाएँ। इस छोटे-लेबल पैटर्न में वर्टिकल स्पेसर या ऐसा कंटेंट फ़्रेम न रखें जिसकी न्यूनतम ऊँचाई व्यूपोर्ट से निकाली गई हो। ऐसे विकल्प सामग्री की ऊँचाई को व्यूपोर्ट पर निर्भर बनाते हैं, जिससे स्वतंत्र माप में बाधा आती है जिसकी आकार तय करने वाले नियम को ज़रूरत है।
अगर नीचे की पैडिंग कट रही है, तो पुष्टि करें कि माप से पहले पैडिंग शामिल है। अगर टेक्स्ट ऐप में जोड़ने के बाद अलग तरह से लिपटता है, तो देखें कि नया पैरेंट वास्तव में कितनी चौड़ाई दे रहा है। एक चौड़ाई पर मापी गई ऊँचाई को दूसरी चौड़ाई पर स्थायी ऊँचाई न मानें।
240-पॉइंट की सीमा को आसपास के इंटरफ़ेस के अनुसार रखें। नमूने में विंडो की न्यूनतम ऊँचाई उसके कंट्रोल, पैडिंग, कैप्शन और सीमा तक बढ़े पैनल के लिए पर्याप्त है। छोटे कंटेनर में कम ऊँचाई का बजट या अलग आसपास का लेआउट चाहिए; यह निश्चित सीमा दूसरे कंट्रोल के नीचे बची जगह की गणना अपने आप नहीं करती।
यह तरीका कम संख्या में ऐसे लेबल के लिए इस्तेमाल करें जिनका लेआउट एक साथ तैयार होता है। Apple LazyVStack को ऐसा बताता है जो स्क्रीन पर रेंडरिंग के लिए ज़रूरत पड़ने पर ही आइटम बनाता है। इसलिए यह न मानें कि भीतरी स्टैक को लेज़ी स्टैक से बदलने पर पूरी सामग्री का माप लेने की यही रणनीति बनी रहेगी। बड़े संग्रह के लिए, लेज़ी सामग्री वाला स्थिर व्यूपोर्ट आम तौर पर अधिक उपयुक्त डिज़ाइन है। Apple का LazyVStack दस्तावेज़।
AppKit और TextKit के माप कब लागू होते हैं?
इस SwiftUI लेबल पैनल के लिए रेंडर किए गए SwiftUI व्यू समूह का माप लें। AppKit से टेक्स्ट का माप लेने पर एक और लेआउट कॉन्फ़िगरेशन जुड़ जाएगा, जिसे दिखाए जा रहे लेबल से मेल खाना होगा।
अगर आपके ऐप में पहले से NSScrollView है, तो दस्तावेज़ और व्यूपोर्ट के बीच यही अंतर बनाए रखें। Apple documentView को स्क्रॉल किए जा रहे व्यू के रूप में परिभाषित करता है, जबकि contentSize स्क्रॉल व्यू के कंटेंट व्यू का आकार है। नाम कुछ भी संकेत दे, contentSize पूरे दस्तावेज़ का माप नहीं है। AppKit documentView, AppKit contentSize।
अगर मौजूदा TextKit लेआउट NSLayoutManager पर आधारित है, तो usedRect(for:) उन ग्लिफ़ का बाउंडिंग आयत देता है जिनका टेक्स्ट कंटेनर में लेआउट पहले ही हो चुका है; Apple स्पष्ट रूप से कहता है कि इसे पूछने से ग्लिफ़ नहीं बनते और न ही लेआउट होता है। जब पूरे कंटेनर का लेआउट ज़रूरी हो, तो ensureLayout(for:) वह दस्तावेज़ित मेथड है जो पहले उस लेआउट को लागू करता है। TextKit usedRect(for:), TextKit ensureLayout(for:)।
फिर भी, ग्लिफ़ का आयत पूरे टेक्स्ट व्यू का फ़्रेम नहीं होता। Apple textContainerInset के ज़रिए टेक्स्ट कंटेनर के आसपास की अतिरिक्त जगह का दस्तावेज़ देता है; साथ ही उसके भीतर लाइन-फ़्रैगमेंट पैडिंग भी होती है। ग्लिफ़ की ऊँचाई को सीधे स्क्रॉलिंग व्यूपोर्ट में रखने के बजाय वास्तविक टेक्स्ट व्यू के कॉन्फ़िगरेशन का हिसाब रखें। ये मेथड NSLayoutManager वाले तरीके का वर्णन करते हैं; हर टेक्स्ट कंपोनेंट के लिए ये सार्वभौमिक आकार-निर्धारण विधि नहीं हैं। AppKit textContainerInset।
छोटे SwiftUI लेबल पैनल के लिए व्यावहारिक उपाय है कि चौड़ाई लचीली रखें, पैडिंग वाली भीतरी सामग्री का माप लें और बाहरी व्यूपोर्ट की ऊँचाई को सीमा में रखें। छोटी और लंबी, दोनों सामग्री के साथ परिणाम जाँचें, फिर इसे वास्तविक पैरेंट लेआउट में आज़माएँ ताकि आसपास की सीमाएँ भी इसी छोटे आकार का समर्थन करें।
स्रोत
- ScrollView — Apple
- min(::) — Apple
- frame(width:height:alignment:) — Apple
- scrollBounceBehavior(_:axes:) — Apple
- fixedSize(horizontal:vertical:) — Apple
- onGeometryChange(for:of:action:) — Apple
- LazyVStack — Apple
- documentView — Apple
- contentSize — Apple
- usedRect(for:) — Apple
- ensureLayout(for:) — Apple
- textContainerInset — Apple