2010-05-10 7 views
103

누구나 UIView'ssetNeedsLayout, layoutIfNeededlayoutSubviews 방법 사이의 관계에 대한 확실한 설명을 줄 수 있습니까? 그리고 세 가지 모두 사용되는 예제 구현. 감사.UIView의 setNeedsLayout, layoutIfNeeded 및 layoutSubviews 사이의 관계는 무엇입니까?

내가 혼란스러워하는 점은 내 맞춤보기를 보내면 setNeedsLayout 메시지가이 메소드 다음에 호출되는 바로 다음에 layoutSubviews이고 바로 건너 뛰는 것은 layoutIfNeeded입니다. 문서에서 나는 흐름이 setNeedsLayout>이 될 것을 기대할 것입니다>layoutIfNeeded이 호출되어>layoutSubviews이 호출됩니다.

답변

101

나는 아직도 자신을 알아 내려고 노력하고 있습니다. 그래서 이것을 회의론으로 생각하고 오류가 있으면 용서해주십시오.

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는 일반적으로 하위 클래스에서 재정의해야하는 것은 아닙니다. 보기를 배치 할 때 전화 할 방법입니다. 지금입니다. 애플의 구현은 다음과 같이 보일 수 있습니다 당신은 그것을 (그리고 superviews 필요가) 즉시 배치 할 강제로보기에 layoutIfNeeded를 부를 것이다

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

.

+2

감사합니다. - 누군가 기꺼이 대답했습니다. 그 동안에는 drawRect를 호출하는 setNeedsDisplay와 contentMode에 대해서도 알아야했습니다. contentMode = UIViewContentModeRedraw만이 뷰의 경계가 변경 될 때 setNeedsDisplay가 호출되도록합니다. 다른 contentMode를 선택하면보기가 크기 조정되거나, 일부 이동되거나, 가장자리로 정렬됩니다. 내 사용자 지정 UITableViewCell 방향 변경 내용을 다시 그리기 싶지 않았다, UIViewContentModeRedraw contentMode 설정할 때까지 경우에만 확장 할 것입니다. – Tarfa

+0

아마 당신의 코드에서 [self.subview1 layoutSubviews]가 [self.subview1 setNeedsLayout]으로 대체되어야한다고 생각합니다. layoutSubviews는 재정의 될 예정이지만 다른보기에서 또는 다른보기로 호출 할 의도가 없습니다. 프레임 워크는 호출 할시기를 결정합니다 (효율성을 높이기 위해 여러 레이아웃 요청을 하나의 호출로 그룹화 함). 또는 뷰 계층의 어딘가에서 layoutIfNeeded를 호출하여 간접적으로 수행합니다. – Tarfa

+0

위의 내용에 대한 수정 : "... layoutSubviews는 자체에서 재정의하거나 호출해야하기 때문에 다른 뷰에서 호출하거나 다른 뷰로 호출 할 수 없습니다." – Tarfa

33

어떤 경우에는 setNeedsLayoutlayoutIfNeeded을 호출해야하는 n8gray의 대답을 추가하고 싶습니다.

하위보기의 위치가 복잡하고 autoresizingMask 또는 iOS6 AutoLayout으로 수행 할 수없는 UIView를 확장하는 맞춤보기를 작성했다고 가정 해 보겠습니다. 맞춤 위치 지정은 layoutSubviews을 재정 의하여 수행 할 수 있습니다.

예를 들어, contentView 속성과 edgeInsets 속성이있는 사용자 지정보기를 사용하여 contentView 주위의 여백을 설정할 수 있습니다. layoutSubviews는 다음과 같이 보일 것이다 :

: 당신이 edgeInsets 속성을 변경할 때마다 프레임 변화를 애니메이션 할 수있게하려면

- (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 세터를 무시하고 setNeedsLayoutlayoutIfNeeded 다음에 호출 할 필요가

- (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의 프레임 변경이 애니메이션으로 표시됩니다.당신이 setEdgeInsets 방법에 layoutIfNeeded에 대한 호출을 추가하지 않으면 layoutSubviews이 애니메이션 블록 외부에서 호출 동일합니다 다음 업데이트주기에서 호출되는 것이기 때문에

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

, 애니메이션이 작동하지 않습니다.

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

+4

또는 가장자리 인세 트를 설정 한 후 애니메이션 블록 내에서'[self layoutIfNeeded] '를 호출 할 수 있어야합니다. –

관련 문제