Swift default AlertViewController breaking constraints

前端 未结 9 656
野趣味
野趣味 2020-11-27 14:25

I am trying to use a default AlertViewController with style .actionSheet. For some reason, the alert causes a constraint error. As long as

相关标签:
9条回答
  • 2020-11-27 15:03

    The solution for Objective-C:

    1. Subclass your own Alert Controller from UIAlertController
    2. Define prune-function like in previous reply

      @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 讨论(0)
  • 2020-11-27 15:05

    It's a new bug in iOS versions:

    • 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

    The only thing we can do is to file a bug report to Apple (I just did that and you should too).

    I'll try to update answer for a new version(s) of iOS when it come out.

    0 讨论(0)
  • 2020-11-27 15:09

    Safe Solution

    You should not remove the constraint because it is used in the future with a correct value.

    As an alternative, you can change its constant to a positive value:

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

    And then to initialize your controller use:

    let controller = PXAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)
    
    0 讨论(0)
  • 2020-11-27 15:09

    Interesting ideas here. Personally I don't like the idea of deleting the constraint or changing it's value (size).

    As the issue hinges on the constraint resolution being forced into a position where it must break a mandated (priority 1000) constraint, a less brutal approach is just to tell the framework that this constraint could be broken if needed.

    So (based on Josh's "Safe" class):

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

    This has the advantages that it doesn't change any layout dimensions, it also stands a good chance of being well behaved in the event of a fix in the framework.

    Tested in iPhone SE simulator (which was giving me my original problem) - constraint related debug has gone.

    0 讨论(0)
  • 2020-11-27 15:11

    An alternative way of getting away from the NSLayoutConstraint bug, is to use preferredStyle: .alert instead of preferredStyle: .actionSheet. This works without generating warnings, but it will display the menu modally.

    0 讨论(0)
  • 2020-11-27 15:14

    The following removes the warning without needing to disable animation. And assuming Apple eventually fixes the root cause of the warning, it shouldn't break anything else.

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

    This can then be used like this:

    // After all addActions(...), just before calling present(...)
    alertController.pruneNegativeWidthConstraints()
    
    0 讨论(0)
提交回复
热议问题