पहलू-अनुपात वाले SwiftUI प्रीव्यू के साथ साइड कंट्रोल्स का मिलान करने के लिए, उपलब्ध विंडो स्पेस से फिट किया गया प्रीव्यू आकार निकालें, फिर कंट्रोल्स को उतनी ही ऊँचाई दें। macOS का यह उदाहरण सिंथेटिक प्रीव्यू को समानुपाती रखता है और कंट्रोल्स अधिक होने पर उन्हें स्क्रॉल करने देता है।
काम एक ऐसा डेस्कटॉप एडिटर बनाना है जिसका आकार बदला जा सके, जिसमें बाईं ओर प्रीव्यू और दाईं ओर निश्चित चौड़ाई वाला कंट्रोल्स पैनल हो। यहाँ “प्रीव्यू” का मतलब आपके चल रहे ऐप के भीतर की सामग्री है। लक्ष्य है कि विंडो का आकार बदलने पर दोनों पैनलों के ऊपर और नीचे के लेआउट किनारे एक सीध में रहें।
कंट्रोल्स को किस ऊँचाई के अनुसार रखना चाहिए?
पहले उस आयत को परिभाषित करें जिससे आप मिलान करना चाहते हैं। पहलू-अनुपात के अनुसार फिट किया गया प्रीव्यू अपने आसपास के कंटेनर से कम जगह घेर सकता है। यदि कंट्रोल्स कंटेनर की ऊँचाई का अनुसरण करें, तो दोनों दृश्य सही ढंग से व्यवस्थित होने पर भी वे दिखाई देने वाले प्रीव्यू से नीचे तक फैल सकते हैं।
Apple पहलू-अनुपात को चौड़ाई भागा ऊँचाई के रूप में परिभाषित करता है। आकार बदल सकने वाली सामग्री के लिए, .fit के साथ aspectRatio(_:contentMode:) प्रस्तावित जगह के भीतर सामग्री को फिट करते हुए उस अनुपात को बनाए रखता है। दस्तावेज़ों का दीर्घवृत्त वाला उदाहरण दिखाता है कि फिट किए गए आकार और उसे घेरने वाले वर्गाकार फ़्रेम के आयाम अलग-अलग क्यों हो सकते हैं। Apple के पहलू-अनुपात संबंधी दस्तावेज़।
यह कार्यान्वयन फिट किए गए आयत को स्पष्ट रूप से तय करता है। यह कंट्रोल्स के लिए एक निश्चित चौड़ाई और पैनलों के बीच एक अंतर रखता है, फिर प्रीव्यू को बची हुई चौड़ाई और उपलब्ध ऊँचाई में फिट करता है। दोनों पैनलों को गणना की गई प्रीव्यू ऊँचाई मिलती है।
इस उदाहरण में यह एक लेआउट नीति है: प्रीव्यू, कंट्रोल्स व्यूपोर्ट की ऊँचाई तय करता है। कंट्रोल्स की लंबी सूची प्रीव्यू को लंबा करने के बजाय उसी व्यूपोर्ट के भीतर स्क्रॉल होती है। Apple ScrollView को ऐसा दृश्य बताता है जो स्क्रॉल किए जा सकने वाले क्षेत्र में अपनी अंतर्निहित सामग्री का चलायमान हिस्सा दिखाता है। Apple के स्क्रॉल-व्यू संबंधी दस्तावेज़।
फिट किया गया आयत एक बार निकालें
मान लें कि बाहरी पैडिंग के बाद उपलब्ध कंटेंट आयाम W और H हैं, कंट्रोल्स की चौड़ाई C है, अंतर G है, और प्रीव्यू का चौड़ाई-से-ऊँचाई अनुपात R है। गणना इस प्रकार है:
remainingWidth = max(0, W - C - G)
previewWidth = min(remainingWidth, H * R)
previewHeight = previewWidth / R
controlsHeight = previewHeight
पहली सीमा प्रीव्यू को कंट्रोल्स की क्षैतिज जगह इस्तेमाल करने से रोकती है। दूसरी सीमा चौड़ी विंडो के कारण प्रीव्यू को उपलब्ध क्षेत्र से अधिक ऊँचा होने से रोकती है। चुनी गई चौड़ाई से ऊँचाई निकालने पर दोनों स्थितियों में अनुपात बना रहता है।
उदाहरण के लिए, मान लें कि पैडिंग से पहले कंटेंट क्षेत्र 960 गुणा 600 पॉइंट का है। हर किनारे पर 16 पॉइंट पैडिंग, 220 पॉइंट का कंट्रोल्स पैनल, 16 पॉइंट का अंतर और 16:9 प्रीव्यू होने पर उपलब्ध क्षेत्र 928 गुणा 568 पॉइंट का होगा। प्रीव्यू की बची हुई चौड़ाई 692 पॉइंट होगी, जिससे प्रीव्यू की ऊँचाई 389.25 पॉइंट बनेगी। इसलिए कंट्रोल्स व्यूपोर्ट 220 गुणा 389.25 पॉइंट का होगा।
ये उदाहरण के लिए लिए गए इनपुट हैं, ऐप के मापे गए परिणाम नहीं। वे यह भी दिखाते हैं कि कंट्रोल्स को उपलब्ध पूरी 568 पॉइंट ऊँचाई से मिलाने पर संरेखण गलत क्यों होगा।
लेआउट आयामों के लिए CGFloat और धनात्मक, सीमित अनुपात का उपयोग करें। Swift में let ratio: CGFloat = 16.0 / 9.0 अपेक्षित फ़्लोटिंग-पॉइंट गणना को स्पष्ट करता है। यदि आपका ऐप बाद में कॉन्फ़िगर किए जा सकने वाले अनुपात स्वीकार करता है, तो भाग देने से पहले उन्हें मान्य करें।
पूरा macOS उदाहरण बनाएँ
Xcode में macOS SwiftUI ऐप बनाएँ और उसका डिप्लॉयमेंट लक्ष्य macOS 13 या बाद का सेट करें। जनरेट किए गए ऐप और कंटेंट-व्यू घोषणाओं को इस एक फ़ाइल से बदलें और टार्गेट में ठीक एक @main घोषणा रखें। इस उदाहरण के लिए किसी एसेट या बाहरी पैकेज की ज़रूरत नहीं है।
यह डिप्लॉयमेंट आवश्यकता यहाँ इस्तेमाल किए गए विंडो-आकार API के कारण है। सीन .contentMinSize का उपयोग करता है, जिसे Apple ऐसे दस्तावेज़ित करता है कि यह सामग्री के आधार पर विंडो का न्यूनतम आकार तय करता है और अधिकतम आकार की सीमा नहीं लगाता। Apple के विंडो-रिसाइज़ेबिलिटी संबंधी दस्तावेज़, Apple के न्यूनतम कंटेंट आकार संबंधी दस्तावेज़।
import SwiftUI
@main
struct MatchedPreviewApp: App {
var body: some Scene {
WindowGroup {
PreviewEditor()
.frame(minWidth: 640, minHeight: 360)
}
.windowResizability(.contentMinSize)
}
}
struct PreviewEditor: View {
@State private var intensity: Double = 0.5
@State private var showsLabel = true
private let ratio: CGFloat = 16.0 / 9.0
private let controlsWidth: CGFloat = 220
private let gap: CGFloat = 16
var body: some View {
GeometryReader { geometry in
let availableHeight = max(0, geometry.size.height)
let remainingWidth = max(
0,
geometry.size.width - controlsWidth - gap
)
let previewWidth = min(
remainingWidth,
availableHeight * ratio
)
let previewHeight = previewWidth / ratio
HStack(alignment: .top, spacing: gap) {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue.opacity(0.2 + intensity * 0.7))
.aspectRatio(ratio, contentMode: .fit)
.overlay {
Text("Sample composition")
.font(.headline)
.opacity(showsLabel ? 1 : 0)
}
.frame(
width: previewWidth,
height: previewHeight
)
ScrollView(.vertical) {
VStack(alignment: .leading, spacing: 16) {
Text("Preview controls")
.font(.headline)
Toggle("Show label", isOn: $showsLabel)
VStack(alignment: .leading, spacing: 8) {
Text("Color intensity")
Slider(value: $intensity, in: 0...1)
}
ForEach(1...8, id: \.self) { index in
Text("Sample setting \(index)")
.font(.caption)
}
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(
width: controlsWidth,
height: previewHeight
)
.background(Color.gray.opacity(0.08))
}
.frame(
width: geometry.size.width,
height: geometry.size.height,
alignment: .center
)
}
.padding(16)
}
}
GeometryReader एडिटर को आवंटित क्षेत्र का आकार देता है। Apple के अनुसार यह अपने आकार और निर्देशांक-स्थान से सामग्री निर्धारित करता है तथा इसका पसंदीदा आकार लचीला होता है। पैडिंग को रीडर के बाहर रखने से गणना में भीतर के इनसेट वाला कंटेंट क्षेत्र इस्तेमाल होता है। Apple के geometry-reader संबंधी दस्तावेज़।
दोनों निश्चित फ़्रेम साझा ऊँचाई तय करते हैं। Apple के अनुसार frame(width:height:alignment:) दिए गए आयामों वाला एक अदृश्य फ़्रेम बनाता है। कंट्रोल्स की पृष्ठभूमि उस फ़्रेम के बाद आती है, इसलिए यह कंट्रोल्स व्यूपोर्ट को ढकती है, जिसमें छोटी सूची के नीचे की खाली जगह भी शामिल है। Apple के निश्चित-फ़्रेम संबंधी दस्तावेज़।
आख़िरी फ़्रेम पूरी पंक्ति को रीडर के बीच में रखता है। यह किसी भी पैनल को बड़ा नहीं करता। इसलिए लंबी विंडो में पंक्ति के ऊपर और नीचे खाली जगह रह सकती है, जबकि कंट्रोल्स फिर भी प्रीव्यू से मेल खाते हैं।
आकार बदलते समय स्टेट और लाइफ़साइकल व्यवहार बनाए रखें
उपयोगकर्ता द्वारा बदले जा सकने वाले मानों को गणना की गई ज्यामिति से अलग रखें। उदाहरण में, intensity और showsLabel स्टेट हैं; प्रीव्यू की चौड़ाई और ऊँचाई मौजूदा उपलब्ध आकार से निकाले गए स्थानीय मान हैं।
Apple स्टेट को SwiftUI द्वारा प्रबंधित ऐसा स्टोरेज बताता है जिसके बदलाव व्यू हाइरार्की के निर्भर हिस्सों को अपडेट करते हैं। इससे स्लाइडर और टॉगल के मान उस एडिटर में रखना उचित होता है जो उनका स्वामी है। Apple के स्टेट संबंधी दस्तावेज़।
लेआउट गणना में स्टेट असाइनमेंट, अपीयरेंस कॉलबैक, असिंक्रोनस काम या मापन फ़ीडबैक नहीं है। आकार बदलने से उसी व्यू संरचना में इस्तेमाल होने वाले आयाम बदलते हैं। लेबल मौजूद रहता है और टॉगल बदलने पर उसकी अपारदर्शिता बदलती है।
इसे किसी मौजूदा एडिटर में अपनाते समय उसके मॉडल स्वामित्व और बाइंडिंग प्रकार बनाए रखें। आकार तय करने की नीति प्रीव्यू और कंट्रोल्स के आसपास लागू करें; मॉडल बनाना ज्यामिति की गणना में न ले जाएँ। इस उदाहरण की गणना के लिए केवल उपलब्ध आयाम और चुना हुआ अनुपात चाहिए।
आकार बदलने, ओवरफ़्लो और मॉडिफ़ायर क्रम की जाँच करें
गणना की जाँच के लिए नीचे दिए आयामों का उपयोग करें। ये उदाहरण की बाहरी पैडिंग से पहले के कंटेंट क्षेत्र को दर्शाते हैं, विंडो के बाहरी फ़्रेम को नहीं। अपेक्षित मान दो दशमलव स्थानों तक राउंड किए गए हैं।
| कंटेंट क्षेत्र | प्रीव्यू आकार | कंट्रोल्स व्यूपोर्ट |
|---|---|---|
| 960 × 600 पॉइंट | 692 × 389.25 | 220 × 389.25 |
| 640 × 360 पॉइंट | 372 × 209.25 | 220 × 209.25 |
| 1200 × 400 पॉइंट | 654.22 × 368 | 220 × 368 |
| 700 × 900 पॉइंट | 432 × 243 | 220 × 243 |
अंकगणित की जाँच एक छोटी Python गणना से की गई, जिसमें यह assert करना शामिल था कि फिट किया गया प्रीव्यू उपलब्ध ऊँचाई के भीतर रहता है और दोनों पैनल तथा उनके बीच का अंतर क्षैतिज रूप से उपलब्ध जगह में समा जाते हैं। शोध परिवेश में Swift और Xcode उपलब्ध नहीं थे, इसलिए पूरे SwiftUI प्रोग्राम की समीक्षा की गई, लेकिन उसे कंपाइल या चलाया नहीं गया।
Xcode में ऐप चलाएँ और चौड़ी, कम ऊँचाई वाली तथा लंबी आकृतियों के बीच विंडो का आकार बदलें। पुष्टि करें कि पैनल के किनारे एक सीध में रहते हैं, कंटेंट व्यूपोर्ट से अधिक होने पर कंट्रोल्स स्क्रॉल होते हैं, और आकार बदलने के दौरान स्लाइडर तथा टॉगल अपने मान बनाए रखते हैं।
यदि उदाहरण अपनाने के बाद भी संरेखण अलग दिखे, तो इन स्थितियों की जाँच करें:
- पैनल की अतिरिक्त पैडिंग: किसी एक पैनल के निश्चित फ़्रेम के बाहर लगाई गई पैडिंग उसके कुल आकार को बढ़ाती है। कंट्रोल्स की पैडिंग को स्क्रॉल व्यू के भीतर रखें, जैसा दिखाया गया है।
- प्रीव्यू का बड़ा रैपर: यदि आप प्रीव्यू के चारों ओर बड़ा फ़्रेम जोड़ते हैं, तो तय करें कि कंट्रोल्स उस रैपर से मेल खाएँ या फिट की गई सामग्री से। यहाँ की गणना फिट किए गए आयत से मेल खाती है।
- कस्टम कंटेंट का ओवरफ़्लो: फ़्रेम लेआउट आयाम तय करता है, लेकिन उसके बाहर ड्रॉ होने वाली सामग्री को अपने आप छिपाता नहीं है। Apple आयताकार क्लिपिंग के लिए
.clipped()को दस्तावेज़ित करता है; जब प्रीव्यू का जानबूझकर होने वाला ओवरफ़्लो छिपाना हो, तब इसका उपयोग करें। सभी सेटिंग्स तक पहुँच बनाए रखने के लिए कंट्रोल्स को स्क्रॉल करने योग्य रखें। Apple के क्लिपिंग संबंधी दस्तावेज़। - होस्ट में अपर्याप्त जगह: उदाहरण अपने न्यूनतम कंटेंट आकार पर निर्भर करता है। यदि इसे इससे छोटे होस्ट में एम्बेड किया जाए, तो कंट्रोल्स की निश्चित चौड़ाई प्रीव्यू के लिए उपयोगी चौड़ाई नहीं छोड़ सकती। उस होस्ट के लिए छोटा पैनल, ऊर्ध्वाधर व्यवस्था या स्पष्ट न्यूनतम आकार चुनें।
कस्टम लेआउट का उपयोग कब करना चाहिए?
यह तरीका उस एडिटर के लिए उपयुक्त है जिसे सीमित विंडो क्षेत्र मिलता है और जो ज्ञात पहलू-अनुपात का उपयोग करता है। इसकी आकार-निर्धारण नीति स्पष्ट है और यह प्रीव्यू की आंतरिक सामग्री को मापने पर निर्भर नहीं करती।
यदि आपके पुनः उपयोग किए जा सकने वाले कंपोनेंट को अपने बच्चों से पसंदीदा आकार तय करना हो, तो कस्टम Layout पर विचार करें। Apple का दस्तावेज़ित अनुबंध sizeThatFits(proposal:subviews:cache:) के ज़रिए संयुक्त आकार बताने और placeSubviews(in:proposal:subviews:cache:) के ज़रिए बच्चों की स्थिति तय करने को अलग रखता है। सबव्यू प्रॉक्सी मापन और स्थान-निर्धारण के ऑपरेशन देती हैं। Apple के लेआउट संबंधी दस्तावेज़।
यहाँ दिखाए गए सीमित डेस्कटॉप एडिटर के लिए साझा फिट किया गया आयत पर्याप्त है: कंट्रोल्स के लिए क्षैतिज जगह रखें, प्रीव्यू को उपलब्ध दोनों आयामों के अनुसार फिट करें और उसकी ऊँचाई कंट्रोल्स व्यूपोर्ट को दें। इससे आकार बदलने का एक स्पष्ट नियम मिलता है और कंट्रोल्स की सामग्री स्क्रॉल की जा सकती है।
स्रोत
- aspectRatio(_:contentMode:) — Apple डेवलपर दस्तावेज़।
- ScrollView — Apple डेवलपर दस्तावेज़।
- windowResizability(_:) — Apple डेवलपर दस्तावेज़।
- contentMinSize — Apple डेवलपर दस्तावेज़।
- GeometryReader — Apple डेवलपर दस्तावेज़।
- frame(width:height:alignment:) — Apple डेवलपर दस्तावेज़।
- State — Apple डेवलपर दस्तावेज़।
- clipped(antialiased:) — Apple डेवलपर दस्तावेज़।
- Layout — Apple डेवलपर दस्तावेज़।