"squished"숨겨진 뷰에 대한 UIStackView "동시에 제약 조건을 충족 할 수 없음"


95

내 UIStackView "행"이 찌그러지면 AutoLayout경고가 발생합니다. 그러나 그들은 잘 표시되며 다음과 같은 종류의 로깅 외에는 잘못된 것이 없습니다.

제약 조건을 동시에 충족 할 수 없습니다. 아마도 다음 목록의 제약 조건 중 적어도 하나는 원하지 않는 제약 조건 일 것입니다. 다음을 시도해보십시오 : (1) 각 제약 조건을 살펴보고 예상하지 못한 것이 무엇인지 파악하십시오. (2) 원치 않는 제약 또는 제약을 추가 한 코드를 찾아 수정합니다. (참고 : NSAutoresizingMaskLayoutConstraints이해가되지 않는 경우 UIView속성 에 대한 설명서를 참조하십시오. translatesAutoresizingMaskIntoConstraints) (

그래서 나는 이것을 고치는 방법을 아직 모르겠지만 단지 성가신 것 외에는 아무것도 깨뜨리지 않는 것 같습니다.

누구든지 그것을 해결하는 방법을 알고 있습니까? 흥미롭게도 레이아웃 제약 조건은 'UISV-hiding' 태그가 자주 붙는데 , 이는 아마도이 인스턴스에서 서브 뷰 또는 무언가에 대한 높이 최소값을 무시해야한다는 것을 나타냅니다.


1
이 문제는 iOS11에서 수정 된 것으로 보이며 여기에는 경고가 표시되지 않습니다
trapper

답변:


204

이 문제는 하위 뷰를 내부 UIStackView에서 숨김으로 설정할 때 애니메이션을 적용하기 위해 먼저 높이를 0으로 제한하기 때문에 발생합니다.

다음과 같은 오류가 발생했습니다.

2015-10-01 11:45:13.732 <redacted>[64455:6368084] 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:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

내가하려고했던 것은 각 가장자리에 8pt 씩 삽입 된 것을 UIView내 안에 배치하는 것이 었습니다 .UIStackViewUISegmentedControl

숨김으로 설정하면 컨테이너 뷰를 0 높이로 제한하려고하지만 위에서 아래로 제약 조건이 설정되어 있기 때문에 충돌이 발생했습니다.

이 문제를 해결하기 위해 UISV-hiding필요한 경우 제약 조건이 우선 순위를 가질 수 있도록 8pt 상단 및 하단 제약 우선 순위를 1000에서 999로 변경 했습니다.


이것들에 높이 또는 너비 제약이 있고 우선 순위를 낮추면 작동하지 않는 것처럼 보입니다. 높이 / 너비를 제거하고 맨 위의 맨 뒤 맨 아래를 추가 한 다음 우선 순위를 낮게 설정하면 작동합니다
bolnad

4
우선 순위를 변경하는 것도 저에게 효과적이었습니다. 또한 사용하지 않는 크기 클래스에서 실수로 복사 된 초과 (흐리게 표시된) 제약 조건을 제거합니다. 중요 팁 : 이러한 문제를보다 쉽게 ​​디버깅하려면 각 제약 조건에 IDENTIFER 문자열을 설정하십시오. 그런 다음 디버그 메시지에서 어떤 제약 조건이 잘못되었는지 확인할 수 있습니다.
Womble

3
제 경우에는 높이의 우선 순위를 낮추기 만하면됩니다.
pixelfreak jul.

그 IDENTIFIER 팁은 훌륭합니다! 나는 항상 디버그 메시지 이름에 제약 조건을 부여하는 방법을 궁금해했고, 제약 자체보다는 뷰에 무언가를 추가하려고 항상 찾고있었습니다. 감사합니다 @Womble!
라이언

감사! 1000에서 999까지의 우선 순위가 트릭을 수행했습니다 .Xcode : Version 8.3.3 (8E3004b)
Michael Garito

52

해결하기 쉽지 않은 비슷한 문제가 발생했습니다. 제 경우에는 스택 뷰에 스택 뷰가 포함되어 있습니다. 내부 UIStackView에는 두 개의 레이블과 0이 아닌 간격이 지정되었습니다.

addArrangedSubview ()를 호출하면 다음과 유사한 제약 조건이 자동으로 생성됩니다.

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

이제 innerStackView를 숨기려고하면 모호한 제약 조건 경고가 표시됩니다.

이유를 이해하려면이 왜의 처음 보자 하지 않는 경우에 발생 innerStackView.spacing같다 0. 를 호출 할 때 innerStackView.hidden = true@liamnichols가 정확했습니다. outerStackView마법처럼이 호출을 가로 채고 우선 순위가 1000 인 0높이 UISV 숨김 제약 조건을 만듭니다 (필수). 아마도 이것은 숨김 코드가 UIView.animationWithDuration()블록 내에서 호출되는 경우 스택 뷰의 요소가 뷰 밖에서 애니메이션되도록 허용하는 것 입니다. 불행히도이 제약이 추가되는 것을 막을 수있는 방법은없는 것 같습니다. 그럼에도 불구하고 다음과 같은 상황이 발생하기 때문에 "제약 조건을 동시에 충족 할 수 없음"(USSC) 경고가 표시되지 않습니다.

  1. label1의 높이가 0으로 설정 됨
  2. 두 레이블 사이의 간격이 이미 0으로 정의되었습니다.
  3. label2의 높이가 0으로 설정 됨
  4. innerStackView의 높이는 0으로 설정됩니다.

이러한 4 가지 제약 조건을 충족 할 수 있다는 것은 분명합니다. 스택 뷰는 단순히 모든 것을 0 높이 픽셀로 부드럽게 만듭니다.

우리가 설정 한 경우 이제, 버그 예를 다시가는 spacing하기 위해 2, 우리는 지금 이러한 제약이있다 :

  1. label1의 높이가 0으로 설정 됨
  2. 두 레이블 사이의 간격은 1000 우선 순위에서 2 픽셀 높이로 스택보기에 의해 자동으로 생성되었습니다.
  3. label2의 높이가 0으로 설정 됨
  4. innerStackView의 높이는 0으로 설정됩니다.

스택보기는 높이가 0 픽셀 일 수없고 콘텐츠 높이가 2 픽셀 일 수 없습니다. 제약 조건을 충족 할 수 없습니다.

참고 : 더 간단한 예를 통해이 동작을 확인할 수 있습니다. 정렬 된 하위보기로 스택보기에 UIView를 추가하기 만하면됩니다. 그런 다음 해당 UIView에 1000 우선 순위로 높이 제약 조건을 설정하십시오. 이제 Hide를 호출 해보십시오.

참고 : 어떤 이유로 든 스택 뷰가 UICollectionViewCell 또는 UITableViewCell의 하위 뷰인 경우에만 발생했습니다. 그러나 innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)내부 스택보기를 숨긴 후 다음 실행 루프 를 호출하여 셀 외부에서이 동작을 재현 할 수 있습니다.

참고 : UIView.performWithoutAnimations에서 코드를 실행하려고해도 스택 뷰는 여전히 USSC 경고를 발생시키는 0 높이 제약 조건을 추가합니다.


이 문제에 대한 최소 3 가지 해결책이 있습니다.

  1. 스택보기에서 요소를 숨기기 전에 스택보기인지 확인하고, 그렇다면 spacing을 0으로 변경 하십시오. 콘텐츠를 다시 표시 할 때마다 프로세스를 반대로 (원래 간격을 기억해야 함)해야하기 때문에 이것은 성가신 일입니다.
  2. 스택보기에서 요소를 숨기는 대신 removeFromSuperview. 프로세스를 되돌릴 때 제거 된 항목을 삽입 할 위치 를 기억해야하기 때문에 훨씬 더 성가신 일 입니다. removeArrangedSubview를 호출 한 다음 숨기는 것만으로 최적화 할 수 있지만 여전히 수행해야하는 부기가 많이 있습니다.
  3. spacingUIView에서 중첩 된 스택 뷰 (0이 아닌 )를 래핑 합니다. 비 필수 우선 순위 (999 이하)로 하나 이상의 제한 조건을 지정하십시오. 부기를 할 필요가 없기 때문에 이것이 최상의 솔루션입니다. 필자의 예에서는 스택보기와 래퍼보기 사이에 1000에서 위쪽, 선행 및 후행 제약 조건을 만든 다음 스택보기의 아래쪽에서 래퍼보기까지 999 제약 조건을 만들었습니다. 이렇게하면 외부 스택 뷰가 0 높이 제약 조건을 생성 할 때 999 제약 조건이 깨지고 USSC 경고가 표시되지 않습니다. (참고 : 이는 UICollectionViewCell 하위 클래스의 contentView.translatesAutoResizingMaskToConstraints를로 설정해야하는 경우false 에 대한 솔루션과 유사합니다. )

요약하면이 동작이 발생하는 이유는 다음과 같습니다.

  1. 관리되는 하위보기를 스택보기에 추가하면 Apple은 자동으로 1000 개의 우선 순위 제약 조건을 생성합니다.
  2. Apple은 스택보기의 하위보기를 숨길 때 자동으로 0 높이 제한을 생성합니다.

Apple이 (1) 제약 조건 (특히 스페이서)의 우선 순위를 지정하도록 허용했거나 (2) 자동 UISV 숨김 제약 조건 을 옵트 아웃하도록 허용했다면 이 문제는 쉽게 해결 될 것입니다.


6
매우 철저하고 유용한 설명에 감사드립니다. 그러나 이것은 분명히 스택 뷰와 함께 Apple 측의 버그처럼 보입니다. 기본적으로 "숨기기"기능은 "간격"기능과 호환되지 않습니다. 그 이후로이 문제를 해결했거나 추가 포함 뷰로 해킹을 방지하는 몇 가지 기능을 추가 한 아이디어가 있습니까? (다시 한 번, 잠재적 인 솔루션을 크게 분석하고 # 3의 우아함에 동의합니다.)
Marchy

1
욕구가있는 모든 UIStackView아이 를 숨기거나 숨기고 싶은 아이 UIStackView에게 감싸 UIView야합니까?
Adrian

1
이것은 애플 측에서 정말 나쁜 감독처럼 느껴진다. 특히 사용과 관련된 경고 및 오류는 UIStackView모호하고 이해하기 어려운 경향이 있기 때문 입니다.
bompf

이것은 생명의 은인입니다. UITableViewCell에 추가 된 UIStackViews로 인해 셀이 재사용 될 때마다 AutoLayout 오류 로그 스팸이 발생하는 문제가 발생했습니다. 하단 앵커 제약 우선 순위가 낮음으로 설정된 UIView에 stackView를 포함하면 문제가 해결되었습니다. 뷰 디버거가 stackView의 요소를 모호한 높이로 표시하도록하지만 오류 로그 스팸없이 앱에서는 제대로 표시됩니다. 감사합니다.
Womble

6

대부분의 경우이 오류는 충돌을 제거하기 위해 제약 조건 우선 순위를 낮춤으로써 해결할 수 있습니다.


무슨 말이야? 제약은 .... 모든 적층 뷰 내에서 상대적입니다
벤 길드

미안합니다, 당신의 질문을 이해하지 못합니다. 제 영어는 너무 그렇습니다. 그러나 제약은 관련된 뷰 마녀와 관련이 있다고 생각합니다. 뷰가 숨겨지면 제약이 비활성화됩니다. 그게 당신의 의심인지 잘 모르겠지만 제가 도울 수 있기를 바랍니다.
루치아노 알메이다

물건이 찌그러 지거나 표시 / 숨겨지는 과정에서 나오는 것 같습니다. 이 경우 부분적으로 표시됩니다. — 높이가 0이 될 수 있기 때문에 최소 수직 상수를 실제로 통과하여 제거해야 할 수도 있습니다.
Ben Guild

2

보기를 숨김으로 설정하면 UIStackview에서 애니메이션을 적용하려고합니다. 이러한 효과를 원하면 제약 조건에 대해 올바른 우선 순위를 설정하여 충돌하지 않도록해야합니다 (위에서 많은 사람들이 제안한대로).

그러나 애니메이션을 신경 쓰지 않는다면 (아마도 ViewDidLoad에서 숨길 것입니다), removeFromSuperview보기와 함께 제거되므로 제약 조건에 문제가없는 동일한 효과를 가지도록 간단하게 할 수 있습니다.


1

@Senseful의 답변에 따라 다음은 뷰에서 스택 뷰를 래핑하고 권장하는 제약 조건을 적용하는 UIStackView 확장입니다.

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

대신 추가 stackView사용을stackView.wrapped() .


1

첫째, 다른 사람들이 제안했듯이 제어 할 수있는 제약 조건, 즉 UIStackView에 내재 된 제약 조건이 우선 순위 999로 설정되어 뷰가 숨겨 질 때 재정의 될 수 있는지 확인합니다.

여전히 문제가 발생하는 경우 숨겨진 StackView의 간격으로 인한 문제 일 수 있습니다. 내 솔루션은 UIView를 스페이서로 추가하고 UIStackView 간격을 0으로 설정하는 것입니다. 그런 다음 View.height 또는 View.width 제약 조건 (수직 또는 수평 스택에 따라 다름)을 StackView의 간격으로 설정합니다.

그런 다음 새로 추가 된보기의 콘텐츠 포옹 및 콘텐츠 압축 저항 우선 순위를 조정합니다. 상위 StackView의 배포도 변경해야 할 수 있습니다.

위의 모든 작업은 Interface Builder에서 수행 할 수 있습니다. 추가로 새로 추가 된 뷰 중 일부를 프로그래밍 방식으로 숨기거나 숨김 해제해야 원치 않는 간격이 생기지 않을 수 있습니다.


1

나는 최근에 UIStackView. 에서 많은 책을 보관하고 포장하는 대신 UIViews, 나는 parentStackView숨기거나 숨기고 싶은 아이들을위한 콘센트와 콘센트 를 만들기로 결정했습니다 .

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

스토리 보드에서 내 parentStack은 다음과 같습니다.

여기에 이미지 설명 입력

여기에는 4 명의 자식이 있고 각 자식에는 내부에 스택 뷰가 있습니다. 스택 뷰를 숨길 때 스택 뷰인 UI 요소도 있으면 자동 레이아웃 오류 스트림이 표시됩니다. 숨기는 대신 제거하기로 결정했습니다.

내 예에서는 parentStackViewsTop Stack View, StackViewNumber1, Stack View Number 2 및 Stop Button의 4 개 요소 배열을 포함합니다. 인덱스는 arrangedSubviews각각 0, 1, 2, 3입니다. 하나를 숨기고 싶을 때 간단히 parentStackView's arrangedSubviews배열 에서 제거합니다 . 약하지 않기 때문에 메모리에 남아 있으며 나중에 원하는 인덱스에 다시 넣을 수 있습니다. 나는 그것을 다시 초기화하지 않기 때문에 필요할 때까지 놀고 있지만 메모리를 부 풀리지 않습니다.

따라서 기본적으로 ...

1) 부모 스택과 숨기거나 숨기려는 자식에 대한 IBOutlets를 스토리 보드로 드래그합니다.

2) 숨기고 싶을 때, 숨기고 싶은 스택을 parentStackView's arrangedSubviews어레이 에서 제거합니다 .

3) 전화 self.view.layoutIfNeeded()UIView.animateWithDuration.

마지막 두 개의 stackView는 weak. 숨기기를 해제 할 때를 대비해 보관해야합니다.

stackViewNumber2를 숨기고 싶다고 가정 해 보겠습니다.

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

그런 다음 애니메이션을 적용합니다.

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

stackViewNumber2나중에 "숨기기를 해제" 하려면 원하는 parentStackView arrangedSubViews색인 에 삽입 하고 업데이트를 애니메이션하면됩니다.

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

나는 제약에 대한 부기, 우선 순위 등을 다루는 것보다 훨씬 쉽다는 것을 알았다.

기본적으로 숨기고 싶은 것이있는 경우 스토리 보드에 배치하고 제거 viewDidLoad하고을 사용하여 애니메이션없이 업데이트 할 수 view.layoutIfNeeded()있습니다.


1

모든 것이 런타임에 제대로 작동했지만 임베디드 스택 뷰에서 동일한 오류가 발생했습니다.

isHidden = true부모 스택 뷰를 숨기기 전에 먼저 모든 하위 스택 뷰 를 숨기고 (설정 ) 제약 조건 오류를 해결했습니다 .

이렇게하면 하위 정렬 된 뷰를 제거하고 다시 추가해야 할 때 인덱스를 유지하는 모든 복잡성이 없었습니다.

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


1

Senseful은 위의 문제의 근원에 대한 훌륭한 답변을 제공 했으므로 바로 해결 방법으로 이동하겠습니다.

해야 할 일은 모든 stackView 제약 조건 우선 순위를 1000보다 낮게 설정하는 것입니다 (999가 작업을 수행함). 예를 들어 stackView가 superview에 대해 왼쪽, 오른쪽, 위쪽 및 아래쪽으로 제한되는 경우 4 개의 제약 조건 모두 우선 순위가 1000보다 낮아야합니다.


0

특정 크기 클래스 (예 : wCompact hRegular)로 작업하는 동안 제약 조건을 생성 한 다음 다른 크기 클래스 (예 : wAny hAny)로 전환 할 때 복제본을 생성했을 수 있습니다. 크기가 다른 UI 객체의 제약 조건을 확인하고 제약 조건에 이상이 있는지 확인합니다. 충돌 제약을 나타내는 빨간색 선이 표시되어야합니다. 평판 포인트 10 점을받을 때까지 사진을 찍을 수 없어 죄송합니다 : /



네, 인터페이스 빌더에서 크기 클래스를 토글하여 빨간색이 전혀 보이지 않습니다. "모든"크기 만 사용했습니다.
Ben Guild

0

한 번에 전체 UIStackView를 숨기고 싶었지만 OP와 동일한 오류가 발생하여이 문제가 해결되었습니다.

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}

필요한 제약 조건 ( priority = 1000)을 필요하지 않음 ( )으로 변경할 때 자동 레이아웃 엔진이 불평하기 때문에 이것은 저에게 효과적 이지 않았습니다 priority <= 999.
Senseful

0

높이 제약이있는 버튼 행이 있습니다. 이것은 하나의 버튼이 숨겨져있을 때 발생합니다. 해당 버튼 높이 제한의 우선 순위를 999로 설정하면 문제가 해결되었습니다.


-2

이 오류는 UIStackView와 관련이 없습니다. 우선 순위가 동일한 충돌 제약이있을 때 발생합니다. 예를 들어, 뷰의 너비가 100이라는 제약 조건이 있고 동시에 뷰의 너비가 컨테이너의 25 %라는 다른 제약 조건이있는 경우. 두 가지 상충되는 제약이 있습니다. 해결책은 그들 중 하나를 삭제하는 것입니다.


-3

[mySubView removeFromSuperview]가있는 NOP. 누군가를 도울 수 있기를 바랍니다. :)


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