扩展UIView以自动添加标签(或执行另一个init操作)

时间:2015-08-02 16:19:57

标签: ios swift

最近在工作中,我的任务是组建一个应用程序,以编程方式而不是通过故事板创建所有视图。除了在运行应用程序和调整视图之间更多的来回之外,这在大多数情况下都运行良好。作为一个有趣的项目,我想建立一个" debug"帮助那些在同一条船上的人。

使用对齐/样式真正帮助我的是第三方库,用于类似CSS的样式(http://classy.as),这使我可以在将以下代码段添加到样式表后查看每个图层/视图的边界:

^UIView {
    layer: @{
        borderWidth: 1;
    }
}

我想要类似的东西,但左上角附有标签,用于命名组件的类名。实际上,我想要的是每个UIView上的自动生成和自动附加标签,最好是可以通过变量(即debugLabel)切换的标签。我试图通过以下代码实现这一点:

private var debugLabelsEnabled: Bool = false
extension UIView {
    @IBInspectable var debugLabel: Bool! {
        get {
            return objc_getAssociatedObject(self, &debugLabelsEnabled) as? Bool
        }
        set(value) {
            objc_setAssociatedObject(self, &debugLabelsEnabled, value, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
            let label = UILabel(frame: CGRect(origin: self.frame.origin, size: CGSize(width: 100, height: 15)))
            label.text = _stdlib_getDemangledTypeName(self)
            label.font = UIFont(name: "Courier", size: 9)
            self.addSubview(label)
        }
    }
}

然后我尝试通过在debugLabel上设置^UIView来测试上面的代码片段。那没有效果。但是,单独设置myView.debugLabel = true确实有效。不幸的是,我不能指望开发人员在每个视图上手动设置此属性。我想要一些符合Classy逻辑的东西,在那里我可以为视图中的所有元素(以及稍后可能出现的那些元素)切换标签。

我基本上要做的是修改所有UIViews的初始化逻辑,以便在不必子类的情况下做额外的事情(因为我希望从UIView继承的所有现有元素自动执行此逻辑)。 / strong>有人可以帮帮我吗?有没有理智的方法来实现这一目标?我想到的一种方法是进入根视图控制器,然后递归遍历其所有子视图,但是它不会处理当前未显示的视图,比如Classy。如果需要,我不介意切换到Obj-C,但是,我宁愿将其余的逻辑保留在Swift中。

1 个答案:

答案 0 :(得分:0)

据我所知,只有2个有用的UIView初始化,所以你可以使用convenience init轻松实现你想要的东西。

class FancyView : UIView {
    var foo = 0
}

extension UIView {
    convenience init(frame : CGRect?, addDebugThings: Bool) {

    if frame != nil {
        self.init(frame: frame!)
    } else {
        self.init()
    }

    if addDebugThings {
        addLabel()
    }

}

private func addLabel() {
    println("add label")
}
}

let view = UIView(frame: nil, addDebugThings: true)
let fancy = FancyView(frame: CGRectMake(0.0, 35.0, 350.0, 350.0), addDebugThings: false)

<击>

修改 当你指出我不能使用UIView子类时,我可以建议的另一种方法是方法调配,但不幸的是,这需要迁移到Obj-C。

这是一个如何轻松做到这一点的例子。

<强>的UIView + Border.h:

#import <Foundation/Foundation.h>
@interface UIView(Border)
@end

<强>的UIView + Border.m:

#import "UIView+Border.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

@implementation UIView(Border)

- (id)swizzled_initWithFrame:(CGRect)frame
{
    // You might think it is an endless recursion but it is not.
    id result = [self swizzled_initWithFrame:frame];

    // Safe guard: do we have an UIView (or something that has a layer)?
    if ([result respondsToSelector:@selector(layer)]) {
        // Get layer for this view.
        CALayer *layer = [result layer];
        // Set border on layer.
        layer.borderWidth = 2;
        layer.borderColor = [[UIColor redColor] CGColor];
    }

    // Return the modified view.
    return result;
}

- (id)swizzled_initWithCoder:(NSCoder *)aDecoder
{
    // You might think it is an endless recursion but it is not.
    id result = [self swizzled_initWithCoder:aDecoder];

    // Safe guard: do we have an UIView (or something that has a layer)?
    if ([result respondsToSelector:@selector(layer)]) {
        // Get layer for this view.
        CALayer *layer = [result layer];
        // Set border on layer.
        layer.borderWidth = 2;
        layer.borderColor = [[UIColor blueColor] CGColor];
    }

    // Return the modified view.
    return result;
}

+ (void)load
{
    // The "+ load" method is called once, very early in the application life-cycle.
    // It's called even before the "main" function is called. Beware: there's no
    // autorelease pool at this point, so avoid Objective-C calls.
    Method original, swizzle;

    // Get the "- (id)initWithFrame:" method.
    original = class_getInstanceMethod(self, @selector(initWithFrame:));
    // Get the "- (id)swizzled_initWithFrame:" method.
    swizzle = class_getInstanceMethod(self, @selector(swizzled_initWithFrame:));
    // Swap their implementations.
    method_exchangeImplementations(original, swizzle);

    // Get the "- (id)initWithCoder:" method.
    original = class_getInstanceMethod(self, @selector(initWithCoder:));
    // Get the "- (id)swizzled_initWithCoder:" method.
    swizzle = class_getInstanceMethod(self, @selector(swizzled_initWithCoder:));
    // Swap their implementations.
    method_exchangeImplementations(original, swizzle);
}

@end