原文作者是开发界中知晓度相当高的 Mugunth Kumar,他是 MKNetworkKit 的作者(虽然没有 AFNetworking 使用那么广泛,但也是一个很棒的 Network Kit),更是最近流传甚广的《iOS 5/6 Programming - Pushing The Limits》的作者。
文章中 MK 介绍了几点开发中常用的小技巧,几条 Tips 简单易懂,但是很实用,不但可以提高开发效率,而且可以提高代码的可读性和可复用性。
Types in Objective-C 和 Naming Conventions 两个章节介绍性内容较多,下面从 Subclassing 开始简单直译一下,第一次翻译,有诸多不到位的地方,各位多包涵。
Subclassing 继承/子类
大多语言允许开发者子类化框架所提供的类,但是在 Objective-C 中不完全是这样。大部分常用的类,诸如 NSArray、NSSet、NSDictionary 基本上都是集合类型的。不建议继承这些类,除非你准备转发调用或者实现所有必要的原始方法。
在传统的开发语言中,通常会通过继承基础类型(类似 NSArray 的类)来新增方法,重载已有的方法,或是自定义 UI 组件的外观。在 Objective-C 中,一般通过 Category 来扩展新方法。通过混合方法(swizzling the method?)来重载 SDK 提供的实现。以及外观相关的代理协议(Protocol)来定制 UI 组件的外观。
虽说如此,还是有一些类是经常会继承它们的,比如 UIViewController、
UITableViewController、UIControl 等。继承 UIViewController 大概是开发过程中最棒的一件事,因为它使得添加常见的功能变得异常简单。在我开发的每个 App 中,会有一个继承自 UIViewController 的子类,它实现了一组常用的方法。所有其他的 View Controllers 则都继承自这个基础类。
(译者注:Web 开发中也常会有一个用于被继承的 BaseController 来提供公共方法,看来开发是触类旁通的,要多思考)
所以,以下继承方法:
1.
@interface MyAppFeaturedYouTubeVideosViewController : UIViewController
应该替换成:
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
1. 2.
@interface MyAppFeaturedYouTubeVideosFeaturedViewController : MyAppViewController
@interface MyAppViewController : UIViewController
这个公用基础类可以在后续开发过程中用来添加公用的方法。在这个基础父类中,我通常会申明以下方法:
1. 2. 3. 4. 5. 6.
-(UIView*) errorView; -(UIView*) loadingView;
-(void) showLoadingAnimated:(BOOL) animated; -(void) hideLoadingViewAnimated:(BOOL) animated; -(void) showErrorViewAnimated:(BOOL) animated; -(void) hideErrorViewAnimated:(BOOL) animated;
实现如下:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30.
-(UIView*) errorView {
return nil; }
-(UIView*) loadingView {
return nil; }
-(void) showLoadingAnimated:(BOOL) animated {
UIView *loadingView = [self loadingView]; loadingView.alpha = 0.0f;
[self.view addSubview:loadingView];
[self.view bringSubviewToFront:loadingView];
double duration = animated ? 0.4f:0.0f;
[UIView animateWithDuration:duration animations:^{ loadingView.alpha = 1.0f; }]; }
-(void) hideLoadingViewAnimated:(BOOL) animated {
UIView *loadingView = [self loadingView];
double duration = animated ? 0.4f:0.0f;
[UIView animateWithDuration:duration animations:^{ loadingView.alpha = 0.0f;
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59.
} completion:^(BOOL finished) { [loadingView removeFromSuperview]; }]; }
-(void) showErrorViewAnimated:(BOOL) animated {
UIView *errorView = [self errorView]; errorView.alpha = 0.0f;
[self.view addSubview:errorView];
[self.view bringSubviewToFront:errorView];
double duration = animated ? 0.4f:0.0f;
[UIView animateWithDuration:duration animations:^{ errorView.alpha = 1.0f; }]; }
-(void) hideErrorViewAnimated:(BOOL) animated {
UIView *errorView = [self errorView];
double duration = animated ? 0.4f:0.0f;
[UIView animateWithDuration:duration animations:^{ errorView.alpha = 0.0f; } completion:^(BOOL finished) { [errorView removeFromSuperview]; }]; }
现在,App 中的每个 View Controller 中,可以很方便的通过调用以上方法来改变当前 View 的状态为 Loading 或者 Error。而且,View Controller 可以通过重载 -errorView 和 -loadingView 方法来提供自定义错误界面和 Loading 界面。
你还可以通过重载这个基础类中的 -viewDidLoad 来统一修改所有 View 的表现。比如为所有的 View 添加相同的背景图片或背景色:
1. 2. 3. 4.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor appOffWhiteColor]; // changes all my views to \"off-white\"
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
5.
}
UI Customization 自定义 UI
自定义 UI 可以大致分成两类,一是自定义控件,二是皮肤/主题。前者可以让 App 更出色,而后者是大部分 App 都需要的。我建议给 UIFont 和 UIColor 写 Category 扩展来提供自定义字体和自定义颜色。
例如,给 UIFont 添加如下方法:
1. 2. 3. 4. 5. 6. 7. 8. 9.
+(UIFont*) appFontOfSize:(CGFloat) pointSize {
return [UIFont fontWithName:@\"MyriadPro-Regular\" size:pointSize]; }
+(UIFont*) boldAppFontOfSize:(CGFloat) pointSize {
return [UIFont fontWithName:@\"MyriadPro-Black\" size:pointSize]; }
你就可以很方便地使用 [UIFont appFontOfSize:13] 得到 MyriadPro-Regular 字体。这样当你的设计需求变更时,就可以很快速的更换整个 App 中的字体。
相同的设计模式也可以应用到自定义颜色中。给 UIColor 添加以下方法:
1. 2. 3. 4. 5.
#define GREY(color) [UIColor colorWithRed:color/255.0 green:color/255.0 blue:color/255.0 alpha:1]
+(UIColor*) appBackgroundColor {
return [UIColor colorWithPatternImage:[UIImage imageNamed:@\"BGPattern\"]]; }
+(UIColor*) appBlack1Color {
return GREY(38); }
+(UIColor*) appOffWhiteColor {
return GREY(234); }
6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16.
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
所以,千万不要用 Interface Builder 来选颜色。 Subclassing UILabels 继承 UILabel
还有一个小窍门,当开发者继承 UILabel、UITextField 和 UITextView 时,通常在 -initWithFrame: 和 -initWithCoder: 方法中设置字体和颜色,参见以下代码: 1. @implementation AppPrefixLabel 2.
3. -(void) setup { 4.
5.
self.font = [UIFont fontWithName:@\"SourceSansPro-Semibold\" size:self.font.pointSize];
self.textColor = [UIColor redColor]; }
-(id) initWithFrame:(CGRect)frame {
if((self = [super initWithFrame:frame])) {
[self setup]; }
return self; }
-(id) initWithCoder:(NSCoder *)aDecoder {
if((self = [super initWithCoder:aDecoder])) {
[self setup]; }
return self; } @end
6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28.
这个技巧使得开发者可以在 Interface Builder 中自定义这些元素的外观。在 IB 中拖入一个 UILabel,并且修改它的类为你自定义的类,瞬间就完成了这个 Label 字体和颜色的自定义,不用任何多余的代码。
这个技巧多数情况下相当管用,但是当你的 App 支持自定义主题,且用户可以通过设置界面更换主题时,就会显得有些麻烦。
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
-initWithFrame: 和 initWithCoder: 会在 UI 组件创建的时候被调用,所以在这之后如果要改变字体和颜色,就需要很多额外的代码。因此,如果你的 App 支持主题,写一个主题管理器的全局单例来提供全局的主题、字体、颜色。
如果你用到了我说的第一个方法,你的 UIFont 的 Category 现在可以这样实现了: 1. +(UIFont*) appFontOfSize:(CGFloat) pointSize { 2.
3. 4. 5.
NSString *currentFontName = [[ThemeProvider sharedInstance] currentFontName];
return [UIFont fontWithName:currentFontName size:pointSize]; }
UIColor 同理。其实没有正确或错误的方法,上述方法都是可行的。
遵从这里提到的设计模式,可以让你的代码干净得像写的很漂亮的 JS/CSS。试着在你的下一个项目中用这些方法吧。
Allen 后记
之前在想 iOS 开发到底是否需要一个类似 Web 开发中的所谓的框架,但渐渐发现其实 iOS SDK 本就是一个高度封装了的框架了,可能我们需要的不是更更高层的框架,而是一种好的设计模式、开发习惯和代码结构。因此是不是可以从一个 Project 的层面出发,写一个干净的框架,并且定义一些规范,就是一个很好的“框架”了?而不是非得提供 Router 之类的往 Web 开发框架去靠。
北大青鸟中关村软件学院
地址:北京市海淀区中关村大街49号大华科技商厦C座3层
因篇幅问题不能全部显示,请点此查看更多更全内容