自动布局:removeFromSuperview / removeConstraints会抛出异常并且会严重崩溃

56

我们有选择性地使用自动布局约束,主要是为了将标签相对于可编辑字段元素(通常是UITextView、UITextField)进行定位。但是,自从为这些字段实现自动布局以来,每当我们卸载视图、释放内存等操作时,就会出现一个令人讨厌的异常和崩溃。异常发生在尝试在卸载视图之前从视图中删除约束时。

我们的视图/控制器层次结构如下:

UITableViewController (plain style, but with cell appearance to mimic grouped style)
--> UITableViewCell
----> UIViewController (container for editable form)
------> UICollectionViewController (editable form)
--------> UICollectionViewCell
-----------> UIViewController (editable field)
--------------> UILabel (field label)                   **HAS CONSTRAINTS**
--------------> UITextView / UITextField (field value)  **HAS CONSTRAINTS**

经常在释放/替换/重新加载上层表格单元时,我们会遇到一个巨大的异常并崩溃,因为它试图在内部释放/卸载视图层次结构。

我已经试图通过捕获异常(没有帮助)和在释放/卸载之前强制删除所有受影响视图及其子视图上的约束(在viewWillDisappear:中),但似乎都没有帮助。我甚至尝试一步步删除这些约束,看看是否有特定的约束导致问题,但当我们准备消失时,在容器上调用removeConstraint:removeConstraints:时所有约束都会崩溃。

我很困惑!这里是我们异常的一部分--大约剪掉了3000行,如果你需要更多,请问。

Exception while deallocating view: { Rows:
    0x18911270.posErrorMarker == 4 + 1*0x18911270.negError + 1*0x189112f0.marker + -1*0x189113f0.negError + 1*0x189113f0.posErrorMarker + 1*0x18911a60.marker + -0.5*0x1892dae0.negError + 0.5*0x1892dae0.posErrorMarker + 1*0x18951520.negError + -1*0x18951520.posErrorMarker + -0.5*0x18958090.negError + 0.5*0x18958090.posErrorMarker
    0x189112b0.negError == 12 + 1*0x189112b0.posErrorMarker + -1*0x189112f0.marker + 1*0x189113f0.negError + -1*0x189113f0.posErrorMarker + -1*0x18911a60.marker + 1*0x18925530.marker + 0.5*0x1892dae0.negError + -0.5*0x1892dae0.posErrorMarker + 1*0x1893e080.marker + 0.5*0x18958090.negError + -0.5*0x18958090.posErrorMarker + 1*0x18963640.marker
    0x18911370.negError == 9 + -1*0x189112f0.marker + 1*0x18911370.posErrorMarker + 1*0x18925530.marker + 1*0x1892dae0.negError + -1*0x1892dae0.posErrorMarker + 1*0x1893e080.marker + 1*0x18963640.marker
    0x189113b0.slackMarker == 2 + -1*0x189107d0.marker + 1*0x18910b90.negError + -1*0x18910b90.posErrorMarker + 

      ........ EXPLETIVES DELETED .........

   UITableView:0xca2b000.contentHeight == 36 + 1*0xc221c00.marker
   UITableView:0xca2b000.contentWidth == 704 + 1*0xc239470.marker
   UITableView:0xca2b000.minX == 0 + 1*0xc2a23f0.marker + -0.5*0xc2a2590.marker
   UITableView:0xca2b000.minY == 0 + 1*0xc2a25d0.marker + -0.5*0xc2a2630.marker
   UITableViewCellContentView:0x18ab13d0.Height == 174 + 1*0x18abd4f0.marker
   UITableViewCellContentView:0x18ab13d0.Width == 704 + 1*0x18abd470.marker

      ........ EXPLETIVES DELETED .........

    <NSAutoresizingMaskLayoutConstraint:0x18988bc0 h=-&- v=-&- UIView:0x18911e50.midY == UIView:0x1892d0c0.midY>        Marker:0x18988bc0.marker
    <NSAutoresizingMaskLayoutConstraint:0x18994b40 h=-&- v=-&- UIView:0xc4a6fb0.midX == UIView:0xc4b4990.midX>      Marker:0x18994b40.marker
    <NSAutoresizingMaskLayoutConstraint:0x18998480 h=-&- v=-&- UIView:0x18915180.width == UIView:0xc4c5970.width>       Marker:0x18998480.marker
    <NSAutoresizingMaskLayoutConstraint:0x18aae320 h=--& v=--& TapSectionalTableViewCell:0x18a3d270.midX == + 352>      Marker:0x18aae320.marker
    <NSAutoresizingMaskLayoutConstraint:0x18aae410 h=--& v=--& H:[TapSectionalTableViewCell:0x18a3d270(704)]>       Marker:0x18aae410.marker
    <NSAutoresizingMaskLayoutConstraint:0x18aae450 h=--& v=--& TapSectionalTableViewCell:0x18a3d270.midY == + 144>      Marker:0x18aae450.marker

      ........ EXPLETIVES DELETED .........

    <NSAutoresizingMaskLayoutConstraint:0xc2de2f0 h=--& v=--& TapGenericCollectionCell:0xc2ac500.midX == + 499>     Marker:0xc2de2f0.marker
    <NSAutoresizingMaskLayoutConstraint:0xc2de3b0 h=--& v=--& V:[TapGenericCollectionCell:0xc2ac500(34)]>       Marker:0xc2de3b0.marker
    <NSAutoresizingMaskLayoutConstraint:0xc2de430 h=-&- v=-&- UIView:0x18953f80.height == UIView:0xc2acb20.height>      Marker:0xc2de430.marker
    <NSAutoresizingMaskLayoutConstraint:0xc2de520 h=-&- v=-&- UIView:0x18923af0.height == UIView:0xc2ae570.height>      Marker:0xc2de520.marker
    <NSAutoresizingMaskLayoutConstraint:0xc2de560 h=--& v=--& H:[TapGenericCollectionCell:0xc2ac500(280)]>      Marker:0xc2de560.marker

      ........ EXPLETIVES DELETED .........

    <NSContentSizeLayoutConstraint:0xc2f5730 H:[_UIBaselineLayoutStrut:0x18994a30(0)] Hug:250 CompressionResistance:750>        Marker:0xc2f5730.posErrorMarker
    <NSContentSizeLayoutConstraint:0xc2f5730 H:[_UIBaselineLayoutStrut:0x18994a30(0)] Hug:250 CompressionResistance:750>        Marker:0xc2f5730.posErrorMarker
    <NSContentSizeLayoutConstraint:0xc2f5770 V:[_UIBaselineLayoutStrut:0x18994a30(18)] Hug:250 CompressionResistance:750>       Marker:0xc2f5770.posErrorMarker

internal error.  Cannot find an outgoing row head for incoming head UIView:0x189712b0.Width, which should never happen.'
/**** BEGIN Individual Field Controller - This code is from the base individual field controller used in our editable form collection *****/

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.clipsToBounds = YES;
    self.view.opaque = YES;

    CGRect viewFrame = self.view.frame;
    viewFrame.size = [self defaultFieldSize];
    self.view.frame = viewFrame;

    if (self.backgroundColor) {
        self.view.backgroundColor = self.backgroundColor;
    }
    else {
        self.view.backgroundColor = [UIColor whiteColor];
    }
    [self createLabelAndField];

    [self setLabelAndFieldContraints];

    [self.view addConstraints:self.labelValueConstraints];
    [self.view setNeedsUpdateConstraints];
}

- (void)createLabelAndField {
    [self removeLabelAndField];

    UILabel *label = [[UILabel alloc] init];
    label.font = self.labelFont;
    label.textColor = self.labelColor;
    label.lineBreakMode = NSLineBreakByWordWrapping;
    label.textAlignment = NSTextAlignmentLeft;
    label.adjustsFontSizeToFitWidth = NO;
    label.numberOfLines = 0;

    if (self.backgroundColor) {
        label.backgroundColor = self.backgroundColor;
    }
    else {
        label.backgroundColor = [UIColor whiteColor];
    }

    [self.view addSubview:label];

    self.label = label;


    /// EXAMPLE valueView initialization from a subclass that handles long text

    TapEditableTextView *textView = [[TapEditableTextView alloc] init];
    if (self.hasLabelOverValue) {
        textView.shouldMimicTextField = NO;
    }
    else {
        textView.shouldMimicTextField = YES;
    }
    textView.delegate = self;
    textView.keyboardType = UIKeyboardTypeDefault;
    textView.font = self.valueFont;
    textView.textColor = self.valueColor;
    textView.textAlignment = NSTextAlignmentLeft;
    textView.normalBackgroundColor = self.backgroundColor;
    textView.editable = NO;
    textView.textLines = self.textLines;

    self.valueTextView = textView;
    self.valueView = textView;
    [self.view addSubview:textView];
}

- (void)removeLabelAndField {
    [self clearConstraints];

    if (self.label) {
        [self.label removeFromSuperview];
        self.label = nil;
    }
    if (self.valueView) {
        [self.valueView removeFromSuperview];
        self.valueView = nil;
    }
}

- (void)clearConstraints {
    if (self.isViewLoaded && self.labelValueConstraints) {
        [self.view removeConstraints:self.labelValueConstraints];
    }
    self.labelValueConstraints = nil;
    self.labelToValueHorizConstraint = nil;
    self.valueWidthConstraint = nil;
}

// This is called in our field's viewDidLoad, after we've created our label and valueView (UITextField, UITextView, etc)
- (void)setLabelAndFieldContraints {
    [self clearConstraints];

    self.labelValueConstraints = [NSMutableArray array];

    self.label.translatesAutoresizingMaskIntoConstraints = NO;
    self.valueView.translatesAutoresizingMaskIntoConstraints = NO;

    NSLayoutConstraint *constraint = nil;

    constraint = [NSLayoutConstraint
                  constraintWithItem:self.label attribute:NSLayoutAttributeLeft
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeLeft
                  multiplier:1.0f constant:self.labelValueGap];
    constraint.priority = UILayoutPriorityRequired;
    [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.label attribute:NSLayoutAttributeTop
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeTop
                  multiplier:1.0f constant:0];
    constraint.priority = 550;
    [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.label attribute:NSLayoutAttributeBottom
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeBottom
                  multiplier:1.0f constant:0];
    constraint.priority = 400;
    [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.valueView attribute:NSLayoutAttributeTop
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeTop
                  multiplier:1.0f constant:0];
    constraint.priority = UILayoutPriorityRequired;
    [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.valueView attribute:NSLayoutAttributeBottom
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeBottom
                  multiplier:1.0f constant:0];
    constraint.priority = 499;
    [self.labelValueConstraints addObject:constraint];


     constraint = [NSLayoutConstraint
                  constraintWithItem:self.valueView attribute:NSLayoutAttributeRight
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeRight
                  multiplier:1.0f constant: -(kDisclosureWidth + self.labelValueGap) ];
     constraint.priority = 901;
     [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.valueView attribute:NSLayoutAttributeLeading
                  relatedBy:NSLayoutRelationGreaterThanOrEqual
                  toItem:self.label attribute:NSLayoutAttributeTrailing
                  multiplier:1.0f constant:self.labelValueGap];
    constraint.priority = UILayoutPriorityDefaultHigh + 1;
    [self.labelValueConstraints addObject:constraint];
    self.labelToValueHorizConstraint = constraint;


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.label attribute:NSLayoutAttributeBaseline
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.valueView attribute:NSLayoutAttributeBaseline
                  multiplier:1.0f constant:0.f];
    constraint.priority = 600;
    [self.labelValueConstraints addObject:constraint];


    constraint = [NSLayoutConstraint
                  constraintWithItem:self.valueView attribute:NSLayoutAttributeWidth
                  relatedBy:NSLayoutRelationEqual
                  toItem:self.view attribute:NSLayoutAttributeWidth
                  multiplier:(1.f - self.labelWidthPercentage) constant:0];
    constraint.priority = 305;
    [self.labelValueConstraints addObject:constraint];
    self.valueWidthConstraint = constraint;


    [self setCompressionAndHuggingForLabelView:self.label];
    [self setCompressionAndHuggingForValueView:self.valueView];
}

- (void)setCompressionAndHuggingForLabelView:(UILabel *)labelView {
    if (!labelView) {
        return;
    }
    [labelView setContentCompressionResistancePriority:510 forAxis:UILayoutConstraintAxisHorizontal];
    [labelView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
    [labelView setContentHuggingPriority:450 forAxis:UILayoutConstraintAxisHorizontal];
    [labelView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
}

- (void)setCompressionAndHuggingForValueView:(UIView *)valueView {
    if (!valueView) {
        return;
    }
    [valueView setContentCompressionResistancePriority:509 forAxis:UILayoutConstraintAxisHorizontal];
    [valueView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
    [valueView setContentHuggingPriority:300 forAxis:UILayoutConstraintAxisHorizontal];
    [valueView setContentHuggingPriority:650 forAxis:UILayoutConstraintAxisVertical];
}

/****** END Individual Field Controller ******/

3
此外,1)Cocoa Touch 框架中的异常意味着致命错误,不要尝试捕获它们来解决问题,2)我们能看到一些代码吗? - Matt Mc
我会再仔细检查一下,但我几乎可以确定这些都不是后台程序。是的,捕获线程是最后的努力(失败了)。在上面添加一些来自我们表单字段控制器的代码... - Greg Combs
1
有一点需要注意,当你添加所有这些约束条件时,你不需要调用 setNeedsUpdateConstraints 而是应该调用 setNeedsLayout。根据关于 updateConstraints 的文档,“自定义视图应通过重写此方法来设置它们自己的约束条件。”虽然我怀疑这不是问题所在,但总体上代码看起来还不错。需要更多关于你的应用程序的信息:你是否使用 CoreData?什么是“可编辑表单集合”?这里的结构是什么? :) - Matt Mc
1
我明白了。我的直觉告诉我,后台线程上发生了某种调用,或以某种方式违反了UIKit的程序。这就是为什么你会得到NSInternalInconsistency错误的原因;我在过去也遇到过类似的问题。但是如果没有看到你整个项目,我真的无法找出它。这里有类似的建议:https://twitter.com/radiantav/status/296588220626509824 ...如果你认为某人的推文是权威的话XD - Matt Mc
1
脏话?给我看未经审查的异常! - marczellm
显示剩余2条评论
14个回答

95

我和一位苹果工程师就这个崩溃问题进行了(广泛)交谈。

以下是最有可能的两个原因:

  1. 您有一个无效的约束条件,例如 view1.left = view2.left + 20,其中view2意外为空或乘数为0。请确保反复检查您的约束条件是否正确。以下是两个有问题的约束条件的示例:

    // The first constraint would be a problem if view2 were nil
    [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:1 constant:20];
    // The second constraint is a problem because the 0 multiplier causes view2 to be "lost"
    [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:0 constant:5];
    
    您遇到了Foundation自动布局引擎内部的一个bug,与浮点精度损失相关。当程序崩溃时,您可以通过在控制台中搜索异常日志(通常很大)中的非常小的(接近零的)浮点数来确定这一点,例如:

    <505:-7.45058e-08>*PWPlotLegendEntryView:0x600000582be0.Height{id: 34609} +

    (在控制台输出中搜索e-以查找此类小数字。)在这种情况下,该数字(如本例中的-7.45058e-08)表示内部引擎在解决约束时的某个特定时间点上的系数。在这种情况下,该数字应为0,但由于自动布局引擎使用浮点数进行计算的方式,它变成了一个极小的负数,导致整个程序崩溃。如果您在输出中找到了这样的数字,则知道您遇到了这个bug。

    如何解决此问题?

    更改添加(激活)约束的顺序可能会改变内部引擎中计算的顺序,从而使得没有出现任何问题的精度损失。

    当您更改视图的内容压缩阻力或内容抱紧优先级时,可能会更频繁地出现此问题,因此请尝试注释掉执行此操作的任何代码,以查看是否导致此bug发生,或将其在约束设置代码中提前或延后执行。

    关于我的具体情况的更多细节:

    我在iOS上遇到了这个崩溃。重现步骤非常有趣:

    1. 推入一个包含表视图的视图控制器(在导航控制器中)。
    2. 表视图必须包含足够的单元格,以便它们不都适合可见区域,然后必须滚动到最后一个单元格,然后再向上滚动一点(可能会触发单元格的重复使用,从而触发此问题)。
    3. 然后,在弹出包含表视图的视图控制器的导航栈后,当弹出动画完成时,应用程序将在将视图控制器的视图从视图层次结构中移除的时候崩溃。

    经过多次尝试,我能够将问题隔离到一个特定的地方:为表视图单元格中的UIImageView设置内容压缩阻力和内容抱紧优先级。在这种情况下,该图像视图正在使用自动布局在单元格内定位,并且为了实现正确的布局,图像视图需要恰好是其固有内容大小(即其图像的大小)。

    这是有问题的代码:

    // Inside of the UITableViewCell's updateConstraints method...
    
    [self.imageView setContentCompressionResistancePriority:​UILayoutPriorityRequired forAxis:​UILayoutConstraintAxisHorizontal];        
    [self.imageView setContentCompressionResistancePriority:​UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];       
    [self.imageView setContentHuggingPriority:​UILayoutPriorityRequired forAxis:​UILayoutConstraintAxisHorizontal];      
    [self.imageView setContentHuggingPriority:​UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    

    移除上述代码,并用两个必需优先级的约束条件来固定图像视图的宽度和高度为图像的大小,可以达到相同的结果,但避免了崩溃。以下是替换代码(使用PureLayout):

    [self.imageView autoSetDimensionsToSize:self.imageView.image.size];
    

    我还发现,将问题所在的四行代码移动到我的约束设置代码中的其他位置可以解决这个问题,可能是因为这改变了计算顺序,从而避免了精度丢失的问题。


3
这个答案救了我们的一命!我们的视图仅在iPad 2上崩溃,原因是浮点错误。将倍乘数从1.6设置为1.59999解决了问题。 - SpacyRicochet
3
@smileyborg — 这是一些非常出色的调查工作... 很棒的回答! - Greg Combs
3
我遇到了同样的问题,我在模态视图中使用了Autolayout布局子视图,当我再次弹出模态视图时,应用程序崩溃了。我在一个UIView中添加了一个约束,将宽度设置为0.8,作为该约束所在的视图控制器视图的乘数。这是通过代码完成的。将该值设置为除0.8以外的任何其他值,应用程序就不会崩溃。这是一个非常奇怪的错误。 - matfin
1
StackOverflow上最好的答案之一!我把所有的乘数都改成了像1.999、0.333、1.449、0.799等数字(感谢建议这样做的评论)。 - Ferran Maylinch
1
通过您的评论,我发现一些乘数如0.4、0.8、1.6等会引起这个错误。我一直在使用这个浮点转换器,但还没有找到这些数字崩溃的原因。它可能与它们的位模式有关,但不知道具体是什么。也许有人可以为此提供一些线索。 - Ferran Maylinch
显示剩余11条评论

14

内存释放问题 —— 一种可能性

处理自动布局的代码可能会在主线程上运行,但其中一个在后台运行并使用视图(也许是间接地)的块,可能会持有视图或其所有者(如视图控制器)的强引用(这是Objective-C块的默认行为)。当这样的块在后台队列上运行并被释放时,它捕获的强引用将在同一队列上被释放。你可能会遇到一个众所周知的 内存释放问题

  1. 在你的视图控制器中,确保在所有不需要强引用(并且可能在后台运行)的块中,你都使用了对 self 的弱引用。你可以像这样声明: __weak typeof(self) weakSelf = self; 再在块内部使用 weakSelf

  2. 对于任何持有对你的视图的引用的本地变量,也要确保它们的值被作为弱引用捕获。

另一种可能性

在我的工作中,我遇到了类似的问题,当一个隐藏的视图参与布局时,在 iOS 6 上会出现内存释放问题。从层次结构中移除视图(-[UIView removeFromSuperview])而不是将其 hidden 属性设置为 YES 可以修复这个问题。


很好的观点。我不知道这是否解决了我的问题(我已经重新设计了相关代码的大部分),但这肯定是可能的。我没有考虑到释放内存的问题 - 这太棒了。 - Greg Combs
谢谢,@GregCombs!如果这对你以后有所帮助,我很高兴。 - Leon Deriglazov

3
让@smileyborg的出色回答更具可操作性:
如果您有任何具有可能受到浮点精度问题影响的乘数约束,则会发生这种情况。
解决方法:
1.检查所有具有乘数的约束(在布局代码中或通过手动编辑storyboard/nib并搜索multiplier=)。
2.如果乘数不是一个“漂亮”的二次幂浮点数,请将其转换为最接近的一次幂(可以使用浮点计算器)。
轻松完成2的方法是输入您想要的数字,然后在计算器底部的四舍五入的小数值与值匹配之前关闭尾数中的较低精度位。

3

我曾经遇到过同样的问题,通过在IB中逐个删除约束来解决问题,直到崩溃问题得到解决。这缩小了问题范围到是哪个约束造成的崩溃。然后我重新添加了该约束,但反转了项目:

enter image description here

你也可能像我一样幸运,并能够轻松地解决你的AL问题。


1
当然,一个对称算子(相等)为什么会是对称的呢? - Rhythmic Fistman
由于约束被添加到自动布局引擎(NSISEngine)的方式,看起来对称的约束实际上可能不是对称的。至少,反转约束两边可能会改变计算执行的顺序,这可能会暴露(或避免)SDK中的Apple错误。 - smileyborg
意识到自动布局的约束求解器使用浮点数可以很好地解释其一些奇怪的行为!我从未想过这一点。谢谢@smileyborg! - Rhythmic Fistman

2

如果在任何iOS版本> 8.0中遇到此问题,Apple文档建议使用NSLayoutConstraint上的“active”属性而不是UIView上的removeConstraint/addConstraint函数。苹果文档addConstraint参考


1

我花了整个周末试图找出我们的应用程序崩溃的原因,调试控制台中充满了以下内容:

Failed to rebuild layout engine without detectable loss of precision. This should never happen. Performance and correctness may suffer.

这篇文章使我朝着正确的方向前进,因为错误实际上是由于具有多个约束条件的乘数设置为任意浮点数并具有不良精度引起的,如@yonix在他的答案中解释。

在我们的情况下,乘数是通过用户在屏幕上拖动形状动态设置的,范围从0到1.0,因此自然会存在需要在尾数中设置许多位才能定义好的乘数的情况。

通过将乘数对齐到1/2^n的倍数,可以用更少的尾数位精确地定义乘数,从而避免了问题。

我们发现对于我们来说9位(1/2^9)就足够了,通过乘以这个因子、截断再除以这个因子即可对齐乘数。

    let factor = 512.0 // 9-bits alignment
    let alignedMultiplier = (multiplier * factor).rounded() / factor

在使用约束之前,确保所有用户定义的乘数都对齐,解决了我们的问题。

因此,我在这篇文章中添加了一些内容,以备将来参考,以防其他人遇到相同的问题。


1
当我使用一个空参数调用removeConstraints:时,就会出现崩溃。

1
在我的情况下,这是一个宽度比例约束,使用8:9的乘数。我将其更改为7:9,然后它就起作用了。
顺便说一句,找到约束最简单的方法是从视图控制器中开始删除视图。使用二进制算法进行操作:删除一半的视图,然后删除使应用程序崩溃的一半的那一半,以此类推。

1
在您的情况下,这听起来像是@yonix所提到的浮点精度问题。 - Greg Combs
正确,但是当我发布答案时,yonix的答案还不存在。 - Borzh

1
对我来说,问题在于我在调用 dequeueReusableCellWithReuseIdentifier 并设置我的 UICollectionViewCell 属性后,按照我自己的时间逐个删除约束。解决方案是改为调用:
    [_myUICollectionViewCell setNeedsUpdateConstraints];

并覆盖:

    -(void)updateConstraints 

然后在那里搞砸了。看起来你不能随意删除约束。


0
根据苹果文档:
在开发 iOS 8.0 或更高版本时,应将约束的 active 属性设置为 YES,而不是直接调用 addConstraint: 方法。active 属性会自动将约束添加到正确的视图中。
在我的情况下,我需要修改宽度约束。
for var constraint in self.navigationBar.constraints {
            if constraint.identifier == "theProgressWidth" {
                let sizeWidth = self.navigationBar.frame.size.width
                constraint = NSLayoutConstraint(item: progress!, attribute: .Width, relatedBy: .Equal, toItem: self.navigationBar, attribute: .Width, multiplier: ((sizeWidth * (level / 100)) / sizeWidth), constant: 0)
                constraint.active = true
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接