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

时间:2015-01-28 22:35:00

标签: ios cocoa-touch autolayout

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

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

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

3 个答案:

答案 0 :(得分:7)

  

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

我有两个建议。

  1. 在Xcode中,在相同的应用程序运行期间(最好是在获得某些关于约束的控制台转储后),选择Debug - &gt;查看调试 - &gt;捕获视图层次结构。

    应用暂停,系统会显示您的视图层次结构。选择一个视图,然后在Object Inspector中显示其内存地址。当您找到地址为0x8675309的地址时,就是那个地址。另外,您可以在尺寸检查器中看到定位此视图的约束。

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

  3. 以下是实用程序;在他们之间,我节省了数小时的困惑:

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

答案 1 :(得分:4)

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

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

这会产生类似你期望的调试器输出:

  

"<NSLayoutConstraint:0x7fe3fa3ade50 Center Foobar View.centerX == Container BingBat View.centerX (Names: Center Foobar View:0x7fe3fa3a1700, Container BingBat View:0x7fe3fa3856c0 )>"

答案 2 :(得分:1)

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

//UIView+viewName.h
#import <UIKit/UIKit.h>

@interface UIView (NameTagO)

- (NSString*)viewName;
- (void)setViewName:(NSString*)viewName;

@end

//UIView+viewName.m
#import "UIView+viewName.h"
#import <objc/runtime.h>


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

有一个非常好的简洁explanation of associated objects on the NSHipster blog刚刚revisited for 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在其答案(来自他的really comprehensive book on iOS)中描述的记录器结合使用,以使用有意义的名称记录视图。

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

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

然后马特的reportAmbiguity功能可以延长一点:

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可选项。你不来做这件事,但如果你不这样做,字符串插值将报告'可选(“测试”)',这有点麻烦。