您可以在不使用Storyboard的情况下在AutoLayout控制台输出中标记视图吗?

我正在观看关于AutoLayout的WWDC会谈,并且我已经了解到在视图上设置StoryboardID会使控制台输出的约束冲突更容易阅读(即您获得的是名称而不仅仅是地址)。 它们向您展示在Interface Builder中设置StoryboardID(以前只是“标识符”)的位置,但有没有办法在代码中执行此操作? 我正在尝试调试我自己没有编写的一大堆复杂的视图,这是一个100%编程的应用程序,它有许多不可满足的约束,而且(不知何故)在iOS 7中工作但在iOS中开始破解8.我希望能够清楚地了解哪个实际上导致了问题。

我查看了UIView类引用,没有找到“Identifier”或“StoryboardID”。

如果答案是“不”,那不会太惊讶。 毕竟它被称为故事板ID是有原因的。 但我希望得到一个确认,以及一个解决方法,以便尽可能使AutoLayout控制台转储更具可读性。

我希望能够清楚地了解哪个实际上导致了问题。

我有两个建议。

  1. 在Xcode中,在相同的应用程序运行期间(最好在获得某些关于约束的控制台转储后),选择Debug – > View Debugging – > Capture View Hierarchy。

    应用程序暂停,您将看到您的视图层次结构。 选择一个视图,然后在Object Inspector中显示其内存地址。 当你找到地址为0x8675309 ,就是那个。 另外,您可以在Size Inspector中看到定位此视图的约束。

  2. 实现我在本书中提供的约束记录器实用程序。 第二个报告视图层次结构以及标识符和约束。

这是公用事业; 在他们之间,我节省了数小时的困惑:

 extension NSLayoutConstraint { class func reportAmbiguity (var v:UIView?) { if v == nil { v = UIApplication.sharedApplication().keyWindow } for vv in v!.subviews as [UIView] { println("\(vv) \(vv.hasAmbiguousLayout())") if vv.subviews.count > 0 { self.reportAmbiguity(vv) } } } class func listConstraints (var v:UIView?) { if v == nil { v = UIApplication.sharedApplication().keyWindow } for vv in v!.subviews as [UIView] { let arr1 = vv.constraintsAffectingLayoutForAxis(.Horizontal) let arr2 = vv.constraintsAffectingLayoutForAxis(.Vertical) NSLog("\n\n%@\nH: %@\nV:%@", vv, arr1, arr2); if vv.subviews.count > 0 { self.listConstraints(vv) } } } } 

我参加派对有点晚了,但我希望这会有所帮助。 UIView确实有一个非常简单的属性,它通过它与UIAccessibilityIdentification的一致性:

 centerView.accessibilityIdentifier = "Center Foobar View" containerView.accessibilityIdentifier = "Container BingBat View" 

这会像您期望的那样产生调试器输出:

""

Erica Sadun在她关于NSLayout( iOS Auto Layout Demystified )的书中有一个相当酷的建议。 UIView有一个’tag’属性,只能接受NSInteger。 通过在UIView(或者实际上在NSObject上)上适当制作的类别,我们可以使用关联对象添加“viewName”属性 – 它接受一个字符串:

 //UIView+viewName.h #import  @interface UIView (NameTagO) - (NSString*)viewName; - (void)setViewName:(NSString*)viewName; @end //UIView+viewName.m #import "UIView+viewName.h" #import  @implementation UIView (viewName) - (NSString*)viewName { return (NSString*)objc_getAssociatedObject(self, @selector(viewName)); } - (void)setViewName:(NSString*)name { NSParameterAssert([viewName isKindOfClass:NSString.class]); objc_setAssociatedObject(self , @selector(viewName) , name , OBJC_ASSOCIATION_RETAIN_NONATOMIC); } 

然后,您可以向视图添加字符串标记:

  UIView* view = [UIView alloc] init..]; view.viewName = @"name"; 

日志记录:

  NSLog(%@"view: %@",view.viewName) 

在NSHipster博客上有一个关于相关对象的非常简洁的解释,刚刚为Swift重新审视过

这是viewName类别的一个快速版本:

 import Foundation import UIKit extension UIView { private struct AssociatedKeys { static var viewName = "foundry_viewName" } var viewName: String? { get { return objc_getAssociatedObject(self, &AssociatedKeys.viewName) as? String } set { if let newValue = newValue { objc_setAssociatedObject( self, &AssociatedKeys.viewName, newValue as NSString?, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC) ) } } } } 

它也可以是NSObject上的一个类别,尽管在这种情况下它是我们感兴趣的UIView上使用的。通过这个类别,您可以为视图提供有意义的标记,以便进行日志记录和提取。 例如,你可以将这种方法与Matt在他的回答中描述的记录器结合使用(来自他非常全面的关于iOS的书 )来使用有意义的名称记录视图。

创建视图时,您可以设置其viewName:

 let view: UIView = UIView.init() view.viewName = "test" 

然后Matt的reportAmbiguity可以扩展一些reportAmbiguityfunction:

 class func reportAmbiguity (var v:UIView?) { if v == nil { v = UIApplication.sharedApplication().keyWindow } for vv in v!.subviews as [UIView] { let viewName = vv.viewName? if (viewName != nil) { println("\view \(vv.viewName) \(vv.hasAmbiguousLayout())") } else { println("\(vv) \(vv.hasAmbiguousLayout())") } if vv.subviews.count > 0 { //self.reportAmbiguity(vv) } } } 

唯一的烦恼是必须解压viewName可选。 您不必这样做,但如果不这样做,字符串插值将报告’可选(“测试”)’,这有点麻烦。