자동 레이아웃 제약 조건을 사용할 때 뷰의 현재 너비와 높이를 어떻게 얻을 수 있습니까?


108

나는 프레임 속성에 대해 말하는 것이 아닙니다. 그로부터 당신은 xib에서 뷰의 크기를 얻을 수 있기 때문입니다. 제약 조건 (회전 후 또는 이벤트에 대한 응답으로)으로 인해 뷰의 크기가 조정되는시기에 대해 이야기하고 있습니다. 현재 너비와 높이를 얻는 방법이 있습니까?

너비 및 높이 제약 조건을 찾기 위해 제약 조건을 반복하려고 시도했지만 내부 제약 조건이있을 때 매우 깨끗하지 않고 실패합니다 (둘을 구분할 수 없기 때문에). 또한 실제로 너비 및 높이 제한이있는 경우에만 작동하며 크기를 조정하기 위해 다른 제한에 의존하는 경우에는 작동하지 않습니다.

이게 왜 그렇게 어려운가요? ARG!


특정 시간에 뷰의 경계가 현재 너비와 높이를 유지한다고 생각했을 것입니다. 이것이 레이아웃 프로세스 중에 조정되는 것입니다.
Phillip Mills

흠 나는 경계를 확인하려고 생각하지 않았습니다. 지금 할게요.
yuf

답변:


246

대답은 [view layoutIfNeeded]입니다.

그 이유는 다음과 같습니다.

view.bounds.size.widthview.bounds.size.height(또는을 사용하지 않는 한 동일한 프레임)을 검사하여 뷰의 현재 너비와 높이를 여전히 얻습니다 view.transform.

원하는 것이 기존 제약 조건에 내포 된 너비와 높이 인 경우, 제약 조건을 수동으로 검사하지 않는 것이 정답입니다. 자동 레이아웃 시스템의 전체 제약 해결 논리를 다시 구현해야 해석 할 수 있기 때문입니다. 제약. 대신해야 할 일은 자동 레이아웃에 해당 레이아웃을 업데이트하도록 요청 하여 제약 조건을 해결하고 올바른 솔루션으로 view.bounds 값을 업데이트 한 다음 view.bounds를 검사하는 것입니다.

레이아웃 업데이트를 위해 자동 레이아웃을 어떻게 요청합니까? [view setNeedsLayout]자동 레이아웃이 런 루프의 다음 턴에 레이아웃을 업데이트하도록하려면 호출 하십시오.

그러나 레이아웃을 즉시 업데이트하여 나중에 현재 함수 내에서 또는 실행 루프가 시작되기 전 다른 지점에서 새 경계 값에 즉시 액세스 할 수 있도록하려면[view setNeedsLayout] 및 을 호출해야합니다 [view layoutIfNeeded].

두 번째 질문 : "직접 참조가없는 경우 높이 / 너비 제약 조건을 어떻게 변경할 수 있습니까?"

IB에서 제약 조건을 생성하는 경우 최상의 솔루션은 뷰 컨트롤러 또는 뷰에 IBOutlet을 생성하여 직접 참조 할 수 있도록하는 것입니다. 코드에서 제약 조건을 만든 경우에는 만들 때 내부 약한 속성의 참조를 유지해야합니다. 다른 사람이 제약 조건을 생성 한 경우 뷰의 view.constraints 속성과 전체 뷰 계층 구조를 검사하고 중요한 NSLayoutConstraint를 찾는 논리를 구현하여이를 찾아야합니다. 이 질문에 대한 간단한 대답이 보장되지 않을 때 경계 크기를 결정한 특정 제약 조건을 효과적으로 결정해야하기 때문에 잘못된 방법 일 수 있습니다. 최종 경계 값은 여러 제약 조건의 매우 복잡한 시스템에 대한 솔루션이 될 수 있습니다.


16
우수 답변을 잘도 설명했다. 내 머리카락을 뽑은 후 한두 시간 후에 나를 구했습니다.
Ben Kreeger 2013

2
layoutIfNeeded훌륭하며 프레임을 즉시 사용할 수 있습니다. 그러나 호출되는 뷰의 하위 트리에있는 모든 뷰에 대한 제약 조건을 강제 렌더링합니다. 예를 들어 프로그래밍 방식으로 뷰를 추가하고 layoutIfNeeded재귀 루틴에서 뷰를 모두 호출 하는 경우 뷰 계층 구조가 매우 느리게 렌더링되는 것을 알 수 있습니다. (어려운 방법으로 배웠습니다) 우수한 답변에서 언급했듯이 'setNeedsLayout'이 더 효율적이며 다음 레이아웃 패스에서 프레임을 사용할 수있게합니다. 이상적으로는 약 1/60 초 내에 발생합니다.
shmim

1
나는 이것이 오래되었다는 것을 알고 있지만,이 방법을 어디에서 부릅니까? 에 initWithCoder?
MayNotBe 2014 년

4
경계를 얻기 위해 레이아웃을 강제하는 이유는 무엇입니까? 이것은 비효율적이고 복잡한 인터페이스로 인해 잠재적으로 비용이 많이 듭니다. 대신 viewDidLayoutSubviews 또는 viewWillLayoutSubviews에서 최신 경계에 액세스하고 cocoa가 자체 레이아웃 타이밍을 처리하도록합니다. 문제가되는 경우 여러 호출을 방지하기 위해 값 또는 플래그에 액세스해야하는 경우 속성을 설정합니다.
smileBot 2015 년

1
예, 실행 루프가 시작되기 전에 자동 레이아웃 계산 값에 액세스해야하는 경우에만 레이아웃을 강제 실행하면됩니다 (예 : 현재 함수 호출 범위 내).
algal

8

나는이에 상단과 하단 테두리를 추가 할 필요 비슷한 문제를 가지고 UITableView그에서의 제약 조건 설정에 따라 크기를 조절 UIStoryboard. 을 사용하여 업데이트 된 제약 조건에 액세스 할 수있었습니다 - (void)viewDidLayoutSubviews. 이것은 뷰를 서브 클래 싱하고 레이아웃 메서드를 재정의 할 필요가 없도록 유용합니다.

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

메서드에서 메서드를 호출하지 않으면 viewDidLayoutSubview아래쪽 테두리가 화면 밖의 어딘가에 있으므로 위쪽 테두리 만 올바르게 그려집니다.


3
viewDidLayoutSubviews가 몇 번 호출되어 수많은 레이어를 생성하고 있습니다 ... 다른 레이어 를 추가하기 전에 몇 가지 존재 확인을 추가해야합니다 .
Kalzem

몇 년 늦게 주석을 [super viewDidLayoutSubviews];
달아야

5

특히 TableviewCell을 사용하여 여전히 이러한 문제에 직면 할 수있는 사람들을 위해.

메소드를 재정의하십시오.

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

UITableViewCell 또는 UICollectionViewCell의 경우 셀의 하위 클래스를 만들고 동일한 메서드를 재정의합니다.

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

이것이 내 뷰의 실제 최종 크기를 얻을 수있는 유일한 방법입니다.
Benoit Jadinon 2016-08-24

이것은 내가 제한된 뷰에 대한 최종 크기를 얻을 수있는 유일한 방법이었습니다. 왜냐하면 내가 awakeFromNib에서 그것들을 얻으려고하기 전에는 서브 뷰가 실제로 먼저 크기를 조정할 시간을주지 않았기 때문입니다. 감사합니다!
Azin Mehrnoosh

3

사용 -(void)viewWillAppear:(BOOL)animated및 전화 [self.view layoutIfNeeded];-내가 시도한 작동합니다.

사용 -(void)viewDidLayoutSubviews하면 확실히 작동하지만 UI가 업데이트 / 변경을 요구할 때마다이 메서드가 호출 되기 때문 입니다. 관리하기 어려울 것입니다. 소프트 키는 이러한 호출 루프를 피하기 위해 bool 변수를 사용하는 것입니다. 더 나은 사용 viewWillAppear. 기억 viewWillAppear뷰가 다시로드 된 경우 또한 (재 할당없이) 호출됩니다.


2

프레임은 여전히 ​​유효합니다. 결국 뷰는 프레임 속성을 사용하여 자신을 배치합니다. 모든 제약 조건을 기반으로 해당 프레임을 계산합니다. 제약 조건은 초기 레이아웃에만 사용됩니다 (그리고 회전 후와 같이 view에서 layoutSubviews가 호출 될 때마다). 그 후 위치 정보는 프레임 속성에 있습니다. 아니면 다르게보고 있습니까?


1
나는 달리보고있다. 프레임 값은 너비 / 높이 제약 조건을 직접 변경 한 후에도 변경되지 않습니다 (상수를 변경하여. xib에 IBOutlet이 있습니다)
yuf

제약 조건을 변경 한 다음 동일한 기능에서 프레임을 사용해야하기 때문에 내 문제는 고유합니다. setNeedsLayout을 호출해도 즉시 업데이트되지는 않습니다. 먼저 레이아웃을 기다린 다음 프레임을 사용해야한다고 생각합니다. 두 번째 질문은 직접 참조가없는 경우 높이 / 너비 제약 조건을 어떻게 변경할 수 있습니까?
yuf

1
그런 다음 dispatch_async를 수행하면 다음 프레임까지 코드를 지연시킬 수 있습니다. 두 번째 부분에 관해서는 ... 모르겠습니다 ... 모든 제약 조건을 반복하고 어느 것이 적용 가능한지 찾아야합니다 ... IBOutlets가 훨씬 쉽습니다.
borrrden

IBOutlets의 경우 사실이지만 작업 할 IB가없는 UIView에 대한 범주에 함수를 작성하고 싶습니다.
yuf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.