Xcode 8 और iOS10 के बाद से viewDidLayoutSubviews पर विचार ठीक से आकार में नहीं हैं


94

ऐसा लगता है कि Xcode 8 के साथ viewDidLoad, सभी व्यू-कॉन्ट्रैक्टर सबव्यूशंस का आकार 1000x1000 का समान है। अजीब बात है, लेकिन ठीक है, viewDidLoadविचारों को सही ढंग से आकार देने के लिए बेहतर जगह कभी नहीं रही है।

लेकिन viewDidLayoutSubviewsहै!

और मेरी वर्तमान परियोजना पर, मैं एक बटन का आकार मुद्रित करने का प्रयास करता हूं:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    NSLog(@"%@", self.myButton);
}

लॉग myButton के लिए (1000x1000) का आकार दिखाता है! फिर अगर मैं बटन पर क्लिक करता हूं, उदाहरण के लिए, लॉग सामान्य आकार दिखाता है।

मैं ऑटोलेयूट का उपयोग कर रहा हूं।

यह एक बग है?


1
UIImageView के साथ एक ही मुद्दा रहा है - जब मैं प्रिंट करता हूं तो मुझे एक अजीब फ्रेम मिलता है = (0 0; 1000 1000)।; मैं UITableViewCell के अंदर हूं, और एक बार जब मैं टेबलव्यू को रिफ्रेश करता हूं, तो मैं उससे यही उम्मीद करता हूं कि यह भी हो (जब सेल व्यूपोर्ट से बाहर जाए और फिर से वापस आए)। किसी को भी पता है कि ऐसा क्यों हो रहा है (डिफ़ॉल्ट रूप से अजीब फ्रेम)?
यूजेन डिमबियू 14

4
मुझे लगता है कि (0, 0, 1000, 1000)बाउंड इनिशियलाइज़ेशन एक नया तरीका है, जो कि Xcode, IB से विचार प्राप्त करने के लिए प्रेरित करता है। Xcode8 से पहले, xib में उनके कॉन्फ़िगर आकार के साथ विचार बनाए गए थे, फिर स्क्रीन के अनुसार आकार दिया गया। लेकिन अब, आईबी दस्तावेज़ में कोई कॉन्फ़िगर आकार नहीं है क्योंकि आकार आपके डिवाइस के चयन (स्क्रीन के नीचे) पर निर्भर करता है। तो असली सवाल यह है: क्या कोई विश्वसनीय जगह है जहां विचारों को अंतिम आकार की जाँच की जा सकती है?
मार्टिन

4
क्या आप अपने बटन के लिए गोल कोनों का उपयोग कर रहे हैं? पहले कॉल करने का प्रयास करें LayoutIfNeeded ()।
यूजेन दिमबियू

दिलचस्प। मैं वास्तव में एक गोल सीमा की गणना करने के लिए व्यू फ्रेम का उपयोग कर रहा था। यहां तक ​​कि अगर यह सवाल का जवाब नहीं देता है, तो यह काम करता है। इसका एक अच्छा टिप ध्यान में रखना है। धन्यवाद!
मार्टिन

मुझे लगता है कि मैं समान मुद्दों को एक uitextfield के अधिकार के अंदर एक छवि बटन स्थापित कर रहा हूँ। मैं टेक्स्ट बटन की ऊंचाई और चौड़ाई को टेक्स्टफील्ड की ऊंचाई पर सेट करना चाहता था ताकि यह अपने पहलू अनुपात को बनाए रखे और कंटेनर को बाहर निकाल सके।
atlantach_james

जवाबों:


98

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

इस कार्यक्षमता से पहले, उपयोगकर्ता को मैन्युअल रूप से प्रत्येक दृश्य नियंत्रक आकार सेट करना चाहिए। इसलिए दृश्य नियंत्रक को एक निश्चित आकार के साथ सहेजा गया था, जिसका उपयोग initWithCoderप्रारंभिक फ्रेम सेट करने के लिए किया गया था ।

अब, ऐसा लगता है कि initWithCoderस्टोरीबोर्ड में परिभाषित आकार का उपयोग नहीं करते हैं, और 1000 मीटर 1000 px आकार को व्यूकंट्रोलर दृश्य और उसके सभी साक्षात्कारों के लिए परिभाषित करते हैं।

यह कोई समस्या नहीं है, क्योंकि विचारों को हमेशा इन लेआउट समाधानों में से किसी एक का उपयोग करना चाहिए:

  • autolayout, और सभी अड़चनें आपके विचारों को सही ढंग से लेआउट करेंगी

  • ऑटोरेस्पिरिंगमास्क, जो प्रत्येक दृश्य को लेआउट करेगा जिसमें कोई बाधा नहीं जुड़ी है ( नोट ऑटोलॉयआउट और मार्जिन की कमी अब एक ही दृश्य \ _ / में संगत है! )

लेकिन इस है दृश्य परत, की तरह से संबंधित सभी लेआउट सामान के लिए एक समस्या cornerRadiusहै, न तो के बाद से autolayout है और न ही autoresizing मुखौटा परत प्रॉपर्टी पर लागू होती।

इस समस्या का उत्तर देने के लिए, viewDidLayoutSubviewsयदि आप नियंत्रक में हैं, या layoutSubviewयदि आप किसी दृश्य में हैं , तो इसका उपयोग करने का सामान्य तरीका है । इस बिंदु पर (अपने superसंबंधित तरीकों को कॉल करने के लिए मत भूलना ), आपको पूरा यकीन है कि सभी लेआउट सामान हो चुके हैं!

सुंदर हे यकीन है? हम ... पूरी तरह से, मैंने टिप्पणी की है, और इसीलिए मैंने यह सवाल पूछा है, कुछ मामलों में इस पद्धति पर अभी भी दृश्य का 1000x1000 आकार है। मुझे लगता है कि मेरे खुद के सवाल का कोई जवाब नहीं है। इसके बारे में अधिकतम जानकारी देने के लिए:

1- यह केवल कोशिकाओं को बिछाते समय ही खुश होता है! में UITableViewCellऔर UICollectionViewCellउपवर्गों, layoutSubviewनहीं कहा जा जाएगा के बाद subviews सही ढंग से बाहर रखी किया जाएगा।

2- जैसा कि @EugenDimboiu ने टिप्पणी की थी (कृपया अपने उत्तर को अपने लिए उपयोगी होने [myView layoutIfNeeded]पर ), नॉट -आउट आउट सबव्यू पर कॉल करना, इसे समय में सही ढंग से लेआउट करेगा।

- (void)layoutSubviews {
    [super layoutSubviews];
    NSLog (self.myLabel); // 1000x1000 size 
    [self.myLabel layoutIfNeeded];
    NSLog (self.myLabel); // normal size
}

3- मेरी राय में, यह निश्चित रूप से एक बग है। मैंने इसे रडार (आईडी 28562874) पर जमा किया है।

पुनश्च: मैं अंग्रेजी मूल निवासी नहीं हूं, इसलिए मेरे पोस्ट को संपादित करने के लिए स्वतंत्र महसूस करें यदि मेरा व्याकरण सही किया जाना चाहिए;)

PS2: यदि आपके पास कोई बेहतर उपाय है, तो बेझिझक दूसरा उत्तर न लिखें। मैं स्वीकृत उत्तर को स्थानांतरित करूँगा।


3
thx, लेकिन मुझे आईडी 28562874 के साथ रडार नहीं मिला, क्या मुझे रडार URL मिल सकता है?
जॉय

मेरे लिए एक आकर्षण की तरह काम किया, दृश्य की परतों के लिए भी!
१०:०६ पर hlynbech

@ जॉय, मुझे नहीं पता कि मुझे अपने बग का लिंक मिल सकता है या नहीं। मुझे कोई प्रत्यक्ष URL नहीं मिला, और ऐसा लगता है कि अन्य उपयोगकर्ता मेरी रिपोर्ट नहीं देख सकते हैं। इस SO उत्तर के अनुसार stackoverflow.com/a/145223/127493 , ऐसा लगता है कि बग की प्राथमिकता बढ़ाने का सबसे अच्छा तरीका एक डुप्लिकेट बनाना है।
मार्टिन

यह एप्पल के हिस्से पर बेवकूफी से परे है। मेरे पास ऑटो लेआउट व्यू कंट्रोलर द्वारा पूरी तरह से निर्दिष्ट किया गया है, और कई टेक्स्ट फ़ील्ड और एक यूआईवीईवी में यह सभी बेवकूफ 0,0,1000 मिलियन। फ्रेम है। लेकिन सब नहीं। यह QA से कैसे बच सकता है? मुझे लगता है कि मैं एक और रडार दर्ज कर सकता हूं जो वे नहीं पढ़ेंगे।
आह्वान

3
मैं आपको और सभी को आपकी अंतर्दृष्टि और सुझावों के लिए धन्यवाद देना चाहता हूं। मैंने सारा दिन अपने बालों को बाहर निकालने में बिताया क्योंकि UIStackViewअंदर का UICollectionViewCellएक सही ऊंचाई के दौरान वापस नहीं आ रहा था viewDidLayoutSubviewslayoutIfNeededतुरंत कॉल करने से समस्या ठीक हो गई।
रुइज़

40

क्या आप अपने बटन के लिए गोल कोनों का उपयोग कर रहे हैं? layoutIfNeeded()पहले फोन करने की कोशिश करो ।


1
आह, और अधिक प्रतिनिधि पाने की कोशिश कर रहा? जैसा कि मैंने अपनी टिप्पणी में कहा था, इस सवाल का जवाब नहीं है। हालाँकि, इससे मुझे मदद मिली, इसलिए आपने
मार्टिन

4
यह भविष्य में किसी की मदद कर सकता है, और टिप्पणियों की तुलना में स्पॉट करना आसान है
यूजेन डिमबियु

1
अभी मेरी मदद की!
दिनै सिप

@ दादाई को यह सुनकर खुशी हुई!
यूजेन डिमबियू

यह काम! लेकिन क्यों? भी लेकिन उचित फ्रेम आकार पाने के लिए सही समाधान क्या है?
23

24

समाधान: अंदर लपेटें सब कुछ viewDidLayoutSubviewsमें DispatchQueue.main.async

// swift 3

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        // do stuff here
    }
}

1
यह काम कर रहा है, भले ही डाल दिया जाए -viewDidLoad... इस तरह का एक अजीब काम
medvedNick

1
शायद क्योंकि जब viewDidLayoutSubviews सिस्टम के पास वह समय नहीं था जो करने की आवश्यकता थी। प्रेषण कॉलिंग आपको एक रनलूप कूदती है, जिससे सिस्टम को अपना शब्द खत्म करने की अनुमति मिलती है। अगर मैं सही हूँ जब आप कुछ मिलीसेकंड की नींद के साथ एक ही व्यवहार प्राप्त कर सकते हैं
thibaut noah

क्योंकि सभी यूआई कार्यों को मुख्य धागे के भीतर किया जाना चाहिए, आपके समाधान के लिए धन्यवाद!
दिनवार

18

मुझे पता है कि यह आपका सटीक सवाल नहीं था, लेकिन मैं एक ऐसी ही समस्या में भाग गया था, जहां अपडेट के दौरान मेरे कुछ दृश्य व्यूडायलेटआउट साक्षात्कारों में सही फ्रेम आकार होने के बावजूद गड़बड़ हो गए थे। IOS 10 के रिलीज नोट्स के अनुसार:

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

कुछ मौजूदा कोड इस गलत व्यवहार पर निर्भर हो सकते हैं, जिसे अब सुधारा गया है। IOS 10 से पहले जुड़े बायनेरिज़ के लिए कोई व्यवहार परिवर्तन नहीं है, लेकिन iOS 10 पर निर्माण करते समय आपको कुछ स्थितियों को सही करने की आवश्यकता हो सकती है -layoutIfNeeded ट्रांसलेट के पर्यवेक्षण के लिए ।AutoresizingMaskIntoConstraining दृश्य जो कि पिछले रिसीवर था, या पहले स्थिति और इसे आकार देने से पहले ( या बाद में, अपने इच्छित व्यवहार के आधार पर) LayoutIfNeeded।

कस्टम UIView उप-वर्ग के साथ थर्ड पार्टी ऐप ऑटो लेआउट का उपयोग करते हैं जो सुपर कॉल करने से पहले स्वयं पर लेआउट और गंदे लेआउट को ओवरराइड करते हैं, जब वे iOS 10 पर पुनर्निर्माण करते हैं तो लेआउट फीडबैक लूप को ट्रिगर करने का जोखिम होता है। जब उन्हें सही तरीके से बाद में लेआउट भेजा जाता है तो उन्हें कॉल ज़रूर करना चाहिए। कुछ बिंदु पर स्वयं पर गंदे लेआउट को रोकें (ध्यान दें कि यह कॉल iOS 10 से पहले रिलीज में छोड़ दिया गया था)। ”

यदि आप TranslatesAutoresizingMaskIntoConstraints का उपयोग कर रहे हैं, तो अनिवार्य रूप से आप लेआउट को कॉल नहीं कर सकते हैं देखें यदि आप अब लेआउट का उपयोग कर रहे हैं। यदि आपको SuperView पर होना है, और आप इसे अभी भी देख सकते हैं ViewDidLayoutSubviews में।


5

यदि लेआउट लेआउट में सही नहीं हैं। ViewSubViews (जो वे नहीं हैं) आप मुख्य थ्रेड पर async बिट कोड को भेज सकते हैं। यह लेआउट को करने के लिए सिस्टम को कुछ समय देता है। जब आपके द्वारा भेजे जाने वाले ब्लॉक को निष्पादित किया जाता है, तो फ्रेम के अपने उचित आकार होते हैं।


3

इसने मेरे लिए (हास्यास्पद रूप से कष्टप्रद) मुद्दे को तय किया:

- (void) viewDidLayoutSubviews {

    [super viewDidLayoutSubviews];

    self.view.frame = CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height);

}

संपादित करें / ध्यान दें: यह एक पूर्ण स्क्रीन ViewController के लिए है।


मेनस्क्रीन बाउंड्स का उपयोग करना एक अच्छा समाधान नहीं है क्योंकि कई मामलों में व्यूकंट्रोलर पूरे स्क्रीन स्पेस को नहीं लेते हैं। इसके अलावा, आपको [super viewDidLayoutSubviews];इस विधि से कॉल करना चाहिए क्योंकि दृश्य द्वारा किए गए कई ऑटोलॉययट सामान
मार्टिन

@ मर्टिन आप क्या सलाह देते हैं? सहमत यह आदर्श नहीं लगता है।
23

@ चरसालोट जैसा कि मैं अपने उत्तर में कहता हूं, ऑटोलाययट या ऑटोरेस्पिरिंगमास्क का उपयोग करके आपके UIViewएस को सही ढंग से लेआउट करेगा । लेकिन अगर आपको एक निश्चित दृश्य फ्रेम पर विशेष गणना करनी चाहिए, तो यूजेन का जवाब काम करता है: इस layoutIfNeededपर कॉल करें । मुझे लगता है कि यह सबसे अच्छा समाधान नहीं है, लेकिन मुझे अभी भी कोई बेहतर नहीं मिला है।
मार्टिन

2

वास्तव में viewDidLayoutSubviewsभी अपने दृश्य के फ्रेम को सेट करने के लिए सबसे अच्छी जगह नहीं है। जहां तक ​​मुझे समझ में आया, अब से केवल उसी जगह पर इसे किया जाना चाहिए layoutSubviewsजो वास्तविक दृश्य कोड में विधि है। काश मैं सही नहीं होता, कोई मुझे सही कर देता अगर यह सच नहीं होता!


आपके उत्तर के लिए धन्यवाद। Apple प्रलेखन viewDidLayoutSubviewsबहुत अस्पष्ट है। "चर्चा" में दूसरा वाक्य किसी तरह से पिछले एक के विपरीत है। डेवलपर
मार्टिन

0

मैंने इस मुद्दे को पहले ही सेब के लिए सूचित कर दिया था, यह मुद्दा लंबे समय से मौजूद है, जब आप XIV से UIViewController को इनिशियलाइज़ कर रहे हैं, लेकिन मुझे काफी अच्छा वर्कअराउंड मिला। इसके अलावा मैंने पाया कि कुछ मामलों में जब UICollectionView और UITableView पर LayoutIfNeeded किया गया था, जब डेटासोर्स प्रारंभिक क्षण में सेट नहीं किया गया था, और इसकी आवश्यकता भी थी।

extension UIViewController {
    open override class func initialize() {
        if self !== UIViewController.self {
            return
        }
        DispatchQueue.once(token: "io.inspace.uiviewcontroller.swizzle") {
            ins_applyFixToViewFrameWhenLoadingFromNib()
        }
    }

    @objc func ins_setView(view: UIView!) {
        // View is loaded from xib file
        if nibBundle != nil && storyboard == nil && !view.frame.equalTo(UIScreen.main.bounds) {
            view.frame = UIScreen.main.bounds
            view.layoutIfNeeded()
        }
        ins_setView(view: view)
    }

    private class func ins_applyFixToViewFrameWhenLoadingFromNib() {
        UIViewController.swizzle(originalSelector: #selector(setter: UIViewController.view),
                                 with: #selector(UIViewController.ins_setView(view:)))
        UICollectionView.swizzle(originalSelector: #selector(UICollectionView.layoutSubviews),
                                 with: #selector(UICollectionView.ins_layoutSubviews))
        UITableView.swizzle(originalSelector: #selector(UITableView.layoutSubviews),
                                 with: #selector(UITableView.ins_layoutSubviews))
     }
}

extension UITableView {
    @objc fileprivate func ins_layoutSubviews() {
        if dataSource == nil {
            super.layoutSubviews()
        } else {
            ins_layoutSubviews()
        }
    }
}

extension UICollectionView {
    @objc fileprivate func ins_layoutSubviews() {
        if dataSource == nil {
            super.layoutSubviews()
        } else {
            ins_layoutSubviews()
        }
    }
}

एक बार विस्तार से डिस्पैच करें:

extension DispatchQueue {

    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block: (Void) -> Void) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

स्विज़ल एक्सटेंशन:

extension NSObject {
    @discardableResult
    class func swizzle(originalSelector: Selector, with selector: Selector) -> Bool {

        var originalMethod: Method?
        var swizzledMethod: Method?

        originalMethod = class_getInstanceMethod(self, originalSelector)
        swizzledMethod = class_getInstanceMethod(self, selector)

        if originalMethod != nil && swizzledMethod != nil {
            method_exchangeImplementations(originalMethod!, swizzledMethod!)
            return true
        }
        return false
    }
}

0

से उपयोग बदलकर मेरी समस्या हल कर दी गई

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];
    self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height;
}

सेवा

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height;
}

तो, डिड टू विल से

सुपर अजीब


0

मेरे लिए बेहतर समाधान।

protocol LayoutComplementProtocol {
    func didLayoutSubviews(with targetView_: UIView)
}

private class LayoutCaptureView: UIView {
    var targetView: UIView!
    var layoutComplements: [LayoutComplementProtocol] = []

    override func layoutSubviews() {
        super.layoutSubviews()

        for layoutComplement in self.layoutComplements {
            layoutComplement.didLayoutSubviews(with: self.targetView)
        }
    }
}

extension UIView {
    func add(layoutComplement layoutComplement_: LayoutComplementProtocol) {
        func findLayoutCapture() -> LayoutCaptureView {
            for subView in self.subviews {
                if subView is LayoutCaptureView {
                    return subView as? LayoutCaptureView
                }
            }
            let layoutCapture = LayoutCaptureView(frame: CGRect(x: -100, y: -100, width: 10, height: 10)) // not want to show, want to have size
            layoutCapture.targetView = self
            self.addSubview(layoutCapture)
            return layoutCapture
        }

        let layoutCapture = findLayoutCapture()
        layoutCapture.layoutComplements.append(layoutComplement_)
    }
}

का उपयोग करते हुए

class CircleShapeComplement: LayoutComplementProtocol {
    func didLayoutSubviews(with targetView_: UIView) {
        targetView_.layer.cornerRadius = targetView_.frame.size.height / 2
    }
}

myButton.add(layoutComplement: CircleShapeComplement())

0

लेआउट को ओवरराइड करें (परत का: CALayer) बजाय लेआउट में इंटरव्यू के सही सेलव्यू में सही फ्रेम हो


0

यदि आपको अपने दृश्य के फ़्रेम के आधार पर कुछ करने की आवश्यकता है - लेआउट को ओवरराइड करें और लेआउट को देखें IfNeeded

    override func layoutSubviews() {
    super.layoutSubviews()

    yourView.layoutIfNeeded()
    setGradientForYourView()
}

मेरे पास देखने के लिए मुद्दा था, जब मैं अपने व्यू के लिए गलत फ्रेम लौटा रहा था , जिसके लिए मुझे एक ढाल जोड़ने की जरूरत थी। और केवल लेआउटअनफैड ने सही काम किया :)


-1

आईओएस में नए अपडेट के अनुसार यह वास्तव में एक बग है लेकिन हम इसका उपयोग करके इसे कम कर सकते हैं -

यदि आप अपने प्रोजेक्ट में ऑटोलैयूट के साथ xib का उपयोग कर रहे हैं तो आपको ऑटोलैयूट सेटिंग में सिर्फ फ्रेम अपडेट करना होगा कृपया इसके लिए चित्र ढूंढें।यहां छवि विवरण दर्ज करें

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