भाग 3: स्विफ्ट के ऑपरेटर ओवरलोडिंग और रिजल्ट बिल्डर्स के साथ एक ऑटो लेआउट डीएसएल लिखना ️

Nov 26 2022
आप इस लेख को यहां भी देख सकते हैं: पिछली बार हमने डीएसएल का मुख्य भाग बनाया था, जिससे अंतिम उपयोगकर्ता अंकगणितीय तरीके से बाधाओं को व्यक्त कर सके। इस लेख में हम इसे शामिल करने के लिए विस्तार करेंगे: सबसे पहले हम भाग 1 में परिभाषित लेआउट एंकर प्रोटोकॉल के अनुरूप एंकरों की एक जोड़ी रखने के लिए एक प्रकार बनाते हैं।

आप इस लेख को यहां भी देख सकते हैं:

पिछली बार हमने डीएसएल का मुख्य भाग बनाया था, जिससे अंतिम उपयोगकर्ता अंकगणितीय फैशन में बाधाओं को व्यक्त कर सके।

इस लेख में हम इसे शामिल करने के लिए इसका विस्तार करेंगे:

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

सबसे पहले हम भाग 1 में परिभाषित लेआउट एंकर प्रोटोकॉल के अनुरूप एंकरों की एक जोड़ी रखने के लिए एक प्रकार बनाते हैं।

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

इसके 2 नुकसान हैं:

  • बुनियादी एंकरों के साथ एकल भाव भी बाधाओं की एक सरणी लौटाएगा। उपयोगकर्ताओं के दृष्टिकोण से DSL को जटिल बनाता है।
  • एक उपयोगकर्ता एक एकल एंकर के साथ एक समग्र एंकर (2 या 4 एंकर के साथ) का प्रयास कर सकता है। अतिरिक्त एंकरों की उपेक्षा करके हम इसे संभाल सकते हैं। कंपाइलर कोई चेतावनी नहीं देगा। हालाँकि, ये संचालन और परिणामी बाधाएँ अर्थहीन होंगी। यह अंतिम उपयोगकर्ता कोड में निराशाजनक बग पेश करने की क्षमता रखता है - कुछ ऐसा जिससे हम बचना चाहते हैं!!

चरण 11: LayoutAnchorPair प्रोटोकॉल का विस्तार करें।

यह एक्सटेंशन उसी उद्देश्य को पूरा करेगा जैसा पहले परिभाषित लेआउट एंकर प्रोटोकॉल एक्सटेंशन - यह एक रैपर के रूप में कार्य करता है जो आधार प्रकार के तरीकों को कॉल करता है और परिणामी बाधाओं को वापस करता है।

यहां मुख्य अंतर यह है कि प्रत्येक विधि बाधाओं की एक सरणी लौटाती है, जो 2 लेआउट एंकर प्रकार की बाधाओं को जोड़ती है।

चूँकि LayoutAnchorPair को हम जो एंकर पास करते हैं, वे LayoutAnchor प्रकार के लिए विवश हैं, हम आसानी से एक डिफ़ॉल्ट कार्यान्वयन प्रदान कर सकते हैं।

इसके अतिरिक्त, स्थिरांक के बजाय, ये विधियाँ एक EdgeInsetPair लेती हैं जो प्रत्येक बाधा के लिए अलग-अलग स्थिरांक प्रदान करने की क्षमता प्रदान करेगी।

एंकर 1 और कॉन्स्टेंट 2 को एंकर 2 पर सीमित करने के लिए प्रत्येक विधि निरंतर 1 को मैप करती है।

चरण 13: कंक्रीट लेआउट एंकर प्रकार बनाएँ।

भाग 1 और 2 में हमें ठोस लेआउट एंकर प्रकार बनाने की ज़रूरत नहीं थी क्योंकि हमने अभी डिफ़ॉल्ट NSLayoutAnchors को LayoutAnchor प्रोटोकॉल के अनुरूप बनाया है। यहां हालांकि, हमें अपने स्वयं के एंकर प्रदान करने की आवश्यकता है जो एंकरपेयर प्रोटोकॉल के अनुरूप हों।

पहले इस्तेमाल किए गए टाइपेलियास का रिमाइंडर:

हम एक नेस्टेड प्रकार को परिभाषित करते हैं जो EdgeInsetPair प्रोटोकॉल को संतुष्ट करता है। यह AnchorPair संबद्ध प्रकार की आवश्यकता - इनसेट को संतुष्ट करता है। इनसेट सेट करने के लिए ऑपरेशन ओवरलोडिंग के दौरान इस प्रोटोकॉल का पालन करने वाले ठोस प्रकार का उपयोग किया जाएगा।

हम LayoutAnchorPair और EdgeInsetPair प्रोटोकॉल के अनुरूप गणना की गई संपत्तियों का उपयोग करते हैं। ये संगणित विशेषताएँ LayoutAnchorPair और EdgeInsetPair की आंतरिक विशेषताएँ लौटाती हैं।

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

यह "जेनेरिक" प्रोटोकॉल हमें एक प्रोटोकॉल एक्सटेंशन को परिभाषित करने की अनुमति देता है जो सभी एंकर जोड़े के लिए मान्य है। बशर्ते हम ऊपर चर्चा किए गए नियम का पालन करें। साथ ही हम विस्तार के बाहर अधिक सार्थक एंकर विशिष्ट लेबल - नीचे / ऊपर - का उपयोग कर सकते हैं। जैसे ऑपरेटर ओवरलोड को परिभाषित करते समय।

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

यदि आप मानते हैं कि एक अलग डिजाइन इष्टतम होगा तो कृपया एक टिप्पणी छोड़ दें।

अधिक वास्तु निर्णय बिंदु:

  • इनसेट एक अलग प्रकार का होना चाहिए क्योंकि वे आरंभिक होते हैं और एंकर के बाहर उपयोग किए जाते हैं।
  • नेस्टेड प्रकारों का उपयोग नेमस्पेस की सुरक्षा और इसे साफ रखने के लिए किया जाता है, साथ ही इस तथ्य को भी उजागर किया जाता है कि इनसेट प्रकार का कार्यान्वयन विशिष्ट PairAnchor कार्यान्वयन पर निर्भर करता है।

नोट: इनसेट जोड़ना किसी व्यंजक में स्थिरांक जोड़ने के समान नहीं है।

आपने देखा होगा कि एजपेयर इंटरफ़ेस के हिस्से के रूप में इसे वापस करने से पहले हमने शीर्ष स्थिरांक में एक माइनस ऑपरेटर जोड़ा था। इसी तरह, XAxisAnchorPair कार्यान्वयन अनुगामी एंकर में एक माइनस जोड़ता है। स्थिरांकों को उलटने का अर्थ है कि इनसेट प्रत्येक किनारे को एक ही दिशा में स्थानांतरित करने के बजाय इनसेट के रूप में कार्य करेगा।

छवि एक और दो में लाल दृश्य 200 तक सीमित है।

बाईं छवि में, ब्लू व्यू के सभी एज एंकर्स को रेड व्यू प्लस 40 के बराबर सेट किया गया है। जिसके परिणामस्वरूप ब्लू व्यू का आकार समान है लेकिन दोनों अक्षों पर 40 से शिफ्ट हो गया है। जबकि यह बाधाओं के संदर्भ में समझ में आता है, यह अपने आप में एक सामान्य ऑपरेशन नहीं है।

किसी दृश्य के चारों ओर इनसेट सेट करना या किसी दृश्य के चारों ओर पैडिंग जोड़ना कहीं अधिक सामान्य है। इसलिए संयुक्त अभिनेताओं के लिए एक एपीआई प्रदान करने के संदर्भ में और अधिक समझ में आता है।

सही छवि में, हम इस डीएसएल का उपयोग करके नीले दृश्य को लाल दृश्य और 40 के इनसेट के बराबर सेट करते हैं। यह मेरे ऊपर वर्णित स्थिरांक के व्युत्क्रम को प्राप्त करता है।

चरण 14: कंपोजिट एंकर को इनिशियलाइज़ करने के लिए View & LayoutGuide को एक्सटेंड करें।

जैसा कि हमने पहले किया था, हम व्यू और लेआउटगाइड प्रकारों को संगणित गुणों के साथ विस्तारित करते हैं जो कॉल किए जाने पर लेआउटब्लॉक को इनिशियलाइज़ करते हैं।

चरण 15: एज इनसेट के साथ भावों को अनुमति देने के लिए +/- ऑपरेटरों को ओवरलोड करें।

हॉरिजॉन्टल एज और वर्टिकल एज एंकर के लिए हम चाहते हैं कि उपयोगकर्ता इनसेट निर्दिष्ट करने में सक्षम हो। इसे प्राप्त करने के लिए हम UIEdgeInsets प्रकार का विस्तार करते हैं क्योंकि यह इस DSL के अधिकांश उपयोगकर्ताओं के लिए पहले से ही परिचित है।

एक्सटेंशन सिर्फ ऊपर/नीचे या बाएं/दाएं इनसेट के साथ आरंभीकरण की अनुमति देता है - बाकी 0 पर डिफ़ॉल्ट।

हमें edgeInsets को Store करने के लिए LayoutBlock में एक नई संपत्ति जोड़ने की भी आवश्यकता है ।

इसके बाद हम इनपुट के लिए ऑपरेटरों को ओवरलोड करते हैं: लेआउटब्लॉक UIEdgeInsets के साथ ।

हम कंक्रीट LayoutAnchorPair के हिस्से के रूप में परिभाषित प्रासंगिक नेस्टेड प्रकार के लिए उपयोगकर्ता द्वारा प्रदान किए गए UIEdgeInsets के उदाहरण को मैप करते हैं

UIEdgeInset प्रकार के भाग के रूप में उपयोगकर्ता द्वारा पास किए गए अतिरिक्त या गलत पैरामाटर्स को अनदेखा कर दिया जाएगा।

चरण 15: बाधा संबंधों को परिभाषित करने के लिए तुलना ऑपरेटरों को अधिभारित करें।

सिद्धांत पहले जैसा ही रहता है। हम LayoutBlocks और LayoutAnchorPair इनपुट के लिए रिलेशनल ऑपरेटर्स को ओवरलोड करते हैं।

यदि कोई उपयोगकर्ता एज इनसेट की एक जोड़ी प्रदान करता है - हम उनका उपयोग करते हैं, अन्यथा हम लेआउटब्लॉक्स स्थिरांक से जेनेरिक इनसेट की एक जोड़ी उत्पन्न करते हैं। जेनेरिक इनसेट स्ट्रक्चर एक रैपर है, अन्य इनसेट के विपरीत यह किसी एक पक्ष को नकारता नहीं है।

चरण 16: आयाम लेआउट एंकरपेयर

एकल आयाम एंकर की तरह, आयाम एंकरों की एक जोड़ी - चौड़ाई एंकर और ऊंचाई एंकर - को स्थिरांक तक सीमित किया जा सकता है। इसलिए, हमें इस उपयोग के मामले को संभालने के लिए अलग-अलग ऑपरेटर ओवरलोड प्रदान करना होगा।

  • DSL के उपयोगकर्ता को ऊँचाई और चौड़ाई दोनों को एक ही स्थिरांक पर ठीक करने की अनुमति दें - एक वर्ग बनाना।
  • DSL के उपयोगकर्ता को SizeAnchorPair को CGSize प्रकार में ठीक करने की अनुमति दें - ज्यादातर मामलों में अधिक समझ में आता है क्योंकि दृश्य वर्ग नहीं होते हैं।

चरण 17: [NSLayoutConstraint] और NSLayoutConstraint प्रकारों को संभालने के लिए परिणाम बिल्डर्स का उपयोग करना।

समग्र एंकर एक दिलचस्प समस्या पैदा करते हैं। अभिव्यक्ति में इन एंकरों का उपयोग करने से बाधाओं की एक सरणी होती है। यह DSL के अंतिम उपयोगकर्ता के लिए गड़बड़ हो सकता है।

हम बॉयलरप्लेट कोड के बिना इन बाधाओं को एक साथ बैचने का एक तरीका प्रदान करना चाहते हैं और उन्हें प्रभावी ढंग से सक्रिय करना चाहते हैं - अलग-अलग या अलग-अलग बैचों में एक बार में।

स्विफ्ट 5.4 परिणाम बिल्डरों (जिसे फ़ंक्शन बिल्डर्स के रूप में भी जाना जाता है) में प्रस्तुत किया गया है, जो आपको घटकों की एक श्रृंखला से निहित रूप से 'बिल्ड ब्लॉक' का उपयोग करके परिणाम बनाने की अनुमति देता है। वास्तव में, वे स्विफ्ट यूआई के पीछे अंतर्निहित बिल्डिंग ब्लॉक हैं।

इस DSL में अंतिम परिणाम NSLayoutConstraint ऑब्जेक्ट की एक सरणी है।

हम बिल्ड फ़ंक्शंस प्रदान करते हैं, जो परिणाम बिल्डर को व्यक्तिगत बाधाओं और बाधाओं के सरणियों को एक सरणी में हल करने की अनुमति देते हैं। यह सारा तर्क DSL के अंतिम उपयोगकर्ता से छिपा हुआ है।

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

यह सब परिणाम निम्नलिखित में डालने पर:

परिणाम बिल्डर्स हमें क्लोजर के भीतर अतिरिक्त नियंत्रण प्रवाह को शामिल करने की अनुमति भी देते हैं, जो कि एक सरणी के साथ संभव नहीं होगा।

बस इतना ही, पढ़ने के लिए धन्यवाद! इस लेख को लिखने में कई दिन लगे - इसलिए यदि आपने कुछ नया सीखा है तो मैं इस रेपो पर ⭐ की सराहना करूँगा!

यदि आपके पास मेरे लिए कोई सलाह है, तो शर्माएं नहीं: और अपना अनुभव साझा करें!

इस डीएसएल के अंतिम संस्करण में एक चार आयामी एंकर शामिल है जो एंकरपेयर प्रकार की एक जोड़ी से बना है ...

आप यहां सभी कोड पा सकते हैं: