NSLayoutConstraint“UIView-Encapsulated-Layout-Height”란 무엇이며 어떻게 깨끗하게 다시 계산해야합니까?


262

UITableViewiOS 8 에서 실행 중이며 스토리 보드의 제약 조건에서 자동 셀 높이를 사용하고 있습니다.

내 셀 중 하나에 하나가 포함되어 있으며 UITextView사용자 입력을 기반으로 수축 및 확장해야합니다. 텍스트를 축소 / 확장하려면 탭하십시오.

텍스트 뷰에 런타임 제약 조건을 추가하고 사용자 이벤트에 대한 응답으로 제약 조건의 상수를 변경 하여이 작업을 수행합니다.

-(void)collapse:(BOOL)collapse; {

    _collapsed = collapse;

    if(collapse)
        [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
    else
        [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];

    [self setNeedsUpdateConstraints];

}

내가 할 때마다 tableView업데이트로 포장 하고 전화 [tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView setNeedsUpdateConstraints];
// I have also tried 
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.

[tableView endUpdates];

이 작업을 수행하면 셀이 확장되고 애니메이션이 적용되지만 제약 조건 경고가 표시됩니다.

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] Unable to simultaneously satisfy constraints.

Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 

(

    "<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",

    "<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"
 )

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388은 내 계산 높이이며, 다른 제약 조건 UITextView은 Xcode / IB의 광산입니다.

마지막 것은 나를 귀찮게합니다- UIView-Encapsulated-Layout-Height셀이 처음 렌더링 될 때 계산 된 셀 높이 라고 생각합니다 -( UITextView높이를> = 70.0으로 설정) 그러나이 파생 제약 조건이 업데이트 된 사용자 cnstraint.

더 나쁜 것은 레이아웃 코드가 높이 제한을 위반하려고한다고하지만 셀 높이를 다시 계산하지 않고 원하는대로 모든 것을 그립니다.

그렇다면 NSLayoutConstraint UIView-Encapsulated-Layout-Height(자동 셀 크기 조정을 위해 계산 된 높이라고 생각합니다) 무엇이고 깨끗하게 다시 계산하도록하려면 어떻게해야합니까?


3
크로스 애플 dev 포럼에 게시 : devforums.apple.com/thread/238803
Rog

3
다음과 같은 방식으로 비슷한 문제를 해결했으며 iOS 7/8에서 작동합니다. 1) 제약 우선 순위 중 하나를 750으로 낮추십시오. 1 또는 2를 시도합니다. 2) 셀 하위 클래스에서 awakeFromNib set self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;. 처음에 자동 크기 조정 마스크를 설정하면 마지막 제약 조건이 추가되지 않습니다. 이 솔루션을 여기에서 찾았습니다 : github.com/wordpress-mobile/WordPress-iOS/commit/…
Jesse

3
@RogerNolan, 뉴스가 있습니까? Interface Builder에서 자동 레이아웃으로 재생하는 동안 동일한 문제가 발견되었습니다. 일부 세포는이 문제를 일으 킵니다.
orkenstein

3
자동 크기 조정 표시를 추가하는 것이 좋은 해결책이라고 생각하지 않습니다.
Rog

1
@RogerNolan 이러한 레이아웃 변경을 수행하기 전에 IB에서이 셀을 생성합니까? 동일한 문제를 디버깅했지만 추가 제약 조건을 추가하지 않았습니다. 나는 처음부터 내 견해를 재구성하여 경고를 억제했으며, 2 개의 스토리 보드 파일을 비교했을 때 유일한 차이점은 경고가있는 버전 <rect key="frame" x="0.0" y="0.0" width="600" height="110"/>의 정의에 줄이 누락 되어 이것이 IB 버그라고 생각하게 만드는 것입니다 . 어쨌든 적어도 내 것이 었습니다.
Ell Neal

답변:


301

우선 순위 _collapsedtextHeightConstraint를 999 로 낮추십시오 . 시스템 제공 UIView-Encapsulated-Layout-Height제한 조건이 항상 우선합니다.

반환 내용을 기반으로합니다 -tableView:heightForRowAtIndexPath:. 올바른 값과 자신의 제약 조건을 반환해야하며 생성 된 제약 조건은 동일해야합니다. 축소 / 확장 애니메이션이 비행하는 동안 충돌을 방지하기 위해 자신의 제약 조건에 대한 우선 순위가 낮을 필요가 있습니다.


74
그것은 내가 원하는 것과 정반대를 이룰 것입니다. UIView-Encapsulated-Layout-Height가 잘못되었습니다-이전 레이아웃에 속합니다.
Rog

7
UIView-Encapsulated-Layout-Height높이가 결정되면 제약 jQuery과 추가됩니다. systemLayoutSizeFittingSizecontentView를 기반으로 높이를 계산합니다 . 여기서는 UIView-Encapsulated-Layout-Height중요하지 않습니다. 그런 다음 tableView는 contentSize를 명시 적으로에서 반환 한 값으로 설정합니다 heightForRowAtIndexPath:. 이 경우 rowHeights가 계산 된 후 tableView 제약 조건이 우선해야하므로 사용자 지정 제약 조건의 우선 순위를 낮추는 것이 좋습니다.
Ortwin Gentz

8
@OrtwinGentz ​​: rowHeights가 계산 된 후 tableView 제약 조건이 우선해야하므로 사용자 지정 제약 조건의 우선 순위를 낮추는 것이 여전히 옳지 않습니다 . UIView-Encapsulated-Layout-Height우선 순위를 낮추지 않으면 문제 가되는 것입니다 .
테스트 :

38
실제 문제를 해결하지 않고 충돌을 피할 수 있다고 유지합니다. 여전히 애플 버그처럼 느껴지더라도 아마도 옳은 일이라고 생각합니다. 특히 Apple 이이 제약 조건을 다시 계산하고 오류가 인쇄 된 후 모든 것이 올바르게 배치된다는 사실에 비추어 볼 때.
Rog

9
추가되는 제약 조건이 올바른 것으로 가정하므로이 답변에 -1입니다. UIView-Encapsulated-Layout-Width필자의 경우 추가 된 것은 잘못되었지만 런타임시 명시 적 제약보다 선호되는 것 같습니다.
ray

68

UILabel 객체가 몇 줄있는 행 셀이있는 테이블 뷰와 비슷한 시나리오가 있습니다. iOS 8과 자동 레이아웃을 사용하고 있습니다.

회전했을 때 잘못된 시스템 계산 행 높이를 얻었습니다 (43.5는 실제 높이보다 훨씬 작습니다). 다음과 같습니다.

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

단순한 경고가 아닙니다. 테이블 뷰 셀의 레이아웃이 끔찍합니다. 모든 텍스트가 한 텍스트 줄에 겹칩니다.

다음 줄이 내 문제를 마술처럼 "수정"한다는 사실에 놀랐습니다 (자동 레이아웃은 아무것도 불평하지 않고 화면에 예상 한 것을 얻습니다).

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

이 줄의 유무에 관계없이 :

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem

8
드디어! 정답입니다. WWDC 세션에서 자동 행 높이 크기 조정을 사용하려는 경우 추정 된 RowHeight를 설정하지 않으면 나쁜 일이 발생합니다. (예, 애플은 정말 불쾌한 일이 일어난)
Abdalrahman 임칙서

50
FWIW, 견적을 추가해도 아무런 차이가 없습니다.
Benjohn

3
허. 나는이 같은 대답으로 다시 돌아와서 기쁨과 희망으로 그것을 이행했습니다. 다시 한번, 그것은 나에게 아무런 영향을 미치지 않았다 :-)
Benjohn

3
estimatedHeightForRowAtIndexPath :있는 tableView 의해 반환 예상 해야 적어도 셀 큰 따른다. 그렇지 않으면 계산 된 테이블 높이가 실제 높이보다 낮아지고 테이블이 위로 스크롤 될 수 있습니다 (예 : 풀기 segue가 테이블로 돌아간 후). UITableViewAutomaticDimension을 사용하지 않아야합니다.
Matt

1
낮을수록 낮을수록 estimatedRowHeight더 자주 cellForRowAtIndexPath호출됩니다. tableview 높이를 추정 된 rowHeight로 나눈 값입니다. 12 인치 iPad Pro의 경우,이 숫자는 수천의 숫자 일 수 있으며 데이터 소스를 망치게되어 상당한 지연을 초래할 수 있습니다.
Mojo66

32

경고 메시지가 (아래 ) 깨져야한다고 말하는 제약 조건의 값 중 하나에 우선 순위 를 지정하여 경고가 사라질 수있었습니다 "Will attempt to recover by breaking constraint". 우선 순위를보다 큰 것으로 설정 49하면 경고가 사라지는 것으로 보입니다 .

나에게 이것은 내 제약 조건을 바꾸는 것을 의미했다.

@"V:|[contentLabel]-[quoteeLabel]|"

에:

@"V:|-0@500-[contentLabel]-[quoteeLabel]|"

사실, 나는 그 제약 조건의 요소에 우선 순위를 추가 할 수 있으며 작동합니다. 어느 것이 중요하지 않은 것 같습니다. 내 세포가 적절한 높이로 끝나고 경고가 표시되지 않습니다. 예를 들어 Roger @500388높이 값 제약 조건 바로 뒤에 추가하십시오 (예 :) 388@500.

왜 이것이 효과가 있는지 잘 모르겠지만 조금 조사했습니다. 에서 NSLayoutPriority 열거 , 나타납니다 NSLayoutPriorityFittingSizeCompression우선 순위입니다 50. 해당 우선 순위 레벨에 대한 문서는 다음과 같습니다.

fittingSize 메시지를 뷰로 보내면 뷰의 내용에 대해 가장 작은 크기가 계산됩니다. 이 계산에서 뷰가 가능한 작게되기를 원하는 우선 순위 레벨입니다. 꽤 낮습니다. 일반적으로 정확하게이 우선 순위로 제한하는 것은 적절하지 않습니다. 당신은 더 높거나 더 낮아지기를 원합니다.

설명서를 참조에 대한 fittingSize메시지를 읽습니다 :

보유하고있는 구속 조건을 만족하는 뷰의 최소 크기입니다. (읽기 전용)

AppKit은이 속성과 뷰가 유지하는 모든 제약 조건을 고려하고 뷰를 가능한 작게 만들기위한 기본 설정을 만족시키는 것을 고려하여이 속성을 뷰에 사용 가능한 최상의 크기로 설정합니다. 이 속성의 크기 값은 음수가 아닙니다.

나는 그것을 넘어서지 않았지만 이것이 문제가있는 곳과 관련이 있다는 것이 의미가있는 것 같습니다.


Thatks Jeff. 여전히 나에게 버그 인 것 같습니다. 애플은 비록 레이더에 응답하지 않았다 :-(
Rog

13

사용자 지정 셀 또는 헤더를 사용하는 동안 시간의 99.9 %는 UITableViews테이블이 처음로드 될 때 충돌이 발생합니다. 일단로드되면 일반적으로 충돌이 다시 나타나지 않습니다.

이것은 대부분의 개발자가 일반적으로 고정 된 높이 또는 앵커 구속 조건을 사용하여 셀 / 헤더의 요소를 레이아웃하기 때문에 발생합니다. 충돌은 UITableView첫 번째로드 / 배치시 셀의 높이를 0으로 설정 하기 때문에 발생 합니다. 이는 분명히 자신의 제약 조건과 충돌합니다. 이 문제를 해결하려면 고정 된 높이 구속 조건을 우선 순위가 낮은 ( .defaultHigh)로 설정하십시오. 콘솔 메시지를주의 깊게 읽고 레이아웃 시스템이 어떤 제약을 가하기로 결정했는지 확인하십시오. 일반적으로 우선 순위를 변경해야합니다. 다음과 같이 우선 순위를 변경할 수 있습니다.

let companyNameTopConstraint = companyNameLabel.topAnchor.constraint(equalTo: companyImageView.bottomAnchor, constant: 15)
    companyNameTopConstraint.priority = .defaultHigh

NSLayoutConstraint.activate([
            companyNameTopConstraint,
           the rest of your constraints here
            ])

1
아름다운 설명.
Glenn

12

나는 가짜를 제거하여이 오류를 해결 할 수있었습니다 cell.layoutIfNeeded()내에서 가지고 있음을 tableViewcellForRowAt방법.


1
예, 이것은 저에게도 문제를 해결했습니다. 코드 레이아웃 제약 조건을 수행했기 때문에 처음에 뭔가 빠질 것이라고 생각했습니다. 감사합니다
John

1
똑같은 것! 감사!
Andrey Chernukha

7

제약 조건을 업데이트하도록 테이블 뷰에 알리는 대신 셀을 다시로드하십시오.

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

UIView-Encapsulated-Layout-Height 아마도 해당 시점의 셀 제약 조건에 따라 초기로드 중 셀에 대해 테이블 ​​뷰가 계산 한 높이 일 것입니다.


내 질문에 언급 했어야했는데, 시도했지만 작동하지 않습니다. UIView-Encapsulated-Layout-Height
Rog

6
어쨌든 적어도 답변을 제출 한 바운티를 가지십시오. SO가 증발하지 못하게하는 것처럼 보입니다.
Rog

6

또 다른 가능성 :

자동 레이아웃을 사용하여 셀 높이 (contentView의 높이, 대부분 아래와 같은 시간)를 계산하고 uitableview 구분 기호가있는 경우 셀 높이를 반환하려면 구분 기호 높이를 추가해야합니다. 정확한 높이를 얻으면 자동 레이아웃 경고가 표시되지 않습니다.

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
   [sizingCell setNeedsLayout];
   [sizingCell layoutIfNeeded];
   CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
   return size.height; // should + 1 here if my uitableviewseparatorstyle is not none

}

일부 시뮬레이터 (iPad 6 Plus)에서만 매우 복잡한 셀 레이아웃으로 레이아웃 높이 모호성을 얻은 경우에 도움이되었습니다. 내부 반올림 오류로 인해 내용이 약간 압착되어 있고 제약 조건이 압착 될 준비가되지 않은 경우 모호성을 얻은 것 같습니다. 그래서 반환하는 대신 UITableViewAutomaticDimensionheightForRowAtIndexPath내가 반환 [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] + 0.1
레오

물론 이죠[sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.1
Leo

엄청나게; 구분 기호를 제거하면 테이블이 작동합니다. 감사합니다.
royalmurder

5

Jesse 가 질문의 의견에서 언급했듯이 이것은 나를 위해 작동합니다.

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

참고로, iOS 10에서는이 문제가 발생하지 않습니다.


2
Swift 4.2에서 : self.contentView.autoresizingMask = [.flexibleHeight]
airowe

4

UITableViewAutomaticDimension을 사용하고 셀 내부의 뷰에서 높이 제약 조건을 변경할 때이 오류가 발생했습니다.

나는 그것이 제약 상수 값이 가장 가까운 정수로 반올림되지 않았기 때문에 마침내 알아 냈습니다.

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!

2
이것은 내 문제를 제기 한 의견이었습니다. 동적으로로드 (확장 및 축소)되는 이미지 셀과 추정 RowHeight = 50 및 rowHeight = UITableViewAutomaticDimension이있는 테이블 뷰가 있습니다. tableview가 올바른 높이인데도 여전히 제약 조건을 위반했습니다. 구분 기호의 높이가 0.3333이고 셀의 이미지 크기 제한이 깨지는 것으로 나타났습니다. 세퍼레이터를 끈 후 모든 것이 잘되었습니다. 무엇을 찾아 주셔서 감사합니다.
migs647

1
이 경우 추가 제한 조건 (예 : bottomMargin> = view.bottomMargin+1@900)을 작성하십시오. AutoLayout은 여분의 1 포인트를 수용하고 셀 크기를 조정하며 구분 기호 높이로 인해 혼란스러워지고 일부 제약 조건을 깨거나 해제하고 @ 900을 찾은 다음 버립니다. 경고없이 원하는 레이아웃을 얻습니다.
Anton

내 하루를 구하십시오. 어쨌든 몇 시간
Anton Tropashko

1

내용에 맞게 텍스트보기의 크기를 조정하고 높이 제약 상수를 결과 높이로 업데이트하면 UIView-Encapsulated-Layout-Height제약 조건 충돌이 해결되었습니다.

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;

어디서 했어요? layoutSubviews에서?
stuckj

1

이 버그로 머리를 긁는 데 몇 시간을 보낸 후 마침내 나에게 맞는 해결책을 찾았습니다. 내 주요 문제는 다른 셀 유형에 대해 여러 개의 펜촉이 등록되어 있지만 하나의 셀 유형이 서로 다른 크기를 가질 수 있다는 것입니다 (해당 셀의 모든 인스턴스가 동일한 크기가 아님). 그래서 테이블 뷰가 해당 유형의 셀을 대기열에서 빼려고 시도했을 때 문제가 발생했으며 높이가 다릅니다. 설정으로 해결했습니다

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

셀에 크기를 계산할 데이터가있을 때마다 나는 그것이 될 수 있다고 생각

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

같은

cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

도움이 되었기를 바랍니다!


0

TableView는 대리자로부터 indexPath의 셀 높이를 가져옵니다. 그런 다음 셀을 가져옵니다 cellForRowAtIndexPath.

top (10@1000)
    cell
bottom (0@1000)

cell.contentView.height : 0 // <-> (UIView-Encapsulated-Layout-Height : 0 @ 1000) top (10 @ 1000)이 (UIView-Encapsulated-Layout-Height : 0 @ 1000)과 충돌하는 경우,

우선 순위는 1000과 같습니다. 우선 순위에서 최우선 순위를 설정해야합니다 UIView-Encapsulated-Layout-Height.


0

다음과 같은 메시지가 나타납니다.

동시에 ... 제약 조건을 충족 할 수 없습니다
...
...
...
0x7fe74bdf7e50 'UIView의 캡슐화 - 레이아웃 높이'V는 : [UITableViewCellContentView : 0x7fe75330c5c0은 (21.5)]를 NSLayoutConstraint이
...
...
에 의해 복구하려고합니다 구속 조건 제한 NSLayoutConstraint : 0x7fe0f9b200c0 UITableViewCellContentView : 0x7fe0f9b1e090.bottomMargin == UILabel : 0x7fe0f9b1e970.bottom

높이에 대한 사용자 정의 UITableViewCell를 사용하고 UITableViewAutomaticDimension있습니다. 그리고 그 estimatedHeightForRowAtIndex:방법 도 구현했습니다 .

나에게 문제를주는 제약은 다음과 같이 보였다.

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

제약 조건을 이것으로 변경하면 문제가 해결되지만 다른 대답과 마찬가지로 이것이 필요한 제약 조건의 우선 순위를 낮추기 때문에 이것이 올바르지 않다고 생각했습니다.

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6@999-[title]-6@999-|" options:0 metrics:nil views:views];

그러나 내가 주목 한 것은 실제로 우선 순위를 제거하면 이것이 작동하고 주요 제약 조건 로그를 얻지 못한다는 것입니다.

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

이 차이가 사이에 무엇인지에 관해서는 신비의 조금있다 |-6-[title]-6-|하고 |-[title-|. 그러나 크기를 지정하는 것은 나에게 문제가 아니며 로그를 제거하므로 필요한 제약 조건의 우선 순위를 낮출 필요가 없습니다.


0

view.translatesAutoresizingMaskIntoConstraints = NO;이 문제를 해결하려면 설정하십시오 .


완벽한 솔루션이 아니더라도 완벽하게 작동했습니다.
Enkha

0

컬렉션 뷰 셀과 비슷한 문제가있었습니다.

셀 하단에 연결된 최종 구속 조건의 우선 순위 (보기의 상단에서 하단까지 체인의 마지막 제약 조건-이것이 궁극적으로 높이를 결정하는 것)를 999로 낮추어 문제를 해결했습니다.

셀의 높이가 정확하고 경고가 사라졌습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.