जब मैं लेआउट बाधाओं को सक्रिय / निष्क्रिय कर सकता हूं?


104

मैंने IB में बाधाओं के कई सेट किए हैं, और मैं कुछ राज्य के आधार पर प्रोग्राम के बीच उनके बीच टॉगल करना चाहूंगा। वहाँ एक constraintsAआउटलेट संग्रह है, जो सभी आईबी से स्थापित के रूप में चिह्नित हैं, और एक constraintsBआउटलेट संग्रह जिसमें से सभी को आईबी में अनइंस्टॉल किया गया है।

मैं प्रोग्रामेटिक रूप से दो सेटों के बीच टॉगल कर सकता हूँ जैसे:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

लेकिन ... मुझे यह पता नहीं चल सकता कि कब क्या करना है। ऐसा लगता है कि मुझे एक बार ऐसा करने में सक्षम होना चाहिए viewDidLoad, लेकिन मुझे वह काम नहीं मिल सकता है। मैंने कॉल करने की कोशिश की है view.updateConstraints()और view.layoutSubviews()बाधाओं को सेट करने के बाद, लेकिन कोई फायदा नहीं हुआ।

मुझे लगता है कि अगर मैं viewDidLayoutSubviewsअपेक्षा के अनुसार काम करता हूं, तो मैंने बाधाओं को निर्धारित किया । मुझे लगता है कि मैं दो बातें जानना चाहता हूं ...

  1. मुझे यह व्यवहार क्यों मिल रहा है?
  2. क्या viewDidLoad से बाधाओं को सक्रिय / निष्क्रिय करना संभव है?

2
क्या आपका मतलब है कि निष्क्रिय करेंकॉन्स्ट्रक्ट्स और सक्रिय करेंकॉन्स्ट्रक्ट्स ने व्यूविलेयूटआउट साक्षात्कारों में काम किया है? मैंने कोशिश की कि, और यह वहाँ या viewDidLoad में काम नहीं किया। यह देखने में काम किया। यह दृश्य दिखाई दिया कि नए अवरोधों को कहां रखा जाना चाहिए, लेकिन अगर मैंने परिदृश्य को घुमाया, तो दृश्य आईबी में निर्धारित बाधाओं द्वारा निर्धारित स्थिति में वापस चला गया (और जब मैं चित्र पर वापस घुमाया गया तो वहां रुका था)। बाधाओं को लॉग करते हुए, सही लोगों (नए सक्रिय वाले) को दिखाया। यह मुझे एक बग की तरह लगता है।
rdelmar

1
हां, वे मान्य थे (उन्होंने viewDidAppear में काम किया था), और सुपर को कॉल करने की कोई आवश्यकता नहीं है क्योंकि viewWillLayoutSubviews का कोई डिफ़ॉल्ट कार्यान्वयन नहीं है (मैंने वैसे भी सुपर कॉल करने की कोशिश की, लेकिन इससे कोई फर्क नहीं पड़ा)।
rdelmar

1
@rdelmar को बस और अधिक परीक्षण करने का मौका मिला ... मैं सत्यापित कर सकता हूं कि वास्तव में आपके द्वारा वर्णित एक ही व्यवहार मिला है ... पहले viewDidAppear में काम करता है, लेकिन फिर रोटेशन पर बदल जाता है।
tybro0103

3
स्पष्ट रूप से आप इस उद्देश्य के लिए आईबी में स्थापित नहीं होने के कारण बाधाओं को चिह्नित नहीं कर सकते। उस जानकारी को यहां मिला: stackoverflow.com/questions/27663249/… और इसने मेरे लिए समस्या हल कर दी।
स्टीफन

1
मेरे पास मेरी बाधाओं को उसी तरह लागू किया गया जैसा कि प्रश्न में वर्णित है, सिवाय इसके कि मैंने उनमें से कुछ को सक्रिय / निष्क्रिय कर दिया है। यह काम किया है, लेकिन आप तत्वों को जल्दी से स्थिति (एक मामूली लेकिन अवांछनीय मुद्दा) बदल सकते हैं। ViewWillAppear या viewDidLoad में परिवर्तन करने से काम नहीं हुआ। लेकिन इस सवाल को पढ़ने के बाद, मैंने उस बदलाव को viewDidLayoutSubviews में करने की कोशिश की। यह काम किया और स्थिति परिवर्तन अब उपयोगकर्ता को दिखाई नहीं दे रहा है। (यह viewWillLayoutSubviews में भी काम किया है)। तो उस टिप के लिए धन्यवाद!
मोरपंखी

जवाबों:


185

मैं सक्रिय और निष्क्रिय NSLayoutConstraintsमें viewDidLoad, और मैं इसके साथ कोई समस्या नहीं है। तो यह काम करता है। आपके ऐप और मेरे बीच सेटअप में अंतर होना चाहिए :-)

मैं बस अपने सेटअप का वर्णन करूँगा - शायद यह आपको एक लीड दे सकता है:

  1. मैं @IBOutletsउन सभी बाधाओं के लिए तैयार हूं , जिन्हें मुझे सक्रिय / निष्क्रिय करने की आवश्यकता है।
  2. में ViewController, मैं बाधाओं को वर्ग गुणों में बचाता हूं जो कमजोर नहीं हैं। इसका कारण यह है कि मैंने पाया कि एक बाधा को निष्क्रिय करने के बाद, मैं इसे पुन: सक्रिय नहीं कर सका - यह शून्य था। तो, इसे हटाए जाने पर हटा दिया गया लगता है।
  3. NSLayoutConstraint.deactivate/activateआप जैसा करते हैं, मैं वैसा उपयोग नहीं करता constraint.active = YES/ करती हूं NO
  4. अड़चनें खड़ी करने के बाद मैं फोन करता हूं view.layoutIfNeeded()

131
"बाधाओं को वर्ग गुणों में सहेजें जो कमजोर नहीं हैं" आपने मुझे बहुत समय बचाया, धन्यवाद!
OpenUserX03

10
"मैं बाधाओं को वर्ग के गुणों में बचाता हूं जो कमजोर नहीं हैं": इसने मुझे बहुत सारे दिल के दर्द से बचाया। मुझे नहीं पता था कि मैं एक चयनकर्ता को नील वस्तु पर बुला रहा हूं। धन्यवाद!!
स्थिर0886

4
यह ध्यान रखना महत्वपूर्ण है कि "निष्क्रिय" बाधाओं को ऑटो लेआउट द्वारा अनदेखा नहीं किया जाता है, उन्हें हटा दिया जाता है। बाधाओं को सक्रिय / निष्क्रिय करना वास्तव में उन्हें जोड़ता है और उन्हें हटा देता है। कुछ समय बाद एक विवादित ऑटो लेआउट को डिबग करने के बाद मैंने उन बाधाओं को जोड़ा, जो मैंने पहले से निर्धारित किए थे कि .active = falseजब तक मैं उन्हें सक्रिय करने के लिए सेट नहीं करता, तब तक उन्हें अनदेखा किया जाएगा।
लार्बोसा

1
कमजोर वर्ग के गुणों में बाधाओं को बचाने के लिए, ठीक नहीं है, यह बहुत समय बचाता है, मुझे इसके बिना कुछ मिश्रित परिणाम मिल रहे थे। धन्यवाद दोस्त!
मेगामैनएक्स

3
डॉकल्स डॉक कहता है: बाधा कॉल को सक्रिय या निष्क्रिय करना AddConstraint ( :) और removeConstraint ( :) इस दृष्टिकोण पर है कि इस बाधा द्वारा प्रबंधित वस्तुओं का निकटतम सामान्य पूर्वज है। AddConstraint ( :) या removeConstraint ( :) सीधे कॉल करने के बजाय इस संपत्ति का उपयोग करें । इस प्रकार ऐसा लगता है कि जब एक बाधा को निष्क्रिय किया जाता है तो उसे हटा दिया जाता है और फिर बाधक के लिए कोई मजबूत संदर्भ नहीं बचा है, जब तक कि IBOutlet मजबूत नहीं होता है। इसलिए बाधा हटा दी जाती है। IMHO यह लगभग एक बग या कम से कम बहुत अप्रत्याशित व्यवहार है।
ओलले राब

52

हो सकता है कि आप अपनी जाँच@propertiesweakstrong कर सकते हैं , के साथ बदल सकते हैं

कभी-कभी क्योंकि यह active = NOसेट है self.yourConstraint = nil, ताकि आप self.yourConstraintफिर से उपयोग नहीं कर सके ।


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

30
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

1
क्योंकि मेरा व्यू कंट्रोलर एक चाइल्ड व्यू कंट्रोलर है - इसे "doLayoutSubviews" में करना एक ही तरीका लगता है जो काम करता है !! FYI करें।
TalL

यह एकमात्र वैध उत्तर है
यूनुस एरेन गुज़ेल

@TalL क्या आपका मतलब था कि चाइल्ड व्यू कंट्रोलर पर ही अड़चनें हैं, या यह सबव्यू है?
स्टीफन

यह सबसे अच्छा एक है
ACAkgul

14

मेरा मानना ​​है कि आप जिस समस्या का सामना कर रहे हैं, वह इस वजह से है कि बाधाओं को उनके विचारों से नहीं जोड़ा जा रहा है जब तक कि AFTER viewDidLoad()को नहीं कहा जाता है। आपके पास कई विकल्प हैं:

ए) आप अपने लेआउट बाधाओं को एक IBOutlet से कनेक्ट कर सकते हैं और इन संदर्भों के द्वारा उन्हें अपने कोड में एक्सेस कर सकते हैं। चूंकि आउटलेट्स पहले से जुड़े हुए हैंviewDidLoad() किक से हुए हैं, अड़चनें सुलभ होनी चाहिए और आप उन्हें वहां सक्रिय और निष्क्रिय कर सकते हैं।

बी) यदि आप constraints()विभिन्न बाधाओं का उपयोग करने के लिए यूआईवीवाई के फ़ंक्शन का उपयोग करना चाहते हैं viewDidLayoutSubviews(), तो आपको इसे बंद करने के लिए इंतजार करना होगा और वहां से करना होगा, क्योंकि एक नीब से एक दृश्य नियंत्रक बनाने के बाद यह पहला बिंदु है कि इसमें कोई भी स्थापित बाधाएं होंगी। layoutIfNeeded()जब आप काम कर लें तो कॉल करना न भूलें । इसका नुकसान यह है कि लेआउट पास को दो बार किया जाएगा यदि लागू करने के लिए कोई परिवर्तन हैं और आपको यह सुनिश्चित करना होगा कि कोई अनंत लूप ट्रिगर होने की संभावना नहीं है।

चेतावनी का एक त्वरित शब्द: अक्षम बाधाएं विधि द्वारा वापस नहीं की जाती हैं constraints() ! इसका मतलब यह है कि यदि आप एक बाधा को फिर से चालू करने के इरादे से अक्षम करते हैं तो बाद में आपको इसका संदर्भ रखना होगा।

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


10

आप priorityसंपत्ति को "सक्षम" और उन्हें "अक्षम" करने के लिए समायोजित कर सकते हैं (सक्षम करने के लिए 750 मूल्य और उदाहरण के लिए 250 को अक्षम करने के लिए)। किसी कारण से activeBOOL बदलने का मेरे UI पर कोई प्रभाव नहीं पड़ा। इसके लिए कोई ज़रूरत नहीं है layoutIfNeededऔर इसे किसी भी समय viewDidLoad या किसी भी समय सेट और बदला जा सकता है।


एक बहुत अच्छा सुझाव। बाधा प्राथमिकता को बदलने में काम करता है viewWillTransition(to:, with:)या viewWillLayoutSubviews()आप एक स्टोरीबोर्ड में "स्थापित" के रूप में अपने सभी वैकल्पिक बाधाओं को रख सकते हैं। बाधा प्राथमिकता आवश्यक से गैर-आवश्यक में नहीं बदल सकती है, इसलिए नीचे दिए गए मानों का उपयोग करें 1000। दूसरी ओर, सक्रिय करना (जोड़ना) और निष्क्रिय करना (हटाना) बाधाओं में ही काम करता है viewDidLayoutSubviews()और -s के strong @IBOutletसंदर्भों को रखने की आवश्यकता होती है NSLayoutConstraint
गैरी

"किसी कारण से सक्रिय BOOL बदलने से मेरे UI पर कोई प्रभाव नहीं पड़ा"। यहाँ पर आधारित है । मुझे लगता है कि आप रनटाइम के दौरान 1000 प्राथमिकता के साथ एक बाधा नहीं बदल सकते। यदि आप इसे निष्क्रिय करना चाहते हैं, तो आपको प्रारंभिक प्राथमिकता को 999 या उससे कम पर सेट करना चाहिए ....
Honey

मैं इस कथन से असहमत हूं क्योंकि यह कठिन समस्याओं को दूर कर सकता है और सवाल का जवाब नहीं देता। प्राथमिकता को 250 पर सेट करना बाधा को "निष्क्रिय" नहीं करेगा, यह अभी भी प्रभाव और लेआउट को प्रभावित करेगा। ऐसा लग सकता है कि यह ज्यादातर मामलों में बाधा को "निष्क्रिय" करता है लेकिन निश्चित रूप से सभी मामलों में नहीं। (विशेष रूप से, ऐसा मामला नहीं जो मुझे इस प्रश्न का उत्तर खोजने के लिए प्रेरित करता है)
टम्टा

इससे दुर्घटनाएँ हो सकती हैं जैसे "स्थापित बाधा पर नहीं (या इसके विपरीत) से प्राथमिकता को प्राथमिकता देना समर्थित नहीं है। आपने प्राथमिकता 250 और मौजूदा प्राथमिकता 1000 थी।"
कार्तिक रमेश

8

अप्रयुक्त बाधाओं को निष्क्रिय करने का उचित समय:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

ध्यान रखें कि viewWillLayoutSubviewsकई बार बुलाया जा सकता है, इसलिए यहाँ कोई भारी गणना नहीं है, ठीक है?

नोट: यदि आप बाद में कुछ अड़चनों के बारे में प्रतिक्रिया देना चाहते हैं, तो हमेशा strongउनके संदर्भ को संग्रहीत करें।


2
मेरे लिए एकमात्र विश्वसनीय तरीका बाधाओं को समायोजित करना है viewDidLayoutSubviews()viewWillLayoutSubviews()मेरे मामले में बाधाओं का समायोजन काम नहीं करता है।
पेट्रिसन

6

जब एक दृश्य बनाया जा रहा है निम्नलिखित जीवन चक्र विधियों क्रम में कहा जाता है:

  1. loadView
  2. viewDidLoad
  3. viewWillAppear
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. viewDidAppear

अब आपके प्रश्नों के लिए।

  1. मुझे यह व्यवहार क्यों मिल रहा है?

उत्तर: क्योंकि जब आप व्यू में बाधाओं को निर्धारित करने का प्रयास करते हैं viewDidLoad, तो इसकी सीमा नहीं होती है, इसलिए बाधाओं को सेट नहीं किया जा सकता है। इसके बाद ही viewDidLayoutSubviewsदृश्य की सीमा को अंतिम रूप दिया जाता है।

  1. क्या viewDidLoad से बाधाओं को सक्रिय / निष्क्रिय करना संभव है?

उत्तर: नहीं। कारण ऊपर बताया गया है।


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

viewDidLoad ठीक होना चाहिए क्योंकि दृश्य बनाए और लोड किए गए हैं ... वास्तविकता में जहां आप बाधाओं को सक्रिय करते हैं, मुख्य रूप से प्रदर्शन के लिए नीचे आते हैं। मैं अनुमान लगा रहा हूं कि मूल समस्या असंबंधित थी जहां बाधाओं को सक्रिय किया गया था। stackoverflow.com/questions/19387998/…
गाबे

@ABakerSmith मैंने अपने उत्तर को अधिक स्पष्ट होने के लिए संपादित किया है।
सुमीत

1

मैंने पाया है कि जब तक आप - (void)updateConstraints(उद्देश्य c) के ओवरराइड में बाधाओं को सामान्य रूप से सेट करते हैं , तब तक strongसक्रिय और बिना-सक्रिय अवरोधों के उपयोग के संदर्भ के साथ । और कहीं और देखने के चक्र में निष्क्रिय और / या सक्रिय करें जो आपको चाहिए, फिर कॉल करना layoutIfNeeded, आपके पास कोई समस्या नहीं होनी चाहिए।

मुख्य बात यह नहीं है कि लगातार ओवरराइड का पुन: उपयोग न करें updateConstraintsऔर बाधाओं को अलग करने के लिए, जब तक आप updateConstraintअपने पहले इनिशियलाइज़ेशन और लेआउट के बाद कॉल नहीं करते। यह देखने के चक्र में कहाँ के बाद बात करने लगता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.