2014-04-30 6 views
29

मैं ऑटो लेआउट सीखने की कोशिश कर रहा हूं, आखिरकार सोचा कि जब यह हुआ तो मुझे उस पर लटका मिल रहा था। मैं प्रोटोटाइप कोशिकाओं और लेबल के साथ खेल रहा हूँ। मैं एकऑटोलायआउट मल्टीलाइन UILabel कुछ पाठ को काट रहा है

  • शीर्षक, titleLbl के लिए कोशिश कर रहा हूँ - चित्र में नीले बॉक्स
  • पैसा, moneyLbl - चित्र में हरे बॉक्स
  • उपशीर्षक/विवरण, subTitleLbl - तस्वीर में लाल बॉक्स

iphone screenshot

:

अब तक मैं इस राशि 363,210

क्या मैं हासिल करना चाहते हैं:

  • हरे बॉक्स हमेशा 1 लाइन पर प्रदर्शित करना चाहिए पूरी तरह से
  • हरे बॉक्स के अंदर मूल्य नीले बॉक्स के आधार पर केंद्रित होना चाहिए
  • नीले बॉक्स ले जाएगा शेष स्थान (2 के बीच क्षैतिज बाधा के लिए -8 पीएक्स) और यदि आवश्यक हो तो एकाधिक लाइनों पर प्रदर्शित करें
  • लाल बॉक्स नीचे जितना आवश्यक हो उतना लाइनों के नीचे होना चाहिए और प्रदर्शित करना चाहिए।

जैसा कि आप देख सकते हैं कि यह आखिरी पंक्ति में उदाहरण के अपवाद के साथ लगभग सभी काम कर रहा है। मैंने इस पर शोध करने की कोशिश की है और जो मैं इकट्ठा कर सकता हूं वह आंतरिक सामग्री आकार के साथ कुछ करना है। ऑनलाइन पोस्ट कई पोस्ट setPreferredMaxLayoutWidth सेट करने की सलाह देते हैं, मैंने इसे titleLbl के लिए कई स्थानों पर किया है और इसका कोई प्रभाव नहीं पड़ा है। केवल 180 पर हार्डकोडिंग करने पर मुझे परिणाम मिलते थे, लेकिन इसमें पाठ के नीचे/नीचे के अन्य नीले रंग के बक्से में व्हाइटस्पेस/पैडिंग भी शामिल था, जो लाल/नीले रंग के बक्से के बीच की जगह को काफी बढ़ाता था।

यहाँ मेरी कमी कर रहे हैं:

शीर्षक: title constraints

मनी: money constraints

उपशीर्षक: subtitle constraints

परीक्षण मैं अन्य पाठ नमूने यह होता है के साथ चलाने के आधार पर किया है स्टोरीबोर्ड में दिखाए गए चौड़ाई का उपयोग करने लगते हैं, लेकिन इसे ठीक करने का कोई भी प्रयास काम नहीं कर रहा है।

मैं सेल के एक इवर बनाया है और यह प्रयोग किया जाता सेल की ऊंचाई बनाने के लिए है:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [sizingCellTitleSubMoney.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]]; 
    [sizingCellTitleSubMoney.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]]; 
    [sizingCellTitleSubMoney.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]]; 

    [sizingCellTitleSubMoney layoutIfNeeded]; 

    CGSize size = [sizingCellTitleSubMoney.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 
    return size.height+1; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    MATitleSubtitleMoneyTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifierTitleSubTitleMoney]; 

    if(!cell) 
    { 
     cell = [[MATitleSubtitleMoneyTableViewCell alloc] init]; 
    } 


    cell.titleLbl.layer.borderColor = [UIColor blueColor].CGColor; 
    cell.titleLbl.layer.borderWidth = 1; 

    cell.subtitleLbl.layer.borderColor = [UIColor redColor].CGColor; 
    cell.subtitleLbl.layer.borderWidth = 1; 

    cell.moneyLbl.layer.borderColor = [UIColor greenColor].CGColor; 
    cell.moneyLbl.layer.borderWidth = 1; 


    [cell.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]]; 
    [cell.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]]; 
    [cell.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]]; 

    return cell; 
} 

मैं कोशिश की है setPreferredMaxLayoutWidth सेल के लेआउट subviews अंदर की स्थापना:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    NSLog(@"Money lbl: %@", NSStringFromCGRect(self.moneyLbl.frame)); 
    NSLog(@"prefferredMaxSize: %f \n\n", self.moneyLbl.frame.origin.x-8); 

    [self.titleLbl setPreferredMaxLayoutWidth: self.moneyLbl.frame.origin.x-8]; 
} 

लॉग :

2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] Money lbl: {{209, 20}, {91, 61}} 
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] prefferredMaxSize: 201.000000 

जो मैं उम्मीद करता हूं कि 2 तत्वों के बीच 8px है और कंसोल यह सत्यापित करता है।यह पहली पंक्ति पर पूरी तरह से काम करता है इससे कोई फर्क नहीं पड़ता कि मैं ब्लू बॉक्स में कितना टेक्स्ट जोड़ता हूं, जो स्टोरीबोर्ड जैसा ही है, लेकिन हरे रंग के बॉक्स में कोई भी वृद्धि चौड़ाई की गणना से फेंकता है। मैंने इसे tableView:willDisplayCell और tableView:heightForRowAtIndexPath: में अन्य प्रश्नों के उत्तर के आधार पर सेट करने का प्रयास किया है। लेकिन कोई फर्क नहीं पड़ता कि यह सिर्फ लेआउट नहीं है।

किसी भी मदद की, विचार या टिप्पणियों का बहुत सराहना की जाएगी

उत्तर

26

इस पढ़ने के बाद एक और भी बेहतर जवाब मिला: http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights

एक UILabel उपवर्ग बनाने ताकि तरह layoutSubviews ओवरराइड करने के लिए:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds); 

    [super layoutSubviews]; 
} 

यह सुनिश्चित करता है preferredMaxLayoutWidth हमेशा सही है।

अद्यतन:

कई और रिहाई के iOS के और अधिक USECASE के परीक्षण के बाद मैं ऊपर पाया है हर मामले के लिए पर्याप्त नहीं हो सकता है। आईओएस 8 (मुझे विश्वास है) के रूप में, कुछ परिस्थितियों में केवल didSetbounds को स्क्रीन लोड से पहले समय में बुलाया गया था।

आईओएस 9 में बहुत हाल ही में मैं जब एक UITableView साथ एक मॉडल UIVIewController का उपयोग कर एक और मुद्दा भर में आया था, कि layoutSubviews और set bounds दोनोंheightForRowAtIndexPath के बाद बुलाया जा रहा था। बहुत डीबगिंग के बाद एकमात्र समाधान set frame को ओवरराइड करना था।

नीचे कोड अब, यह सब आईओएस के, नियंत्रक, स्क्रीन आकार आदि

override public func layoutSubviews() 
{ 
    super.layoutSubviews() 
    self.preferredMaxLayoutWidth = self.bounds.width 
    super.layoutSubviews() 
} 

override public var bounds: CGRect 
{ 
    didSet 
    { 
     self.preferredMaxLayoutWidth = self.bounds.width 
    } 
} 

override public var frame: CGRect 
{ 
    didSet 
    { 
     self.preferredMaxLayoutWidth = self.frame.width 
    } 
} 
2

मुझे यकीन है कि मैं तुम्हें सही समझ में नहीं हूँ और अपने समाधान वास्तव में अब तक सबसे अच्छा एक से है, लेकिन यहाँ यह है:

मैंने लेबल पर एक ऊंचाई बाधा जोड़ा जो कट गया था, सेल के आउटलेट के लिए बाधा जुड़ा हुआ है। मैंने लेआउट सबव्यूव विधि को ओवरराइड किया है:

- (void) layoutSubviews { 
    [super layoutSubviews]; 

    [self.badLabel setNeedsLayout]; 
    [self.badLabel layoutIfNeeded]; 

    self.badLabelHeightConstraint.constant = self.badLabel.intrinsicContentSize.height; 
} 

और व्हायोला! कृपया उस विधि को आजमाएं और मुझे परिणाम बताएं, धन्यवाद।

+0

इनपुट के लिए धन्यवाद, लेकिन मुझे खेद है कि यह कोई रास्ता नहीं है। मुद्दा (जैसा कि मेरे उत्तर द्वारा उल्लिखित है) यह है कि यह हरे रंग के लेबल से 'एक्स' स्थिति का उपयोग कर रहा था इससे पहले कि हरे रंग के लेबल को अपनी सामग्री में फिट करने के लिए बढ़ाया गया हो। अधिकांश मामलों में मल्टीलाइन लेबल का उपयोग करते समय आपको 'setPreferredMaxLayoutWidth' सेट करने की आवश्यकता होती है, मुझे नीले रंग के इस मान की गणना करने के लिए हरे रंग की प्रदर्शित चौड़ाई की गणना करने की आवश्यकता होती है। एक बार यह सेट हो जाने के बाद, यह सही ऊंचाई –

0

Omg में काम करता है सुनिश्चित करने के लिए आवश्यक प्रतीत हो रहा है सिर्फ एक ही मुद्दा था। @ सिमॉन मैकलोफ्लिन के जवाब ने मेरी मदद नहीं की। लेकिन

titleLabel.adjustsFontSizeToFitWidth = true 

एक जादू किया! यह काम करता है, मुझे नहीं पता क्यों, लेकिन ऐसा करता है। और हां, आपके लेबल में एक स्पष्ट पसंदीदा MaxLayoutWidth मान होना चाहिए। कई प्रयास मैंने किया था एक समाधान कैसे प्रोटोटाइप सेल है कि वास्तव में काम कर उपयोग करने के लिए के साथ आया था के बाद

+--------------------------------------------------+ 
| This is my cell.contentView      | 
| +------+ +-----------------+ +--------+ +------+ | 
| |  | | multiline | | title2 | |  | | 
| | img | +-----------------+ +--------+ | img | | 
| |  | +-----------------+ +--------+ |  | | 
| |  | |  title3  | | title4 | |  | | 
| +------+ +-----------------+ +--------+ +------+ | 
|             | 
+--------------------------------------------------+ 

:

+2

पता लगाया यह वास्तव में एक आदर्श समाधान नहीं है। लेबल को बढ़ने की अनुमति देने के बजाय, यह निश्चित रूप से एक निश्चित चौड़ाई फिट करने के लिए फ़ॉन्ट को बढ़ाने/घटाने जा रहा है। यह परिणामस्वरूप आपका यूआई बहुत असंगत दिख रहा है। मुझे आईओएस में और अधिक बग मिली हैं और उन मामलों का उपयोग करें जिन्हें अतिरिक्त कोड की आवश्यकता है। मैंने ऊपर अपना जवाब अपडेट किया। –

0

मैं सेल ऊपर लपेटकर, इस तरह लग रही के साथ कुछ समय बिताने के लिए किया था।

मुख्य समस्या यह थी कि मेरे लेबल हो सकते हैं या नहीं हो सकते हैं। प्रत्येक तत्व वैकल्पिक होना चाहिए।

सबसे पहले, हमारे 'प्रोटोटाइप' सेल को केवल "वास्तविक" वातावरण में नहीं होने पर लेआउट सामान होना चाहिए।

इसलिए, मैंने ध्वज 'ऊंचाई गणनाकरण प्रगति' बनाई है।

जब कोई (टेबल व्यू का डेटा स्रोत) ऊंचाई की गणना करने के लिए कह रहा है तो हम ब्लॉक: का उपयोग कर प्रोटोटाइप सेल को डेटा प्रदान कर रहे हैं और फिर हम सामग्री प्रोटोकॉल सेल से सामग्री को लेआउट करने और इसकी ऊंचाई को पकड़ने के लिए कह रहे हैं। सरल।

आदेश layoutSubview प्रत्यावर्तन के साथ iOS7 बग से बचने के लिए एक "layoutingLock" चर है

- (CGFloat)heightWithSetupBlock:(nonnull void (^)(__kindof UITableViewCell *_Nonnull cell))block { 
    block(self); 

    self.heightCalculationInProgress = YES; 

    [self setNeedsLayout]; 
    [self layoutIfNeeded]; 

    self.layoutingLock = NO; 
    self.heightCalculationInProgress = NO; 

    CGFloat calculatedHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; 
    CGFloat height = roundf(MAX(calculatedHeight, [[self class] defaultHeight])); 

    return height; 
} 

यह बाहर कर सकता से 'ब्लॉक' इस तरह दिखता है:

[prototypeCell heightWithSetupBlock:^(__kindof UITableViewCell * _Nonnull cell) { 
    @strongify(self); 
    cell.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0); 
    cell.dataObject = dataObject; 
    cell.multilineLabel.text = @"Long text"; 
    // Include any data here 
}]; 

और अब शुरू होता है सबसे दिलचस्प हिस्सा। लेआउटिंग:

- (void)layoutSubviews { 
    if(self.layoutingLock){ 
     return; 
    } 

    // We don't want to do this layouting things in 'real' cells 
    if (self.heightCalculationInProgress) { 

    // Because of: 
    // Support for constraint-based layout (auto layout) 
    // If nonzero, this is used when determining -intrinsicContentSize for multiline labels 

    // We're actually resetting internal constraints here. And then we could reuse this cell with new data to layout it correctly 
     _multilineLabel.preferredMaxLayoutWidth = 0.f; 
     _title2Label.preferredMaxLayoutWidth = 0.f; 
     _title3Label.preferredMaxLayoutWidth = 0.f; 
     _title4Label.preferredMaxLayoutWidth = 0.f; 
    } 

    [super layoutSubviews]; 

    // We don't want to do this layouting things in 'real' cells 
    if (self.heightCalculationInProgress) { 

     // Make sure the contentView does a layout pass here so that its subviews have their frames set, which we 
     // need to use to set the preferredMaxLayoutWidth below. 
     [self.contentView setNeedsLayout]; 
     [self.contentView layoutIfNeeded]; 

     // Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label's frame, 
     // as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height. 
     _multilineLabel.preferredMaxLayoutWidth = CGRectGetWidth(_multilineLabel.frame); 
     _title2Label.preferredMaxLayoutWidth = CGRectGetWidth(_title2Label.frame); 
     _title3Label.preferredMaxLayoutWidth = CGRectGetWidth(title3Label.frame); 
     _title4Label.preferredMaxLayoutWidth = CGRectGetWidth(_title4Label.frame); 
    } 

    if (self.heightCalculationInProgress) { 
     self.layoutingLock = YES; 
    } 
} 
संबंधित मुद्दे