Objective c 用于iOS的类似CSS的样式类

Objective c 用于iOS的类似CSS的样式类,objective-c,ios,cocoa-touch,styling,Objective C,Ios,Cocoa Touch,Styling,我目前正在为我的iOS应用程序的本机控件实现一个类似CSS的样式引擎,以避免从plist读取一大堆样式属性,并在每个控件上应用每一个样式属性 (编辑:不,我不想要UIWebView,我需要自定义本机控件。我不想实现纯CSS,只是看起来像CSS的东西,可以与简单CSS一起使用。) 假设我有这样一个plist结构: closeButtonStyle = "background:transparent;font:Georgia/14;textColor:#faa" titleLabelStyle =

我目前正在为我的iOS应用程序的本机控件实现一个类似CSS的样式引擎,以避免从plist读取一大堆样式属性,并在每个控件上应用每一个样式属性

(编辑:不,我不想要UIWebView,我需要自定义本机控件。我不想实现纯CSS,只是看起来像CSS的东西,可以与简单CSS一起使用。)

假设我有这样一个plist结构:

closeButtonStyle = "background:transparent;font:Georgia/14;textColor:#faa"
titleLabelStyle  = "background:transparent;font:Helvetica/12;textAlignment:left"
你可以很容易地想象我在这里面加入了什么样的属性

到目前为止,一切正常,我有一个
UIStyle
类,它解析这些声明并将所有找到的值存储在它的ivar中;我还有
ui视图
ui标签
ui按钮
。。。它只声明一个
-(void)setStyle:(UIStyle*)样式
方法。此方法仅当样式变量已定义时才应用它们

正如我所说的,一切都正常

我唯一的问题是关于样式字符串的解析。我选择使用NSScanner,但我不确定这是否是最好的选择,我想听听你的意见

下面是我如何实现我的
UIStyle

--UIStyle.h

typedef struct {
    BOOL frame:1;
    BOOL font:1;
    BOOL textColor:1;
    BOOL backgroundColor:1;
    BOOL shadowColor:1;
    BOOL shadowOffset:1;
    BOOL textAlignment:1;
    BOOL titleEdgeInsets:1;
    BOOL numberOfLines:1;
    BOOL lineBreakMode:1;
} UIStyleFlags;

@interface UIStyle: NSObject {
    UIStyleFlags         _has;
    CGRect               _frame;
    UIFont              *_font;
    UIColor             *_textColor;
    UIColor             *_backgroundColor;
    UIColor             *_shadowColor;
    CGSize               _shadowOffset;
    UITextAlignment      _textAlignment;
    UIEdgeInsets         _titleEdgeInsets;
    NSInteger            _numberOfLines;
    UILineBreakMode      _lineBreakMode;
}

@property (readonly, nonatomic) UIStyleFlags         has;
@property (readonly, nonatomic) CGRect               frame;
@property (readonly, nonatomic) UIFont              *font;
@property (readonly, nonatomic) UIColor             *textColor;
@property (readonly, nonatomic) UIColor             *backgroundColor;
@property (readonly, nonatomic) UIColor             *shadowColor;
@property (readonly, nonatomic) CGSize               shadowOffset;
@property (readonly, nonatomic) UITextAlignment      textAlignment;
@property (readonly, nonatomic) UIEdgeInsets         titleEdgeInsets;
@property (readonly, nonatomic) NSInteger            numberOfLines;
@property (readonly, nonatomic) UILineBreakMode      lineBreakMode;

- (id)initWithString:(NSString *)string;
+ (id)styleWithString:(NSString *)string;
+ (id)styleInDict:(NSDictionary *)dict key:(NSString *)key;

@end

@interface UIView (UIStyle)
- (void)setStyle:(UIStyle *)style;
@end

@interface UILabel (UIStyle)
- (void)setStyle:(UIStyle *)style;
@end

@interface UIButton (UIStyle)
- (void)setStyle:(UIStyle *)style;
@end
--UIStyle.m

#import "UIStyle.h"

@implementation UIStyle

@synthesize has               = _has;
@synthesize frame             = _frame;
@synthesize font              = _font;
@synthesize textColor         = _textColor;
@synthesize backgroundColor   = _backgroundColor;
@synthesize shadowColor       = _shadowColor;
@synthesize shadowOffset      = _shadowOffset;
@synthesize textAlignment     = _textAlignment;
@synthesize titleEdgeInsets   = _titleEdgeInsets;
@synthesize numberOfLines     = _numberOfLines;
@synthesize lineBreakMode     = _lineBreakMode;

- (id)initWithString:(NSString *)string {
    if ((self = [super init])) {
        _has.frame           = NO;
        _has.font            = NO;
        _has.textColor       = NO;
        _has.backgroundColor = NO;
        _has.shadowColor     = NO;
        _has.shadowOffset    = NO;
        _has.textAlignment   = NO;
        _has.titleEdgeInsets = NO;
        _has.numberOfLines   = NO;
        _has.lineBreakMode   = NO;

        _frame           = CGRectZero;
        _font            = nil;
        _textColor       = nil;
        _backgroundColor = nil;
        _shadowColor     = nil;
        _shadowOffset    = CGSizeZero;
        _textAlignment   = UITextAlignmentLeft;
        _titleEdgeInsets = UIEdgeInsetsZero;
        _numberOfLines   = 1;
        _lineBreakMode   = UILineBreakModeClip;

        NSScanner *scanner = [[NSScanner alloc] initWithString:string];
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

        NSCharacterSet *keyEndSet = [NSCharacterSet characterSetWithCharactersInString:@":"];
        NSCharacterSet *valueEndSet = [NSCharacterSet characterSetWithCharactersInString:@";"];

        while (![scanner isAtEnd]) {
            NSString *key;
            NSString *value;

            [scanner scanUpToCharactersFromSet:keyEndSet intoString:&key];
            [scanner scanCharactersFromSet:keyEndSet intoString:NULL];
            [scanner scanUpToCharactersFromSet:valueEndSet intoString:&value];
            [scanner scanCharactersFromSet:valueEndSet intoString:NULL];

            [dict setValue:value forKey:key];
        }
        [scanner release];

        for (NSString *key in dict) {
            NSString *value = (NSString *)[dict objectForKey:key];

            if ([key isEqualToString:@"frame"]) {
                _frame = CGRectFromString(value);
                _has.frame = YES;
            }

            else if ([key isEqualToString:@"font"]) {
                NSArray *font = [value componentsSeparatedByString:@"/"];
                NSString *fontName = (NSString *)[font objectAtIndex:0];
                CGFloat fontSize = (CGFloat)[(NSString *)[font objectAtIndex:1] floatValue];

                _font = [[UIFont fontWithName:fontName size:fontSize] retain];
                _has.font = YES;
            }

            else if ([key isEqualToString:@"textColor"]) {
                _textColor = [[UIColor colorWithString:value] retain];
                _has.textColor = YES;
            }

            else if ([key isEqualToString:@"backgroundColor"]) {
                _backgroundColor = [[UIColor colorWithString:value] retain];
            }

            else if ([key isEqualToString:@"shadow"]) {
                NSArray *shadow = [value componentsSeparatedByString:@"/"];
                _shadowColor = [[UIColor colorWithString:(NSString *)[shadow objectAtIndex:0]] retain];
                _shadowOffset = CGSizeMake((CGFloat)[(NSString *)[shadow objectAtIndex:1] floatValue], (CGFloat)[(NSString *)[shadow objectAtIndex:2] floatValue]);
                _has.shadowColor = YES;
                _has.shadowOffset = YES;
            }

            else if ([key isEqualToString:@"textAlignment"]) {
                if ([value isEqualToString:@"center"]) {
                    _textAlignment = UITextAlignmentCenter;
                }
                else if ([value isEqualToString:@"right"]) {
                    _textAlignment = UITextAlignmentRight;
                }
                else {
                    _textAlignment = UITextAlignmentLeft;
                }
                _has.textAlignment = YES;
            }

            else if ([key isEqualToString:@"titleEdgeInsets"]) {
                _titleEdgeInsets = UIEdgeInsetsFromString(value);
                _has.titleEdgeInsets = YES;
            }

            else if ([key isEqualToString:@"numberOfLines"]) {
                _numberOfLines = (NSInteger)[value integerValue];
                _has.numberOfLines = YES;
            }

            else if ([key isEqualToString:@"lineBreakMode"]) {
                if ([value isEqualToString:@"character"]) {
                    _lineBreakMode = UILineBreakModeCharacterWrap;
                }
                else if ([value isEqualToString:@"clip"]) {
                    _lineBreakMode = UILineBreakModeClip;
                }
                else if ([value isEqualToString:@"head"]) {
                    _lineBreakMode = UILineBreakModeHeadTruncation;
                }
                else if ([value isEqualToString:@"tail"]) {
                    _lineBreakMode = UILineBreakModeTailTruncation;
                }
                else if ([value isEqualToString:@"middle"]) {
                    _lineBreakMode = UILineBreakModeMiddleTruncation;
                }
                else {
                    _lineBreakMode = UILineBreakModeWordWrap;
                }
                _has.lineBreakMode = YES;
            }
        }

        [dict release];
    }
    return self;
}

- (void)dealloc {
    [_font            release];
    [_textColor       release];
    [_backgroundColor release];
    [_shadowColor     release];
    [super dealloc];
}

+ (id)styleWithString:(NSString *)string {
    return [[[UIStyle alloc] initWithString:string] autorelease];
}

+ (id)styleInDict:(NSDictionary *)dict key:(NSString *)key {
    return [[[UIStyle alloc] initWithString:(NSString *)[dict objectForKey:key]] autorelease];
}

@end


@implementation UIView (UIStyle)
- (void)setStyle:(UIStyle *)style {
    if (style.has.frame) {
        [self setFrame:style.frame];
    }

    if (style.has.backgroundColor) {
        [self setBackgroundColor:style.backgroundColor];
    }
}
@end

@implementation UILabel (UIStyle)
- (void)setStyle:(UIStyle *)style {
    [super setStyle:style];

    if (style.has.font)
        [self setFont:style.font];

    if (style.has.textColor)
        [self setTextColor:style.textColor];

    if (style.has.shadowColor)
        [self setShadowColor:style.shadowColor];

    if (style.has.shadowOffset)
        [self setShadowOffset:style.shadowOffset];

    if (style.has.textAlignment)
        [self setTextAlignment:style.textAlignment];

    if (style.has.numberOfLines)
        [self setNumberOfLines:style.numberOfLines];

    if (style.has.lineBreakMode)
        [self setLineBreakMode:style.lineBreakMode];
}
@end

@implementation UIButton (UIStyle)
- (void)setStyle:(UIStyle *)style {
    [super setStyle:style];

    if (style.has.titleEdgeInsets)
        [self setTitleEdgeInsets:style.titleEdgeInsets];
}
@end
这是最好的方式吗?特别是,我想听听您对代码扫描部分的意见(while(![scanner isattend])循环)。

您不能使用普通CSS吗


这难道不比重新发明轮子更简单吗?轮子可能很有趣,但你可能无法做(或支持)CSS能够做的很多事情&使用这种方法,你可能也无法利用iOS功能。。。您可能需要重新考虑您的方法。

由于这不是一种标记语言,我更愿意在其上使用。

不,我需要本机控件,这里不需要webview。我不想复制一个完整的CSS引擎。仅尝试减少自定义本机控件所需的行数,因为我的应用程序可以为我公司的每个客户端进行蒙皮。同样,我的问题不是“我应该使用它吗?”,而是“这是实现它的最佳方式吗?”哦,如果你需要本机控件,那么chuck CSS。使用iOS样式。非常好。你为什么不这么做?每个元素都有自定义其外观的方法。尝试利用这一点……我能够在plist中使用一行代码和一个字符串对控件进行蒙皮,而在plist中声明每个属性并读取它并应用我的应用程序的每个控件会产生几十行代码。如果字体在plist中声明,请阅读字体,然后应用字体。如果颜色是。。。每一个值都是如此。现在使用我的UIStyle,我必须阅读一行代码,比如
frame:{{19230},{282169};行数:0;lineBreakMode:wordwrap;字体:巴斯克维尔/16;文本对齐:居中;textColor:#a4a4a2;阴影:#000/0/1;背景色:透明
并立即应用。webview的问题是,与objective-c相比,它非常有限。有了这个项目,任何人都可以添加做不同事情的自定义规则,等等。你会在什么地方发表吗?我真的认为这比你用“正常”的方式定制你的视图所需要的工作量要大得多。另外,我认为你应该查看文档(需要iOS开发者程序帐户)。@vikingosegundo:嗯,也许吧。让我再填充一点,以包含以前所有UIKit控件的所有常用属性!不要称你的班级为UIStyle。由于缺少名称空间,Objective-C类使用前缀来限制冲突
NS
UI
是苹果的东西:你应该选择一个不同的前缀。还有一件事需要提及:按名称约定
-setStyle:
将是成员
样式
的setter/自定义属性实现。但您不是在设置对象,而是在配置许多样式属性。更好的名称应该是
-applyStyle:
-configureWithStyle:
。这不是标记。NSScanner和regex哪个更快、使用更少内存?我不知道,但有一个有趣的问题。我倾向于认为扫描仪更快。它只需扫描每个字符,直到在所需集合中找到一个为止。另一方面,正则表达式本身需要解析,因为它按设计数量级更复杂。我想,您希望在创建视图后执行样式设置。所以这不应该导致一个显著的更长的实例。所以你应该使用一种简单易懂的方式,一种让你感到舒适的方式,而不是基于几毫秒的时间,它会运行得更快。你猜对了。我通常尽可能避免笔尖,因此我在创建子视图时正确地应用了样式。我还考虑在启动时将所有样式加载到某个字典中,这是花费时间最多的。然后,重复使用多次的单个样式只需加载一次。我开始相信我这里有一个很好的项目!