it-swarm.asia

دائرة الرقابة الداخلية AutoLayout متعدد الخطوط UILabel

السؤال التالي هو نوع من استمرار هذا واحد:

iOS: UILabel متعدد الخطوط في التخطيط التلقائي

الفكرة الرئيسية هي أنه من المفترض أن توضح كل طريقة عرض حجمها "المفضل" (المضمّن) بحيث يمكن لـ AutoLayout معرفة كيفية عرضه بشكل صحيح. UILabel هو مجرد مثال على الموقف الذي يكون فيه العرض لا يمكن في حد ذاته معرفة الحجم الذي يحتاجه للعرض. ذلك يعتمد على ما يتم توفير العرض.

As mwhuss وأشار ، setPreferredMaxLayoutWidth فعلت الحيلة المتمثلة في جعل امتداد العلامة عبر خطوط متعددة. لكن هذا ليس السؤال الرئيسي هنا. السؤال هو أين وأين أحصل على هذه القيمة {width التي أرسلها كوسيطة إلى setPreferredMaxLayoutWidth.

تمكنت من جعل شيئًا ما يبدو شرعيًا ، لذا صححني إذا كنت مخطئًا بأي حال من الأحوال وأخبرني من فضلك إذا كنت تعرف طريقة أفضل.

في UIView

-(CGSize) intrinsicContentSize

I setPreferredMaxLayoutWidth من أجل UILabels الخاصة بي وفقًا لـ self.frame.width.

وUIViewController

-(void) viewDidLayoutSubviews

هي طريقة رد الاتصال الأولى التي أعرفها حيث يتم تعيين المقابلات الفرعية للعرض الرئيسي مع الإطارات المحددة التي يقيمون بها على الشاشة. من داخل هذه الطريقة ، إذن ، أعمل على عروضي الفرعية ، وأبطل أحجامها الداخلية بحيث يتم تقسيم UILabels إلى خطوط متعددة بناءً على العرض الذي تم تعيينه لهم.

59
ancajic

يبدو مزعجًا أن UILabel لا تتخلى عن عرضه لتنسيق العرض الأقصى المفضل ، إذا كان لديك قيود تحدد هذا العرض بشكل لا لبس فيه لك.

في كل حالة واحدة تقريبًا استخدمت التسميات في Autolayout ، كان عرض الحد الأقصى المفضل للتخطيط هو العرض الفعلي للتسمية ، بمجرد تنفيذ باقي المخطط.

لذلك ، لتحقيق ذلك تلقائيًا ، استخدمت فئة فرعية UILabel ، والتي تتجاوز setBounds:. هنا ، اتصل بالتنفيذ الفائق ، ثم ، {إذا لم يكن الأمر كذلك بالفعل} ، فقم بتعيين عرض تخطيط الحد الأقصى المفضل ليكون عرض حجم الحدود.

التركيز مهم - يؤدي تعيين تخطيط أقصى مفضل إلى تنفيذ مسار تخطيط آخر ، بحيث يمكنك أن تنتهي بحلقة لا نهائية.

42
jrturton

توجد إجابة لهذا السؤال على objc.io في قسم "حجم المحتوى الجوهري للنص متعدد الخطوط" في مربع أدوات التخطيط التلقائي المتقدم . إليك المعلومات ذات الصلة:

حجم المحتوى الجوهري لـ UILabel و NSTextField غامض بالنسبة للنص متعدد الأسطر. يعتمد ارتفاع النص على عرض الخطوط ، والذي لم يتم تحديده بعد عند حل القيود. لحل هذه المشكلة ، يكون لدى كلتا الفئتين خاصية جديدة تسمى preferMaxLayoutWidth ، والتي تحدد الحد الأقصى لعرض الخط لحساب حجم المحتوى الجوهري.

نظرًا لأننا عادةً لا نعرف هذه القيمة مقدمًا ، فإننا نحتاج إلى اتباع نهج من خطوتين لتحقيق ذلك. أولاً ، نترك Auto Layout يقوم بعمله ، ثم نستخدم الإطار الناتج في مسار التصميم لتحديث الحد الأقصى المفضل للعرض وتخطيط المشغل مرة أخرى.

الكود الذي يقدمونه للاستخدام داخل وحدة التحكم في العرض:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

ألقِ نظرة على المنشور ، فهناك مزيد من المعلومات حول سبب ضرورة القيام بالتخطيط مرتين.

57
nevan king

تحديث

يبدو أن إجابتي الأصلية مفيدة ، لذا فقد تركتها دون مساس أدناه ، ومع ذلك ، فقد وجدت في مشاريعي حلاً أكثر موثوقية يعمل حول الأخطاء في iOS 7 و iOS 8. https://github.com/nicksnyder/تخطيط خلايا ios

الإجابة الأصلية

هذا هو الحل الكامل الذي يعمل بالنسبة لي على iOS 7 و iOS 8

الهدف C

@implementation AutoLabel

- (void)setBounds:(CGRect)bounds {
  if (bounds.size.width != self.bounds.size.width) {
    [self setNeedsUpdateConstraints];
  }
  [super setBounds:bounds];
}

- (void)updateConstraints {
  if (self.preferredMaxLayoutWidth != self.bounds.size.width) {
    self.preferredMaxLayoutWidth = self.bounds.size.width;
  }
  [super updateConstraints];
}

@end

سويفت

import Foundation

class EPKAutoLabel: UILabel {

    override var bounds: CGRect {
        didSet {
            if (bounds.size.width != oldValue.size.width) {
                self.setNeedsUpdateConstraints();
            }
        }
    }

    override func updateConstraints() {
        if(self.preferredMaxLayoutWidth != self.bounds.size.width) {
            self.preferredMaxLayoutWidth = self.bounds.size.width
        }
        super.updateConstraints()
    }
}
32
Nick Snyder

كان لدينا موقف حيث وضعت UILabel ذات تصميم تلقائي داخل UIScrollView بشكل جيد في صورة ، ولكن عند تدويرها إلى المناظر الطبيعية لم يتم إعادة حساب ارتفاع UILabel.

لقد وجدنا أن الإجابة منjrturton قد أصلحت هذا الأمر ، لأنه من المفترض الآن أنه تم تعيين تفضيلMaxLayoutWidth بشكل صحيح.

إليك الكود الذي استخدمناه. فقط قم بتعيين الفئة المخصصة من باني الواجهة لتكون CVFixedWidthMultiLineLabel .

CVFixedWidthMultiLineLabel.h

@interface CVFixedWidthMultiLineLabel : UILabel

@end 

CVFixedWidthMultiLineLabel.m

@implementation CVFixedWidthMultiLineLabel

// Fix for layout failure for multi-line text from
// http://stackoverflow.com/questions/17491376/ios-autolayout-multi-line-uilabel
- (void) setBounds:(CGRect)bounds {
    [super setBounds:bounds];

    if (bounds.size.width != self.preferredMaxLayoutWidth) {
        self.preferredMaxLayoutWidth = self.bounds.size.width;
    }
}

@end
9
Ben Clayton

نظرًا لأنه لا يُسمح لي بإضافة تعليق ، فأنا ملزم بإضافته كإجابة. كان إصدار jrturton يعمل فقط بالنسبة لي إذا اتصلت بـ layoutIfNeeded في updateViewConstraints قبل الحصول على preferredMaxLayoutWidth من التصنيف المعني. بدون استدعاء layoutIfNeeded ، كان preferredMaxLayoutWidth دائمًا 0 في updateViewConstraints. ومع ذلك ، كانت دائمًا القيمة المطلوبة عند تسجيل الدخول setBounds:. لم أتمكن من معرفة متى تم تعيين preferredMaxLayoutWidth الصحيح. لقد تجاوزت setPreferredMaxLayoutWidth: في فئة UILabel ، لكن لم يتم الاتصال بها مطلقًا.

ملخصة ، أنا:

  • ... sublcassed UILabel
  • ... وتجاوز setBounds: إلى ، إن لم يكن قد تم بالفعل تعيين ، قم بتعيين preferredMaxLayoutWidth على CGRectGetWidth(bounds)
  • ... اتصل بـ [super updateViewConstraints] قبل التالي
  • ... اتصل بـ layoutIfNeeded قبل الحصول على preferredMaxLayoutWidth ليتم استخدامه في حساب حجم التسمية

EDIT: يبدو أن هذا الحل البديل يعمل فقط أو تكون هناك حاجة إليه في بعض الأحيان. لقد واجهت مشكلة (iOS 7/8) حيث لم يتم حساب ارتفاع الملصق بشكل صحيح ، حيث أرجع preferredMaxLayoutWidth0 بعد تنفيذ عملية التخطيط مرة واحدة. لذلك بعد بعض التجارب والخطأ (وبعد العثور على هذا إدخال المدونة ) ، قمت بالتبديل إلى استخدام UILabel مرة أخرى وقمت فقط بتعيين قيود تخطيط تلقائي من الأعلى والأسفل واليسار واليمين. ولأي سبب تم تعيين ارتفاع التسمية بشكل صحيح بعد تحديث النص.

2
dergab

باستخدام boundingRectWithSize

لقد حلت كفاحي من خلال تسميتين متعدد الأسطر في UITableViewCell كانت تستخدم "\ n" كفاصل سطحي عن طريق قياس العرض المطلوب مثل هذا:

- (CGFloat)preferredMaxLayoutWidthForLabel:(UILabel *)label
{
    CGFloat preferredMaxLayoutWidth = 0.0f;
    NSString *text = label.text;
    UIFont *font = label.font;
    if (font != nil) {
        NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
        mutableParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

        NSDictionary *attributes = @{NSFontAttributeName: font,
                                     NSParagraphStyleAttributeName: [mutableParagraphStyle copy]};
        CGRect boundingRect = [text boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
        preferredMaxLayoutWidth = ceilf(boundingRect.size.width);

        NSLog(@"Preferred max layout width for %@ is %0.0f", text, preferredMaxLayoutWidth);
    }


    return preferredMaxLayoutWidth;
}

ثم كان الاتصال بهذه الطريقة بسيطًا مثل:

CGFloat labelPreferredWidth = [self preferredMaxLayoutWidthForLabel:textLabel];
if (labelPreferredWidth > 0.0f) {
    textLabel.preferredMaxLayoutWidth = labelPreferredWidth;
}
[textLabel layoutIfNeeded];
2
Cameron Lowell Palmer

كما اقترحت إجابة أخرى ، حاولت تجاوز viewDidLayoutSubviews:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    _subtitleLabel.preferredMaxLayoutWidth = self.view.bounds.size.width - 40;
    [self.view layoutIfNeeded];
}

نجح ذلك ، لكنه كان مرئيًا على واجهة المستخدم وتسبب في "وميض مرئي" ، أي أولاً تم تقديم الملصق بارتفاع سطرين ، ثم أعيد تقديمه بارتفاع سطر واحد فقط.

هذا لم يكن مقبولا بالنسبة لي.

لقد وجدت بعد ذلك حلاً أفضل عن طريق تجاوز updateViewConstraints:

-(void)updateViewConstraints {
    [super updateViewConstraints];

    // Multiline-Labels and Autolayout do not work well together:
    // In landscape mode the width is still "portrait" when the label determines the count of lines
    // Therefore the preferredMaxLayoutWidth must be set
    _subtitleLabel.preferredMaxLayoutWidth = self.view.bounds.size.width - 40;
}

كان هذا هو الحل الأفضل بالنسبة لي ، لأنه لم يتسبب في "الخفقان البصري".

1
jbandi

الحل النظيف هو ضبط rowcount = 0 واستخدام خاصية لارتفاع مستوى العلامة الخاصة بك. ثم بعد تعيين المحتوى دعوة

CGSize sizeThatFitsLabel = [_subtitleLabel sizeThatFits:CGSizeMake(_subtitleLabel.frame.size.width, MAXFLOAT)];
_subtitleLabelHeightConstraint.constant = ceilf(sizeThatFitsLabel.height);

-(void) updateViewConstraints لديه مشكلة منذ iOS 7.1.

0
netshark1000

في نظام التشغيل iOS 8 ، يمكنك إصلاح مشكلات تخطيط الملصقات متعددة الأسطر في خلية عن طريق الاتصال بـ cell.layoutIfNeeded() بعد إلغاء تثبيت الخلية وتكوينها. الدعوة غير ضارة في نظام التشغيل iOS 9.

انظر إجابة نيك سنايدر. تم أخذ هذا الحل من الكود الخاص به في https://github.com/nicksnyder/ios-cell-layout/blob/master/CellLayout/TableViewController.Swift .

0
phatmann