最近在工作中,我的任务是组建一个应用程序,以编程方式而不是通过故事板创建所有视图。除了在运行应用程序和调整视图之间更多的来回之外,这在大多数情况下都运行良好。作为一个有趣的项目,我想建立一个" 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中。
答案 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