UIView의 setNeedsLayout, layoutIfNeeded 및 layoutSubviews 간의 관계는 무엇입니까?


105

누구든지 UIView's setNeedsLayout, layoutIfNeededlayoutSubviews메서드 의 관계에 대한 명확한 설명을 할 수 있습니까 ? 그리고 세 가지가 모두 사용되는 예제 구현입니다. 감사.

나를 혼란스럽게하는 것은 내가 사용자 정의보기에 setNeedsLayout메시지를 보내면 이 메소드 이후에 호출되는 바로 다음 작업은 layoutSubviews바로 건너 뛰는 것 layoutIfNeeded입니다. 문서에서 나는 흐름이 setNeedsLayout> layoutIfNeeded호출되는 원인> 호출되는 원인 이 될 것으로 예상합니다 layoutSubviews.

답변:


104

나는 여전히 이것을 스스로 알아 내려고 노력하고 있으므로 약간의 회의를 가지고 이것을 받아들이고 오류가 있으면 나를 용서하십시오.

setNeedsLayout쉬운 것입니다. UIView 어딘가에 레이아웃이 필요한 것으로 표시하는 플래그를 설정합니다. layoutSubviews다음 다시 그리기가 발생하기 전에 뷰에서 강제 로 호출됩니다. 대부분의 경우 autoresizesSubviews속성 때문에 이것을 명시 적으로 호출 할 필요가 없습니다 . 이것이 설정되면 (기본적으로) 뷰의 프레임을 변경하면 뷰가 하위 뷰를 레이아웃하게됩니다.

layoutSubviews모든 흥미로운 일을하는 방법입니다. drawRect원한다면 레이아웃 과 동일 합니다. 간단한 예는 다음과 같습니다.

-(void)layoutSubviews {
    // Child's frame is always equal to our bounds inset by 8px
    self.subview1.frame = CGRectInset(self.bounds, 8.0, 8.0);
    // It seems likely that this is incorrect:
    // [self.subview1 layoutSubviews];
    // ... and this is correct:
    [self.subview1 setNeedsLayout];
    // but I don't claim to know definitively.
}

AFAIK layoutIfNeeded는 일반적으로 하위 클래스에서 재정의되는 것을 의미하지 않습니다. 지금 바로 뷰를 배치하고 싶을 때 호출해야하는 메서드입니다 . Apple의 구현은 다음과 같습니다.

-(void)layoutIfNeeded {
    if (self._needsLayout) {
        UIView *sv = self.superview;
        if (sv._needsLayout) {
            [sv layoutIfNeeded];
        } else {
            [self layoutSubviews];
        }
    }
}

layoutIfNeeded뷰를 요청 하여 뷰 (및 필요에 따라 수퍼 뷰)가 즉시 배치되도록 할 수 있습니다.


2
감사합니다-누군가가 마침내 대답 해 주셔서 기쁩니다. 그 동안 나는 또한 setNeedsDisplay (drawRect가 호출되도록 함)와 contentMode를 조사해야했습니다. contentMode = UIViewContentModeRedraw 만보기의 경계가 변경 될 때 setNeedsDisplay가 호출되도록합니다. 다른 contentMode 선택 사항은 뷰의 크기를 조정하거나, 일정량만큼 이동하거나, 가장자리에 정렬되도록합니다. 내 사용자 정의 UITableViewCell은 방향 변경시 다시 그리기를 원하지 않았으며 contentMode를 UIViewContentModeRedraw로 설정할 때까지 크기가 조정됩니다.
Tarfa

아마 코드의 [self.subview1 layoutSubviews]를 [self.subview1 setNeedsLayout]로 바꿔야한다고 생각합니다. layoutSubviews는 재정의를 의미하지만 다른 뷰에서 호출되거나 다른 뷰로 호출되는 것은 아닙니다. 프레임 워크가 호출시기를 결정하거나 (효율성을 위해 여러 레이아웃 요청을 하나의 호출로 그룹화하여) 간접적으로 뷰 계층 구조의 어딘가에서 layoutIfNeeded를 호출하여 수행합니다.
Tarfa

위 주석 수정 : "... layoutSubviews는 재정의되거나 자체에서 호출되지만 다른 뷰에서 호출되지는 않기 때문에 ..."
Tarfa

에 대해 잘 모르겠습니다 [subview1 layoutSubviews]. ,처럼 drawRect직접 전화를해서는 안됩니다. 그러나 setNeedsLayout레이아웃 단계에서 호출 하면 동일한 레이아웃 단계에서 뷰가 레이아웃되는지 또는 다음 단계까지 지연 되는지 확실하지 않습니다 . 이 모든 것이 어떻게 작동하는지 정말로 이해하는 누군가의 답변을 보는 것이 좋을 것입니다 ...
n8gray

34

나는 어떤 경우에는 당신이 호출해야한다는 n8gray의 대답에 추가 할 setNeedsLayout다음에 layoutIfNeeded.

예를 들어 하위 뷰의 위치가 복잡하고 autoresizingMask 또는 iOS6 AutoLayout으로 수행 할 수없는 UIView를 확장하는 사용자 정의 뷰를 작성했다고 가정 해 보겠습니다. 을 재정 의하여 사용자 지정 위치 지정을 수행 할 수 있습니다 layoutSubviews.

예를 들어 contentView속성 이있는 사용자 지정보기 와 edgeInsetscontentView 주변에 여백을 설정할 수 있는 속성이 있다고 가정 해 보겠습니다 . layoutSubviews다음과 같이 보일 것입니다.

- (void) layoutSubviews {
    self.contentView.frame = CGRectMake(
        self.bounds.origin.x + self.edgeInsets.left,
        self.bounds.origin.y + self.edgeInsets.top,
        self.bounds.size.width - self.edgeInsets.left - self.edgeInsets.right,
        self.bounds.size.height - self.edgeInsets.top - self.edgeInsets.bottom); 
}

edgeInsets속성을 변경할 때마다 프레임 변경에 애니메이션을 적용하려면 edgeInsets다음과 같이 setter 를 재정의하고 setNeedsLayout뒤에 다음을 호출 해야합니다 layoutIfNeeded.

- (void) setEdgeInsets:(UIEdgeInsets)edgeInsets {
    _edgeInsets = edgeInsets;
    [self setNeedsLayout]; //Indicates that the view needs to be laid out 
                           //at next update or at next call of layoutIfNeeded, 
                           //whichever comes first 
    [self layoutIfNeeded]; //Calls layoutSubviews if flag is set
}

이렇게하면 다음을 수행하면 애니메이션 블록 내에서 edgeInsets 속성을 변경하면 contentView의 프레임 변경이 애니메이션으로 표시됩니다.

[UIView animateWithDuration:2 animations:^{
    customView.edgeInsets = UIEdgeInsetsMake(45, 17, 18, 34);
}];

setEdgeInsets 메서드에서 layoutIfNeeded에 대한 호출을 추가하지 않으면 다음 업데이트주기에서 layoutSubviews가 호출되므로 애니메이션 블록 외부에서 호출하는 것과 동일하므로 애니메이션이 작동하지 않습니다.

setEdgeInsets 메서드에서 layoutIfNeeded 만 호출하면 setNeedsLayout 플래그가 설정되지 않았으므로 아무 일도 일어나지 않습니다.


4
또는 [self layoutIfNeeded]가장자리 삽입을 설정 한 후 애니메이션 블록 내에서 호출 할 수 있어야합니다 .
스콧 피스터
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.