레이아웃 제약은 언제 활성화 / 비활성화 할 수 있습니까?


104

IB에 여러 제약 조건 세트를 설정했으며 일부 상태에 따라 프로그래밍 방식으로 전환하고 싶습니다. 거기의 constraintsAIB에서 설치로 표시된 모두 출구 컬렉션, 그리고 constraintsB모든 출구 수집은 IB에서 제거됩니다.

다음과 같이 프로그래밍 방식으로 두 세트간에 전환 할 수 있습니다.

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

근데 ... 언제 해야할지 모르겠어요 . 에서 한 번 할 수 있어야 할 것 같지만 viewDidLoad작동하지 않습니다. 나는 전화 해봤 view.updateConstraints()view.layoutSubviews()제약 조건을 설정 한 후,하지만 소용.

viewDidLayoutSubviews모든 제약 조건을 설정하면 예상대로 작동 한다는 것을 알았습니다 . 두 가지를 알고 싶은 것 같아요 ...

  1. 이 동작이 발생하는 이유는 무엇입니까?
  2. viewDidLoad에서 제약 조건을 활성화 / 비활성화 할 수 있습니까?

2
deactivateConstraints 및 activateConstraints가 viewWillLayoutSubviews에서 작동했음을 의미합니까? 나는 그것을 시도했지만 거기 또는 viewDidLoad에서 작동하지 않았습니다. viewDidAppear에서 작동했습니다. 뷰는 새로운 제약 조건이 배치되어야하는 곳에 나타 났지만, 내가 가로 방향으로 회전하면 뷰는 IB에 설정된 제약 조건에 의해 결정된 위치로 다시 이동했습니다 (그리고 다시 세로 방향으로 회전했을 때 거기에 머물 렀습니다). 제약 조건을 기록하고 올바른 제약 사항 (새로 활성화 된 것)을 표시했습니다. 이것은 나에게 버그처럼 보입니다.
rdelmar

1
예, 그들은 유효했습니다 (viewDidAppear에서 작동했습니다). viewWillLayoutSubviews의 기본 구현이 없기 때문에 super를 호출 할 필요가 없습니다 (어쨌든 super를 호출하여 시도했지만 차이가 없었습니다).
rdelmar

1
@rdelmar 방금 더 테스트 할 기회를 얻었습니다 ... 실제로 설명한 것과 동일한 동작이 있는지 확인할 수 있습니다 ... 처음에는 viewDidAppear에서 작동하지만 회전시 되돌아갑니다.
tybro0103

3
이 목적으로 제약 조건을 IB에 설치되지 않은 것으로 표시 할 수 없습니다. 여기에서 해당 정보를 찾았습니다. stackoverflow.com/questions/27663249/… 그리고 그것은 나를 위해 문제를 해결했습니다.
Stefan

1
viewDidAppear에서 일부를 활성화 / 비활성화 한 것을 제외하고는 질문에 설명 된 것과 동일한 방식으로 제약 조건을 구현했습니다. 이것은 효과가 있었지만 요소의 위치가 빠르게 변경되는 것을 볼 수 있습니다 (사소하지만 바람직하지 않은 문제). viewWillAppear 또는 viewDidLoad의 변경이 작동하지 않았습니다. 그러나이 질문을 읽은 후 viewDidLayoutSubviews에서 변경을 시도했습니다. 작동했고 위치 변경은 더 이상 사용자에게 표시되지 않습니다. (또한 viewWillLayoutSubviews에서도 작동했습니다). 그 팁에 감사드립니다!
peacetype

답변:


185

에서 활성화 및 비활성화 NSLayoutConstraints했는데 viewDidLoad아무런 문제가 없습니다. 그래서 작동합니다. 앱과 내 앱 사이에 설정에 차이가 있어야합니다. :-)

내 설정에 대해 설명하겠습니다. 아마도 당신에게 리드를 줄 수 있습니다.

  1. @IBOutlets활성화 / 비활성화하는 데 필요한 모든 제약 조건을 설정했습니다 .
  2. 에서는 ViewController약하지 않은 클래스 속성에 제약 조건을 저장합니다. 그 이유는 제약 조건을 비활성화 한 후 다시 활성화 할 수 없다는 것을 발견했기 때문입니다. 따라서 비활성화되면 삭제되는 것 같습니다.
  3. 나는 NSLayoutConstraint.deactivate/activate당신처럼 사용하지 않고 대신 constraint.active = YES/를 NO사용합니다.
  4. 제약 조건을 설정 한 후 view.layoutIfNeeded().

131
"약하지 않은 클래스 속성에 제약 조건을 저장하십시오"당신은 저에게 많은 시간을 절약했습니다. 감사합니다!
OpenUserX03

10
"약하지 않은 클래스 속성에 제약 조건을 저장합니다": 이로 인해 많은 고통을 덜 수있었습니다. nil 객체에서 선택자를 호출하고 있다는 것을 몰랐습니다. 감사!!
static0886

4
"비활성"제약 조건은 자동 레이아웃에서 무시되지 않고 제거된다는 점에 유의하십시오. 제약 조건을 활성화 / 비활성화하면 실제로 제약 조건이 추가되고 제거됩니다. 이전에 설정 .active = false했던 제약 조건을 추가 한 후 충돌하는 자동 레이아웃을 디버깅하는 데 시간을 보냈 습니다.
lbarbosa

1
약하지 않은 클래스 속성에 제약 조건을 저장하십시오. 좋습니다. 이것은 많은 시간을 절약 할 수 있습니다. 저는 이것없이 혼합 된 결과를 얻었습니다. 감사합니다!
MegaManX

3
Apples 문서에 따르면 : 제약 조건을 활성화하거나 비활성화하면 이 제약 조건에 의해 관리되는 항목의 가장 가까운 공통 조상 인 뷰에서 addConstraint ( :) 및 removeConstraint ( :)를 호출 합니다. addConstraint ( :) 또는 removeConstraint ( :)를 직접 호출하는 대신이 속성을 사용합니다 . 따라서 제약 조건이 비활성화되면 제거되고 IBOutlet이 강하지 않으면 제약 조건에 대한 강한 참조가 남아 있지 않은 것 같습니다. 따라서 제약 조건이 삭제됩니다. IMHO 이것은 거의 버그이거나 적어도 예상치 못한 동작입니다.
Olle Raab

52

어쩌면 당신은 할 수 귀하의 확인 @properties, 교체 weak와 함께strong .

때로는 active = NOset 때문에 다시 self.yourConstraint = nil사용할 수 없습니다 self.yourConstraint.


5
Swift Language Guide 에서 언급했듯이 속성은 기본적으로 강력하므로 제거 만하면 weak됩니다.
Jonathan Cabrera

30
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

1
내 뷰 컨트롤러는 자식 뷰 컨트롤러이기 때문에 "didLayoutSubviews"에서 수행하는 것이 유일한 방법 인 것 같습니다 !! 참고로.
TalL

이것은 유일하게 유효한 대답이다
유누스 에렌 구젤

@TalL 자식 뷰 컨트롤러 자체에 대한 제약을 의미 했습니까, 아니면 하위 뷰입니까?
스테판

이 최고입니다
ACAkgul

14

나는 당신이 겪고있는 문제는 AFTER viewDidLoad()가 호출 될 때까지 그들의 뷰에 추가되지 않는 제약 때문이라고 생각합니다 . 다음과 같은 여러 옵션이 있습니다.

A) 레이아웃 제약 조건을 IBOutlet에 연결하고 이러한 참조를 통해 코드에서 액세스 할 수 있습니다. 콘센트는 viewDidLoad()킥오프 전에 연결되어 있으므로 제약 조건에 액세스 할 수 있어야하며 계속해서 활성화 및 비활성화 할 수 있습니다.

B)constraints() 다양한 제약 조건에 액세스 하기 위해 UIView의 기능을 사용하려면 viewDidLayoutSubviews()시작 하기 를 기다려야 합니다. 그것이 설치된 제약 조건이있는 펜촉에서 뷰 컨트롤러를 만든 후 첫 번째 지점이기 때문입니다. layoutIfNeeded()완료되면 전화하는 것을 잊지 마십시오 . 이것은 적용 할 변경 사항이있는 경우 레이아웃 패스가 두 번 수행된다는 단점이 있으며 무한 루프가 트리거 될 가능성이 없는지 확인해야합니다.

간단한 경고 : 비활성화 된 제약 조건은 메서드에 의해 반환되지 않습니다 constraints() ! 즉, 나중에 다시 켜려는 의도로 제약 조건을 비활성화하면 해당 참조를 유지해야합니다.

C) 스토리 보드 접근 방식을 잊어 버리고 대신 수동으로 제약 조건을 추가 할 수 있습니다. 이 작업을 수행하고 viewDidLoad()있으므로 즉시 레이아웃을 변경하는 대신 객체의 전체 수명 동안 한 번만 수행하는 것으로 가정하므로 허용되는 방법이어야합니다.


10

priority속성을 "활성화"및 "비활성화"하도록 조정할 수도 있습니다 (예 : 활성화하려면 750 값, 비활성화하려면 250). 어떤 이유로 activeBOOL을 변경해도 UI에 영향을 미치지 않았습니다. 필요 layoutIfNeeded하지 않으며 viewDidLoad 또는 그 이후에 언제든지 설정 및 변경할 수 있습니다.


아주 좋은 제안입니다. 제약 우선 순위 변경은 viewWillTransition(to:, with:)또는 에서 작동하며 viewWillLayoutSubviews()모든 대체 제약 조건을 스토리 보드에 "설치된"상태로 유지할 수 있습니다. 제약 우선 순위는 필수가 아닌 것에서 필수로 변경되지 않을 수 있으므로 아래 값을 사용하세요 1000. 반면에 제약 조건 활성화 (추가) 및 비활성화 (제거)는에서만 작동 하며 -s에 대한 참조를 viewDidLayoutSubviews()유지해야합니다 . strong @IBOutletNSLayoutConstraint
Gary

"어떤 이유로 활성 BOOL을 변경해도 UI에 영향을주지 않았습니다." 여기를 기반으로 합니다 . 내가 생각하는 런타임 동안 1000 우선 순위 제약 조건을 변경할 수 없습니다. 비활성화하려면 초기 우선 순위를 999 이하로 설정해야합니다 ....
Honey

나는 문제를 디버그하기 어렵게 만들 수 있고 질문에 대답하지 않기 때문에이 진술에 동의하지 않는다. 우선 순위를 250으로 설정해도 제약 조건이 "비활성화"되지 않으며 여전히 효과가 있고 레이아웃에 영향을줍니다. 대부분의 경우 제약 조건을 "비활성화"하는 것처럼 보일 수 있지만 모든 경우에 확실히 그런 것은 아닙니다. (특히, 저를 인도하지 않는 경우는이 질문에 대한 답을 찾을 수)
Tumata

"설치된 제약 조건에 맞지 않는 우선 순위를 필수에서 변경 (또는 그 반대로)하는 것은 지원되지 않습니다. 우선 순위 250을 통과했으며 기존 우선 순위는 1000이었습니다."와 같이 충돌이 발생할 수 있습니다.
Karthick Ramesh

8

사용하지 않는 제약 조건을 비활성화하는 적절한 시간 :

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

명심 viewWillLayoutSubviews좋아, 무거운 계산하므로, 여러 번 호출 할 수 있을까?

참고 : 나중에 제약 조건 중 일부를 다시 적용하려면 항상 strong참조를 저장 하십시오.


2
나에게 유일한 신뢰할 수있는 방법은 viewDidLayoutSubviews(). viewWillLayoutSubviews()제 경우에는 제약 조건을 조정하는 것이 작동하지 않습니다.
petrsyn 2011

6

뷰가 생성 될 때 다음 라이프 사이클 메소드가 순서대로 호출됩니다.

  1. loadView
  2. viewDidLoad
  3. viewWillAppear
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. viewDidAppear

이제 귀하의 질문에.

  1. 이 동작이 발생하는 이유는 무엇입니까?

답변 : 뷰의 뷰에 대한 제약을 설정하려고 할 때 viewDidLoad뷰의 경계가 없으므로 제약을 설정할 수 없기 때문입니다. viewDidLayoutSubviews뷰의 경계가 확정 된 후에야 합니다.

  1. viewDidLoad에서 제약 조건을 활성화 / 비활성화 할 수 있습니까?

답변 : 아니요. 위에 설명 된 이유.


viewController 라이프 사이클에 대한 설명에서 뷰가 처음로드 된 다음 viewDidLoad가 호출되는 방법에 대해 설명했습니다. 그러나 당신은 또한 viewDidLoad가 호출 될 때 뷰가 생성되지 않는다고 말했습니다. 이것은 분명히 모순입니다. 또한 뷰에 하위 뷰를 추가 할 수 있으므로 직접 테스트하여 viewDidLoad가 호출 될 때 뷰가 생성되었는지 확인할 수 있습니다.
ABakerSmith 2015

viewDidLoad는 뷰가 생성되고로드되기 때문에 괜찮습니다. 실제로 제약 조건을 활성화하는 경우 주로 성능이 저하됩니다. 원래 문제는 제약 조건이 활성화 된 위치와 관련이 없다고 생각합니다. stackoverflow.com/questions/19387998/…
Gabe 2011

@ABakerSmith 나는 더 명확하게 대답을 편집했습니다.
Sumeet 2015

1

활성 및 비활성 제약 조건을 사용하는 초기성에 - (void)updateConstraints대한 strong참조 와 함께 (목표 c) 재정의에서 정상 당 제약 조건을 설정하는 한 발견했습니다 . 뷰 사이클의 다른 곳에서 필요한 것을 비활성화 및 / 또는 활성화 한 다음을 호출 layoutIfNeeded하면 문제가 없어야합니다.

가장 중요한 것은 첫 번째 초기화 및 레이아웃 후에 s updateConstraints를 호출하는 한 제약 조건 의 재정의를 지속적으로 재사용하지 않고 제약 조건의 활성화를 분리하는 것 updateConstraint입니다. 그 후에는 뷰 사이클에서 중요한 것 같습니다.

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