Programing

iOS AutoLayout 여러 줄 UILabel

lottogame 2020. 12. 7. 07:45
반응형

iOS AutoLayout 여러 줄 UILabel


다음 질문은 이것의 일종의 연속입니다.

iOS : 자동 레이아웃의 여러 줄 UILabel

주요 아이디어는 모든 뷰가 "선호"(내재) 크기로 표시되어 AutoLayout이 올바르게 표시하는 방법을 알 수 있다는 것입니다. UILabel은 뷰 디스플레이에 필요한 크기를 스스로 알 수없는 상황의 예일뿐입니다 . 제공되는 너비에 따라 다릅니다.

으로 mwhuss는 지적 setPreferredMaxLayoutWidth는 여러 줄에 걸쳐 레이블 범위를 만드는 트릭을했다. 그러나 그것은 여기서 주요 질문이 아닙니다. 질문은 setPreferredMaxLayoutWidth에 인수로 보내는 너비 값을 언제 어디서 얻을 수 있는가입니다 .

나는 합법적으로 보이는 것을 만들 수 있었으므로 어떤 식 으로든 내가 틀리면 나를 수정하고 더 나은 방법을 알고 있으면 알려주십시오.

UIView의

-(CGSize) intrinsicContentSize

I setPreferredMaxLayoutWidth self.frame.width에 따라 내 UILabels합니다.

UIViewController의

-(void) viewDidLayoutSubviews

메인 뷰의 하위 뷰가 화면에있는 정확한 프레임으로 지정되는 위치를 아는 첫 번째 콜백 메서드입니다. 그런 다음 해당 메서드 내에서 하위 뷰를 조작하여 UILabels가 지정된 너비에 따라 여러 줄로 나뉘도록 고유 크기를 무효화합니다.


너비를 명확하게 정의하는 제약 조건이있는 경우 UILabel이 선호하는 최대 레이아웃 너비에 대한 너비를 기본값으로 설정하지 않는 것이 성가신 것 같습니다.

Autolayout에서 레이블을 사용한 거의 모든 경우에서 선호하는 최대 레이아웃 너비는 나머지 레이아웃이 수행 된 후 레이블의 실제 너비였습니다.

그래서이 작업이 자동으로 이루어 지도록하기 위해 .NET Framework를 재정의하는 UILabel 하위 클래스를 사용했습니다 setBounds:. 여기에서 슈퍼 구현을 호출 한 다음 아직 그렇지 않은 경우 선호하는 최대 레이아웃 너비를 경계 크기 너비로 설정합니다.

강조점은 중요합니다. 선호하는 최대 레이아웃을 설정하면 다른 레이아웃 패스가 수행되므로 무한 루프로 끝날 수 있습니다.


고급 자동 레이아웃 도구 상자 의 "여러 줄 텍스트의 기본 콘텐츠 크기"섹션 objc.io대한이 질문에 대한 답변이 있습니다. 관련 정보는 다음과 같습니다.

UILabel 및 NSTextField의 기본 콘텐츠 크기는 여러 줄 텍스트에 대해 모호합니다. 텍스트의 높이는 선의 너비에 따라 달라지며 제약 조건을 해결할 때 아직 결정되지 않았습니다. 이 문제를 해결하기 위해 두 클래스 모두 기본 콘텐츠 크기를 계산하기위한 최대 선 너비를 지정하는 preferredMaxLayoutWidth라는 새 속성이 있습니다.

일반적으로이 값을 미리 알지 못하기 때문에이 값을 올바르게 설정하려면 2 단계 접근 방식을 취해야합니다. 먼저 자동 레이아웃이 작업을 수행하도록 한 다음 레이아웃 패스의 결과 프레임을 사용하여 선호하는 최대 너비를 업데이트하고 레이아웃을 다시 트리거합니다.

뷰 컨트롤러 내에서 사용하기 위해 제공하는 코드 :

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

그들의 게시물을 살펴보면 레이아웃을 두 번 수행해야하는 이유에 대한 자세한 정보가 있습니다.


최신 정보

내 원래 답변이 도움이되는 것 같아서 아래에 그대로 두었지만 내 프로젝트에서 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()
    }
}

UIScrollView 내부의 자동 레이아웃 UILabel이 세로로 잘 배치되었지만 가로로 회전하면 UILabel의 높이가 다시 계산되지 않는 상황이 발생했습니다.

우리는 @jrturton의 대답이 이것을 수정했다는 것을 발견했습니다. 아마도 이제 preferredMaxLayoutWidth가 올바르게 설정 되었기 때문일 것입니다.

다음은 우리가 사용한 코드입니다. 인터페이스 빌더의 Custom 클래스를 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

Using boundingRectWithSize

I resolved my struggle with two multi-line labels in a legacy UITableViewCell that was using "\n" as a line-break by measuring the desired width like this:

- (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;
}

Then calling the method was then as simple as:

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

As I'm not allowed to add a comment, I'm obliged to add it as an answer. The version of jrturton only worked for me if I call layoutIfNeeded in updateViewConstraints before getting the preferredMaxLayoutWidth of the label in question. Without the call to layoutIfNeeded the preferredMaxLayoutWidth was always 0 in updateViewConstraints. And yet, it had always the desired value when checked in setBounds:. I didn't manage to get to know WHEN the correct preferredMaxLayoutWidth was set. I override setPreferredMaxLayoutWidth: on the UILabel subclass, but it never got called.

Summarized, I:

  • ...sublcassed UILabel
  • ...and override setBounds: to, if not already set, set preferredMaxLayoutWidth to CGRectGetWidth(bounds)
  • ...call [super updateViewConstraints] before the following
  • ...call layoutIfNeeded before getting preferredMaxLayoutWidth to be used in label's size calculation

EDIT: This workaround only seems to work, or be needed, sometimes. I just had an issue (iOS 7/8) where the label's height were not correctly calculated, as preferredMaxLayoutWidth returned 0 after the layout process had been executed once. So after some trial and error (and having found this Blog entry) I switched to using UILabel again and just set top, bottom, left and right auto layout constraints. And for whatever reason the label's height was set correctly after updating the text.


As suggested by another answer I tried to override viewDidLayoutSubviews:

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

This worked, but it was visible on the UI and caused a "visible flicker" i.e. first the label was rendered with the height of two lines, then it was re-rendered with the height of only one line.

This was not acceptable for me.

I found then a better solution by overriding 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;
}

This was the better solution for me, because it did not cause the "visual flickering".


A clean solution is to set rowcount = 0 and to use a property for the heightconstraint of your label. Then after the content is set call

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

-(void) updateViewConstraints has a problem since iOS 7.1.


In iOS 8, you can fix multi-line label layout problems in a cell by calling cell.layoutIfNeeded() after dequeuing and configuring the cell. The call is harmless in iOS 9.

See Nick Snyder's answer. This solution was taken from his code at https://github.com/nicksnyder/ios-cell-layout/blob/master/CellLayout/TableViewController.swift.

참고URL : https://stackoverflow.com/questions/17491376/ios-autolayout-multi-line-uilabel

반응형