programing

Swift 기본 AlertViewController 제약 조건 위반

powerit 2023. 9. 20. 20:46
반응형

Swift 기본 AlertViewController 제약 조건 위반

style.action과 함께 기본 AlertView Controller를 사용하려고 합니다.시트. 어떤 이유에서인지 경고로 인해 제약 조건 오류가 발생합니다.Alert Controller가 버튼을 통해 트리거(표시)되지 않는 한, 전체 보기에는 제약 조건 오류가 없습니다.혹시 Xcode의 버그가 아닐까요?

정확한 오류는 다음과 같습니다.

2019-04-12 15:33:29.584076+0200 Appname[4688:39368] [LayoutConstraints] 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. 
(
    "<NSLayoutConstraint:0x6000025a1e50 UIView:0x7f88fcf6ce60.width == - 16   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000025a1e50 UIView:0x7f88fcf6ce60.width == - 16   (active)>

이것이 제가 사용하는 코드입니다.

@objc func changeProfileImageTapped(){
        print("ChangeProfileImageButton tapped!")
        let alert = UIAlertController(title: "Change your profile image", message: nil, preferredStyle: .actionSheet)

        alert.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: nil))
        alert.addAction(UIAlertAction(title: "Online Stock Library", style: .default, handler: nil))
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        alert.view.tintColor = ColorCodes.logoPrimaryColor

        self.present(alert, animated: true)
    }

보시다시피, 그것은 매우 기본적입니다.그래서 이 기본 구현으로 오류가 발생하면 안 되는데 이상한 행동이 나타나는 것에 대해 매우 혼란스럽습니다. 그렇죠?

Output I get

하지만, 제약 조건을 어기면 모든 화면 크기에 경고가 제대로 표시됩니다. 어떤 도움이라도 주시면 정말 감사하겠습니다.

다음은 애니메이션을 비활성화할 필요 없이 경고를 제거한 것입니다.그리고 애플이 결국 경고의 근본 원인을 고친다고 가정한다면, 다른 어떤 것도 깨지지 않을 것입니다.

extension UIAlertController {
    func pruneNegativeWidthConstraints() {
        for subView in self.view.subviews {
            for constraint in subView.constraints where constraint.debugDescription.contains("width == - 16") {
                subView.removeConstraint(constraint)
            }
        }
    }
}

그러면 다음과 같이 사용할 수 있습니다.

// After all addActions(...), just before calling present(...)
alertController.pruneNegativeWidthConstraints()

이 오류는 중요하지 않습니다. 애플의 수정되지 않은 버그인 것 같습니다.이 제약 조건은 프레젠테이션 직후 애니메이션 스타일로 나타납니다.enter image description here 발표하기 전에 (값, 관계, 우선순위를 변경) 파악하고 변경하려고 했지만 이렇게 동적으로 추가된 제약 조건 때문에 성공하지 못했습니다.

애니메이션을 끌 때self.present(alert, animated: false)사용하는 것.alert.view.addSubview(UIView())– 오류가 사라집니다.설명할 수는 없지만 효과는 있어요!

let alert = UIAlertController(title: "Change your profile image", message: nil, preferredStyle: .actionSheet)

alert.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Online Stock Library", style: .default, handler: nil))
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)

alert.addAction(cancel)
alert.view.addSubview(UIView()) // I can't explain it, but it works!

self.present(alert, animated: false)

iOS 버전의 새로운 버그입니다.

  • 12.2
  • 12.3
  • 12.4
  • 13.0
  • 13.1
  • 13.2
  • 13.2.3
  • 13.3
  • 13.4
  • 13.4.1
  • 13.5
  • 13.6
  • 14.0
  • 14.2
  • 14.4

우리가 할 수 있는 유일한 방법은 애플에 버그 보고서를 제출하는 것입니다(저는 방금 그렇게 했고 당신도 그렇게 해야 합니다).

iOS의 새로운 버전에 대한 답변이 나오면 업데이트하도록 노력하겠습니다.

답변에 추가하는 중...이것은 저에게 문제를 제거하고 기존 코드를 변경할 필요가 없는 것 같습니다.

extension UIAlertController {
    override open func viewDidLoad() {
        super.viewDidLoad()
        pruneNegativeWidthConstraints()
    }

    func pruneNegativeWidthConstraints() {
        for subView in self.view.subviews {
            for constraint in subView.constraints where constraint.debugDescription.contains("width == - 16") {
                subView.removeConstraint(constraint)
            }
        }
    }
}

세이프 솔루션

제약 조건은 미래에 정확한 값으로 사용되기 때문에 제거해서는 안 됩니다.

또는 상수를 양의 값으로 변경할 수도 있습니다.

class PXAlertController: UIAlertController {
    override func viewDidLoad() {
        super.viewDidLoad()

        for subview in self.view.subviews {
            for constraint in subview.constraints {
                if constraint.firstAttribute == .width && constraint.constant == -16 {
                    constraint.constant = 10 // Any positive value
                }
            }
        }
    }
}

그런 다음 컨트롤러를 초기화하려면 다음을 사용합니다.

let controller = PXAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)

재미있는 아이디어가 있네요.개인적으로 저는 제약 조건을 삭제하거나 그 값(크기)을 변경하는 생각을 좋아하지 않습니다.

이 문제는 제약 조건 해결이 필수(우선순위 1000) 제약 조건을 어겨야 하는 상황에 놓여 있기 때문에 덜 잔인한 접근 방식은 단지 필요한 경우 이 제약 조건을 어길 수 있다는 것을 프레임워크에 말하는 것입니다.

그래서 (조시의 "안전" 수업에 근거해서):

class PXAlertController: UIAlertController {
    override func viewDidLoad() {
        super.viewDidLoad()
        tweakProblemWidthConstraints()
    }
    
    func tweakProblemWidthConstraints() {
        for subView in self.view.subviews {
            for constraint in subView.constraints {
                // Identify the problem constraint
                // Check that it's priority 1000 - which is the cause of the conflict.
                if constraint.firstAttribute == .width &&
                    constraint.constant == -16 &&
                    constraint.priority.rawValue == 1000 {
                    // Let the framework know it's okay to break this constraint
                    constraint.priority = UILayoutPriority(rawValue: 999)
                }
            }
        }
    }
}

이것은 어떠한 레이아웃 치수도 변경하지 않는다는 장점이 있으며, 프레임워크에 문제가 발생했을 때도 적절하게 동작할 가능성이 높습니다.

iPhone SE 시뮬레이터에서 테스트(원래 문제를 제공하던) - 제약 조건 관련 디버그가 사라졌습니다.

또은 NSLayoutContract 입니다를 preferredStyle: .alertpreferredStyle: .actionSheet 경고를 생성하지 않고 작동하지만 메뉴가 모듈식으로 표시됩니다.

목표-C를 위한 솔루션:

  1. UIAAlertController에서 자신의 AlertController를 서브클래스합니다.
  2. 이전 회신과 같이 가지치기 기능 정의

    @implementation TemplateAlertController
    
    -(void) viewDidLoad {
    
        [super viewDidLoad];
        [self mPruneNegativeWithConstraints];
    }
    
    -(void) mPruneNegativeWithConstraints {
    
        for (UIView* iSubview in [self.view subviews]) {
            for (NSLayoutConstraint* iConstraint in [iSubview constraints]) {
                if ([iConstraint.debugDescription containsString:@"width == - 16"]) {
                    [iSubview removeConstraint:iConstraint];
                }
            }
        }
    }
    
    @end
    

애니메이션과 모든 제약 조건을 유지하려면 경고 컨트롤러를 표시하기 전에 음의 제약 조건을 찾아 양의 제약 조건을 설정해야 합니다.

// Find negative constraint and make it positive
for subview in alert.view.subviews {
    for constraint in subview.constraints {
        if constraint.constant < 0 {
            constraint.constant = -constraint.constant
        }
    }
}

// Present alert controller
present(alert, animated: true)

여기 제가 이 문제를 해결하기 위해 사용하는 기능이 있습니다.제약 조건이 마이너스여서 이유를 알 수 없기 때문에 문제가 발생합니다.

    func showActionSheet(title: String, message: String, actions: [UIAlertAction]) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
        actions.forEach { alertController.addAction($0) }
        let subviewConstraint = alertController.view.subviews
            .flatMap({ $0.constraints })
            .filter({ $0.constant < 0 })
        for subviewConstraint in subviewConstraint {
            subviewConstraint.constant = -subviewConstraint.constant // this is the answer
        }
        self.present(alertController, animated: true)
    }

모든 제약 조건을 가져오기 위한 뷰 확장 만들기

extension UIView {
   func callRecursively(_ body: (_ subview: UIView) -> Void) {
      body(self)
      subviews.forEach { $0.callRecursively(body) }
   }
}

UIALertController 확장자를 만들어 -16 상수의 모든 제약 조건을 찾고 우선 순위를 999로 변경합니다.

extension UIAlertController {
   func fixConstraints() -> UIAlertController {
      view.callRecursively { subview in
         subview.constraints
            .filter({ $0.constant == -16 })
            .forEach({ $0.priority = UILayoutPriority(rawValue: 999)})
    }
    return self
    }
}

다음을 표시하면서 경보를 만들고 fixContracts()를 호출합니다.

let alert = UIAlertController(...
...
present(alert.fixConstraints(), animated: true, completion: nil)

여러분, 제가 알아낸 것 같아요.문제는 popover PresentationController sourceView에 self가 할당된 경우입니다.UIALert Controller의 보기, 원형 참조가 발생하고 제약 조건이 깨집니다.sourceView에는 팝업 자체가 아니라 팝업을 호출한 뷰가 할당되어야 합니다.

언급URL : https://stackoverflow.com/questions/55653187/swift-default-alertviewcontroller-breaking-constraints

반응형