Swift默认AlertViewController突破约束

时间:2019-04-12 13:49:45

标签: ios swift iphone xcode ios-autolayout

我正在尝试使用样式为 .actionSheet 的默认AlertViewController。由于某种原因,该警报会导致约束错误。只要没有通过按钮触发(显示)alertController,整个视图就不会出现约束错误。难道这是Xcode的 bug?

我得到的确切错误如下:

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

尽管通过打破限制,警报可以在所有屏幕尺寸上正确显示,但我非常感谢我能提供的任何帮助。

11 个答案:

答案 0 :(得分:14)

以下内容删除了警告,而无需禁用动画。并且假设苹果最终解决了警告的根本原因,那么它就不会破坏其他任何东西。

-:77.294: Opening and ending tag mismatch: img line 77 and a
 middle;" src="/Content/images/gongshangbiaoshi.gif" alt="" class="footpic"></a>
                                                                               ^
-:77.301: Opening and ending tag mismatch: a line 77 and span
;" src="/Content/images/gongshangbiaoshi.gif" alt="" class="footpic"></a></span>
                                                                               ^
-:77.332: Opening and ending tag mismatch: span line 77 and p
i.gif" alt="" class="footpic"></a></span><br />©2014-2018中国知网(CNKI) </p
                                                                               ^
-:79.15: Opening and ending tag mismatch: p line 74 and div
        </div>
              ^
-:92.8: Opening and ending tag mismatch: div line 61 and body
</body>
       ^
-:93.8: Opening and ending tag mismatch: body line 11 and html
</html>
       ^
-:94.1: Premature end of data in tag html line 2

^

然后可以这样使用:

            var findResult = _coll.Find(cnFilter & orderDateFilter & deliveryDateFilter & statusFilter & keywordFilter);
            var countResult = _coll.Find(cnFilter & orderDateFilter & deliveryDateFilter & statusFilter & keywordFilter).CountDocumentsAsync();

答案 1 :(得分:8)

此错误并不严重,似乎是Apple修复的错误。出现后,此约束以动画样式显示。 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)

答案 2 :(得分:8)

添加到this答案...这似乎为我消除了该问题,并且不需要对现有代码进行任何更改。

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

答案 3 :(得分:4)

这是iOS版本中的新错误:

  • 12.2
  • 12.3
  • 12.4
  • 13.0
  • 13.1
  • 13.2
  • 13.2.3
  • 13.3

我们唯一能做的就是向Apple提交错误报告(我只是这样做,您也应该这样做)。

我将尝试发布新版本的iOS的答案。

答案 4 :(得分:2)

安全解决方案

您不应删除该约束,因为将来会以正确的值使用该约束。

或者,您可以将其常数更改为正值:

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)

答案 5 :(得分:1)

摆脱NSLayoutConstraint错误的另一种方法是使用preferredStyle: .alert而不是preferredStyle: .actionSheet。此方法不会产生警告,但会以模态显示菜单。

答案 6 :(得分:0)

Objective-C的解决方案:

  1. 从UIAlertController继承您自己的警报控制器
  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
    

答案 7 :(得分:0)

有趣的想法在这里。我个人不喜欢删除约束或更改其值(大小)的想法。

由于问题取决于约束解决方案被迫达到必须打破强制性(优先级1000)约束的位置,因此,一种比较不残酷的方法只是告诉框架,如果需要,可以打破此约束。

所以(基于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)
                }
            }
        }
    }
}

它的优点是它不会更改任何布局尺寸,而且在框架修复的情况下,也很有可能表现良好。

在iPhone SE模拟器中进行了测试(这使我遇到了最初的问题)-与约束相关的调试已消失。

答案 8 :(得分:0)

如果要保留动画和所有约束,则应在呈现警报控制器之前找到一个负约束并将其设为正。

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

答案 9 :(得分:0)

这里是我用来解决问题的函数。出现问题是因为约束是负的,我不知道为什么。

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

答案 10 :(得分:0)

创建视图扩展以获取所有约束

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

创建您的提醒并在演示时调用 fixConstraints()

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