Các ràng buộc phá vỡ AlertViewController mặc định của Swift


82

Tôi đang cố gắng sử dụng AlertViewController mặc định với kiểu .actionSheet . Vì một số lý do, cảnh báo gây ra lỗi hạn chế . Miễn là alertController không được kích hoạt (hiển thị) thông qua một nút, không có lỗi ràng buộc nào trên toàn bộ chế độ xem. Có thể đây là một lỗi của Xcode?

Lỗi chính xác mà tôi nhận được trông như sau:

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)>

Đây là mã tôi sử dụng:

@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)
    }

Như bạn thấy, nó rất cơ bản . Đó là lý do tại sao tôi rất bối rối về hành vi kỳ lạ mà tôi nhận được vì việc triển khai mặc định này sẽ không gây ra bất kỳ lỗi nào, phải không?

Đầu ra tôi nhận được

Mặc dù, thông qua việc phá vỡ các ràng buộc, cảnh báo hiển thị chính xác trên tất cả các kích thước màn hình, tôi thực sự cảm ơn vì bất kỳ sự trợ giúp nào mà tôi nhận được.


bạn có xây dựng bất kỳ ràng buộc nào khác trong mã không?
Sh_Khan

@Sh_Khan không, tôi không. Ý tôi là, rõ ràng là tôi có các chế độ xem khác bên dưới Chế độ xem cảnh báo nhưng chúng hoạt động tốt và không tạo ra bất kỳ lỗi ràng buộc nào. Nhưng tôi đã không thay đổi bất cứ điều gì tại những hạn chế AlertView
linus_hologram

1
@linus_hologram, có điều gì gây ra bất kỳ trục trặc hình ảnh nào trên màn hình không? nếu thì sao? nếu không , đừng lãng phí thời gian của bạn để cố gắng sửa chữa một cái gì đó thậm chí không bị hỏng.
holex

1
Hạn chế log không phải lúc nào cũng đúng, đôi khi nó tạo ra những thứ gây hiểu lầm
Sh_Khan

1
Tôi đã xem xét vấn đề với trình gỡ lỗi, nó là tự động thêm khó khăn ... Tôi không thể tìm thấy hạn chế này trước khi trình bày một UIAlertController ... Xem thêm trong câu trả lời của tôi dưới đây
Agisight

Câu trả lời:


42

Lỗi này không nghiêm trọng, dường như là một dạng lỗi chưa được Apple khắc phục. Ràng buộc này xuất hiện trong kiểu hoạt hình ngay sau khi trình bày. nhập mô tả hình ảnh ở đâyTôi đã cố gắng nắm bắt và thay đổi nó (thay đổi giá trị, quan hệ, mức độ ưu tiên) trước khi trình bày - không thành công vì những ràng buộc được thêm động này.

Khi bạn tắt hoạt ảnh trong self.present(alert, animated: false)và sử dụng alert.view.addSubview(UIView())- lỗi sẽ biến mất. Tôi không thể giải thích nó, nhưng nó hoạt động!

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)

1
câu trả lời này có liên quan như thế nào đến vấn đề ràng buộc mà OP có?
holex

3
tắt hoạt ảnh + alert.view.addSubview(UIView())làm việc cho tôi. Nhưng không có hoạt ảnh, cảm thấy rất kỳ lạ ... Dù sao cũng cảm ơn giải pháp này.
Tieda Wei

1
Vâng, tôi giải thích đó là lỗi từ Apple. Tôi đã giải thích nó. Nhưng nếu bạn có thể giải quyết, bạn có thể cố gắng lấy các ràng buộc cần thiết và thay đổi nó. Tôi nghĩ đó không phải là một ý kiến ​​hay, vì nó là một lỗi.
Agisight

1
Tôi nghĩ rằng việc thêm một lượt xem phụ buộc các ràng buộc tự động thanh toán phải được tính toán lại, làm tắt tiếng cảnh báo.
FormigaNinja

3
Sự cố này xảy ra khi animationđược đặt thành true. Tôi nghĩ rằng đó là một lỗi nội bộ và nó có thể được bỏ qua vì actoinSheet hiển thị đúng. Tôi rất vui vì tôi có thể thu hẹp nó xuống nguyên nhân gốc rễ, lúc đầu tôi dạy nó là mã của tôi.
Jerry Okafor

45

Thao tác sau sẽ xóa cảnh báo mà không cần tắt hoạt ảnh. Và giả sử Apple cuối cùng đã khắc phục được nguyên nhân gốc rễ của cảnh báo, thì nó sẽ không vi phạm bất cứ điều gì khác.

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

Điều này sau đó có thể được sử dụng như thế này:

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

1
Hoạt động tuyệt vời, không còn ô nhiễm gỗ! 🥳
Klaas

Điều này thực sự đang gây ra nhiều vấn đề hơn bây giờ (iOS 13.2.2), không rõ nó hoạt động như thế nào trên các phiên bản trước đó. Làm cho UIAlertController hiển thị được ghim vào đầu màn hình.
jovanjovanovic

@jovanjovanovic rất tiếc khi biết rằng bạn đang gặp phải vấn đề đó, nhưng tôi không thể tái tạo những gì bạn đang mô tả. Giải pháp này vẫn là giải pháp loại bỏ các cảnh báo mà không có tác động xấu. Nếu bạn có mẫu mã có thể tái tạo những gì bạn đang mô tả, vui lòng chia sẻ.
jims1103

Tôi sử dụng cose này ngay sau khi tạo Cảnh báo, nhưng dường như không hoạt động! Nơi tốt nhất để đặt mã?
Lucas Tegliabue

@LucasTegliabue sử dụng nó trước khi trình bày alertcontroller như let alertController = UIAlertController () alertController.pruneNegativeWidthConstraints () self.present (alertController, animation: true, hoàn thành: nil)
Jack

17

Đó là một lỗi mới trong các phiên bản 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

Điều duy nhất chúng tôi có thể làm là gửi báo cáo lỗi cho Apple (Tôi vừa làm vậy và bạn cũng vậy).

Tôi sẽ cố gắng cập nhật câu trả lời cho (các) phiên bản iOS mới khi nó ra mắt.


8

Thêm vào câu trả lời này ... Điều này dường như loại bỏ vấn đề đối với tôi và không yêu cầu bất kỳ thay đổi nào đối với mã hiện có.

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)
            }
        }
    }
}

2

Giải pháp an toàn

Bạn không nên loại bỏ ràng buộc vì nó được sử dụng trong tương lai với một giá trị chính xác.

Thay vào đó, bạn có thể thay đổi hằng số của nó thành một giá trị dương:

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
                }
            }
        }
    }
}

Và sau đó để khởi tạo việc sử dụng bộ điều khiển của bạn:

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

Tôi đã thử tất cả các giải pháp được đề xuất áp dụng trong Swift mà không thành công. Tôi đang sử dụng XCode 11.5, biên dịch cho ios 13.0, chạy trong iPhone Xs Max. Vẫn nhận được "<NSLayoutConstraint:0x28002b930 UIView:0x102e32420.width == - 16 (active)>". Tôi không thể giúp gì nhiều nếu không nói với bạn rằng vấn đề vẫn còn đó. Như ai đó đã đề xuất, tôi sẽ bỏ qua lỗi, nhưng tôi không thích nó.
Nicola Mingotti

nó không hoạt động ở tất cả. Các giải pháp từ RichW làm việc ít nhất là trên iOS 13
Vyachaslav Gerchicov

@VyachaslavGerchicov Tuy nhiên, hãy cẩn thận, tôi tin rằng các phiên bản iOS cũ hơn sẽ gặp sự cố khi thay đổi một ràng buộc bắt buộc thành không yêu cầu trong thời gian chạy. stackoverflow.com/questions/31186187/…
Josh Bernfeld

1

Một cách khác để tránh khỏi lỗi NSLayoutConstraint là sử dụng preferredStyle: .alertthay thế preferredStyle: .actionSheet. Điều này hoạt động mà không tạo ra cảnh báo, nhưng nó sẽ hiển thị menu theo phương thức.


0

Giải pháp cho Objective-C:

  1. Phân lớp Bộ điều khiển cảnh báo của riêng bạn từ UIAlertController
  2. Xác định chức năng cắt tỉa như trong câu trả lời trước

    @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
    

0

Ý tưởng thú vị ở đây. Cá nhân tôi không thích ý tưởng xóa ràng buộc hoặc thay đổi giá trị (kích thước) của nó.

Khi vấn đề xoay quanh việc giải quyết ràng buộc bị buộc vào một vị trí mà nó phải phá vỡ ràng buộc bắt buộc (ưu tiên 1000), một cách tiếp cận ít tàn bạo hơn chỉ là nói với khuôn khổ rằng ràng buộc này có thể bị phá vỡ nếu cần.

Vì vậy (dựa trên lớp "An toàn" của Josh):

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)
                }
            }
        }
    }
}

Điều này có những ưu điểm là nó không thay đổi bất kỳ kích thước bố cục nào, nó cũng có cơ hội tốt để hoạt động tốt trong trường hợp có bản sửa lỗi trong khuôn khổ.

Đã thử nghiệm trong trình mô phỏng iPhone SE (điều này đã gây ra cho tôi vấn đề ban đầu của tôi) - đã gỡ lỗi liên quan đến ràng buộc.


Làm việc trong iOS 13 nhưng không trong iOS 12. Cố gắng trên iPhone X mô phỏng
Vyachaslav Gerchicov

@RichW đây là một cách tiếp cận tốt. Tuy nhiên, hãy cẩn thận, tôi tin rằng các phiên bản iOS cũ hơn sẽ gặp sự cố khi thay đổi một ràng buộc bắt buộc thành không yêu cầu trong thời gian chạy. stackoverflow.com/questions/31186187/…
Josh Bernfeld

0

Nếu bạn muốn giữ hoạt ảnh và tất cả các ràng buộc, bạn nên tìm một ràng buộc tiêu cực và làm cho nó tích cực trước khi trình bày bộ điều khiển cảnh báo.

// 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.