8

我很欣赏 XCode 会捕捉到无法满足的情况NSLayoutConstraints并为您提供一些有关它的信息,但是我不知道如何获取控件/约束的内存地址并将其映射到有问题的实际控件。

我的应用程序相当大(大约 20 个屏幕),其中一些是大型 UIViewControllers,其中包含多个子视图控制器。有些是带有自定义单元格的 UITableViews 和带有自定义单元格的 UICollectionViews。我花了很多时间来解决这个错误的原因(发生在横向旋转时)

这是来自我的控制台的信息:

2013-05-02 11:18:53.225 Smile[7519:c07] 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. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x16083b60 h=-&- v=-&- UIView:0xa5a1d00.width == UIWindow:0xa09eee0.width>",
    "<NSLayoutConstraint:0xa5a2180 V:[UIView:0xa59f160]-(954)-|   (Names: '|':UIView:0xa5a1d00 )>",
    "<NSLayoutConstraint:0xa5a2140 V:|-(0)-[UIView:0xa59f160]   (Names: '|':UIView:0xa5a1d00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0xa593340 h=--- v=--- H:[UIWindow:0xa09eee0(768)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xa5a2180 V:[UIView:0xa59f160]-(954)-|   (Names: '|':UIView:0xa5a1d00 )>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

如您所见,列出了内存地址。将这些粘贴到监视窗口的搜索栏中并没有透露太多信息。同样筛选线程和队列调用堆栈,我只在这个断点(异常断点)上得到反汇编代码。

4

3 回答 3

8

多亏了这些网站,我找到了一种解决方案:Debugging iOS AutoLayout IssuesDancing with the Debugger。借助 Xcode 控制台,您可以确定问题来自哪个视图。

解决方案:

  • 步骤1:

    • 打开断点导航器 (cmd+7)
    • 点击左下角的“添加”按钮
    • 选择“添加符号断点”
    • 输入“符号”的地方UIViewAlertForUnsatisfiableConstraints
  • 第 2 步:运行您的项目。当控制台打印Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fc82d3e18a0 H:[UIView:0x7fc82aba1210(768)]>时,复制视图的内存地址(0x7fc82aba1210这里)。

  • 步骤 3:更改此视图的背景以在控制台中使用此命令了解它是哪一个:e (void)[0x7fc82d3e18a0 setBackgroundColor:[UIColor redColor]]

  • 第 4 步:继续程序并查看更新后的 UI。具有红色背景颜色的视图是有问题的视图。

  • 第 5 步:回到您的 IB 并找出哪个约束是该视图的问题。

于 2015-06-09T11:05:31.393 回答
4

简短的回答不是真的,OS X 有一个标识符属性,可以在 NSLayoutConstraint 用来帮助识别视图但在 iOS 上尚不存在的视图上设置。

然而,有一个“变通方法”可以在 iOS 上实现类似的东西,但它并不漂亮。首先,您必须为每个视图建立一个名称,这在 UIView 类别中完成。然后您必须更新 NSLayoutConstraint 描述的输出以使用新标签。

@interface NSLayoutConstraint (Nametags)
- (NSString *)asciiArtDescription;
@end

@interface UIView (Nametags)
@property (nonatomic, strong) NSString *nametag;
@end

然后提供如下实现来改变 NSLayout 的输出

#import <objc/objc-runtime.h>

static const char nametag_key;
@implementation NSLayoutConstraint (Nametags)

- (NSString *)description {
    NSMutableString *myDescription = [NSMutableString stringWithFormat:@"%@, ", [self asciiArtDescription]];
    UIView *firstView = (UIView *)[self firstItem];
    if (firstView) {
        [myDescription appendFormat:@"First View: 0x%0x: %@, ", (int)firstView, firstView.nametag];
    }

    UIView *secondView = (UIView *)[self secondItem];
    if (secondView) {
        [myDescription appendFormat:@"Second View: 0x%0x: %@", (int)secondView, secondView.nametag];
    }

    return myDescription;
}
@end

@implementation UIView (Nametags)
- (id) nametag {
    return objc_getAssociatedObject(self, (void *) &nametag_key);
}

- (void)setNametag:(NSString *) theNametag {
    objc_setAssociatedObject(self, (void *) &nametag_key, theNametag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

然后你就可以像这样在你的视图上设置名称

self.view.nametag = @"MyUsefulName";

或者在 IB 中定义它们

IB中的名牌

最后,当您转储约束时,您将获得您在输出中提供的名称。

"V:|-(202)-[UIView:0x75606f0], First View: 0x75606f0: Blue View, Second View: 0x7560bd0: (null)",
"V:[UIView:0x75606f0]-(72)-|, First View: 0x7560bd0: (null), Second View: 0x75606f0: Blue View",
"V:[UIView:0x7560bd0(548)], First View: 0x7560bd0: (null), "

您应该只在调试版本中使用此代码,不要在应用程序中发布它,因为它会访问私有 [NSLayoutConstraint asciiArtDescription] 方法。

于 2013-05-02T20:25:56.900 回答
0

您可以尝试使用visualizeConstraints:,如此所述,看看您是否可以通过这种方式找到不好的约束。

于 2013-05-02T19:28:32.680 回答