视觉规范化指的是通过对各种UI组件的标准化定义来统一app中的所有控件。经历了一个中型规模app的迭代,我深深感受到了视觉规范化对开发人员的效率提升和app视觉还原度的提升。总而言之这是一件投入产出比极高的工作,但却很容易被忽视。
在客户端开发中,传统UI开发方式是设计师在视觉稿中标注所有的大小、颜色的值来表示一个控件或者view。颜色是由6个十六进制表示,例如#FDFDFD,大小由dp或者sp来表示。对于不同的状态下的控件(例如常规态、点击态、disable态)还需要分别标明不同状态下的各种色值。对于高斯模糊这种遮罩要标明的元素更多了,有颜色、透明度、模糊半径等等。
对于程序员,拿到视觉稿后,根据字体大小、字体颜色、不同点击态颜色等元素一一去设置font、textColor等值,繁琐且容易出错,等开发完成的时候设计师经常抱怨视觉还原度太低,深究原因经常是因为16进制颜色写错或者是懒一点的开发者懒得按照视觉稿来而是自己估计了色值或者字号大小。
鉴于上述的种种麻烦,视觉规范化随之被应用在客户端开发实践中。具体来说,就是通过编码来表示一个个控件或者view,而设计师在视觉稿中标注对应的编码值,开发者只需要设置编码值即可完成UI组件的外观定义。我们对如下元素做了规范化,
在前期开发中,只需要把设计师出的视觉编码表录入到代码中即可。举个简单的Label为例子,设计师会定义整个app使用的字体大小和色值,如下:
而视觉稿中的标注不在是以#FDFDFD和14sp这样的格式给出,而是以T1CG4这样的字符串代替。
而Button用B1这样的字符串表示,B1标识了这Button对应的文字大小、文字颜色以及三种状态下button对应的背景色。
对于页面级别的元素,遮罩用M1字符串表示,M1样式标识了遮罩对应的背景色、透明度和模糊半径。app中经常出现的各种错误页面和空页面,也有E1这样的样式来表示。
代码实现非常简单,没有采用配置文件,而是硬编码到代码中。举Label为例子,
@implementationUIFont(DRCStyle) + (UIFont*)drc_fontWithStyle:(NSString*)style { if([style isEqualToString:@"T0"]){ return[UIFontsystemFontOfSize:10]; } elseif([style isEqualToString:@"T1"]){ return[UIFontsystemFontOfSize:11]; } elseif([style isEqualToString:@"T2"]){ return[UIFontsystemFontOfSize:12]; } elseif([style isEqualToString:@"T3"]){ return[UIFontsystemFontOfSize:14]; } elseif([style isEqualToString:@"T4"]){ return[UIFontsystemFontOfSize:16]; } elseif([style isEqualToString:@"T5"]){ return[UIFontsystemFontOfSize:19]; } elseif([style isEqualToString:@"T6"]){ return[UIFontsystemFontOfSize:21]; } elseif([style isEqualToString:@"T7"]){ return[UIFontsystemFontOfSize:26]; } elseif([style isEqualToString:@"T8"]){ return[UIFontsystemFontOfSize:34]; } elseif([style isEqualToString:@"T9"]){ return[UIFontsystemFontOfSize:49]; } else{ DRCLog(@"error style of font"); return[UIFontsystemFontOfSize:10]; } } @implementationUIColor(DRCStyle) + (UIColor*)drc_colorWithStyle:(NSString*)style { if([style isEqualToString:@"CY0"]) { return[UIColorcolorWithHexString:@"FA8919"]; } elseif([style isEqualToString:@"CY1"]) { return[UIColorcolorWithHexString:@"FFA033"]; } elseif([style isEqualToString:@"CG0"]) { return[UIColorcolorWithHexString:@"FFFFFF"]; } elseif([style isEqualToString:@"CG1"]) { return[UIColorcolorWithHexString:@"FAFAFA"]; } elseif([style isEqualToString:@"CG2"]) { return[UIColorcolorWithHexString:@"F0F0F0"]; } elseif([style isEqualToString:@"CG3"]) { return[UIColorcolorWithHexString:@"EBEBEB"]; } elseif([style isEqualToString:@"CG4"]) { return[UIColorcolorWithHexString:@"E5E5E5"]; } elseif([style isEqualToString:@"CG5"]) { return[UIColorcolorWithHexString:@"CCCCCC"]; } elseif([style isEqualToString:@"CG6"]) { return[UIColorcolorWithHexString:@"ADADAD"]; } elseif([style isEqualToString:@"CG7"]) { return[UIColorcolorWithHexString:@"878787"]; } elseif([style isEqualToString:@"CG8"]) { return[UIColorcolorWithHexString:@"666666"]; } elseif([style isEqualToString:@"CG9"]) { return[UIColorcolorWithHexString:@"333333"]; } else{ return[UIColorcolorWithHexString:@"000000"]; } } @implementationUILabel(DRCStyle) - (void)drc_setStyle:(NSString*)style { NSString*fontStyle; NSString*colorStyle; NSRangerangeOfC = [style rangeOfString:@"C"]; if(rangeOfC.location ==NSNotFound) { return; } fontStyle = [style substringToIndex:rangeOfC.location]; colorStyle = [style substringFromIndex:rangeOfC.location]; self.font = [UIFontdrc_fontWithStyle:fontStyle]; self.textColor = [UIColordrc_colorWithStyle:colorStyle]; }
其他开发者只需要调用 [myLabel drc_setStyle:@"T1CG4"]
即可。
对于所有的组件,都采用category的方式来实现,目的是为了不改变现有控件的继承体系,更加灵活。而Button需要用到Method swizzle方法来设置三种状态下的颜色,建议多个业务团队组合成的大型app谨慎使用。
在实际实践中,视觉规范化的工作让我们解放了许多生产力,设计师对于还原度的抱怨也随之减少。但是远远还不够,如果一个做得好的app,我相信有更多的元素可以规范化,例如cell、alert等等。cell的规范化的争议也是最大的,一方认为cell没有必要规范化,另一方认为cell的规范化非常有必要。cell规范化的长远利益在于一般的展示型app都是由tableview组成,如果cell规范化做得好的话可以在客户端实现动态化,服务端返回的数据只需要一个样式的数组和对应的数据,客户端就可完成tableview的动态渲染。