不知不覺團隊已經有了4個iOS開發,你們的代碼風格徹底不同,因此每次改起別人的代碼就頭疼,理解起來不是那麼順暢,如鯁在喉。因此,就開了場分享會,把一些基本調用方法和代碼風格統一了一下。html
主要參考了:
view層的組織和調用方案
更輕量的View Controllers
整潔的Table View代碼
由於每一個人的風格不同,有些地方很難定義哪一個好那個壞,可是一樣的風格很重要,對團隊有很大的好處。這些博客都詳細介紹了這樣作的緣由,我這裏就把他們的精髓吸收了,加了些本身的想法,就把格式直接定下來了。ios
在一個VC或者View裏,要麼全用Masonry,要麼全用frame。這個要統一,看起來很美觀。
storyboard絕對不用,主要是純代碼結合xib。git
有些人說storyboard是將來,是apple力推的。可是它不只效率低,conflict還多。咱們曾經分紅不少不少小的storyboard減小conflict,可是最後作iPad版本時,整個佈局變掉了,相似QQ空間的風格,它的複用性真的差,最後索性所有純代碼寫,而後重作iOS版,幾天就搞定了。因此只後就完全拋棄了storyboard。github
儘可能不經過繼承,這也是設計模式中最常說的多用組合少用繼承。
不少狀況可使用category或者delegate來實現。
還有就是AOP,它須要一個攔截器,Mehtod Swizzling是個很好的手段。Aspects是個開源的庫,利用Mehtod Swizzling實現攔截的功能。
這樣不少功能能夠統一處理,代碼的侵入性很小。好比打點,自定義導航欄,導航欄回退按鈕,cell的箭頭的統一的設置等。數據庫
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; // 若是 swizzling 的是類方法, 採用以下的方式: // Class class = object_getClass((id)self); // ... // Method originalMethod = class_getClassMethod(class, originalSelector); // Method swizzledMethod = class_getClassMethod(class, swizzledSelector); SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(swizzling_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(originalMethod, swizzledMethod); }); } #pragma mark - Method Swizzling - (void)swizzling_viewWillAppear:(BOOL)animated { [self swizzling_viewWillAppear:animated]; if (self.navigationController.viewControllers.count > 1) { UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; backButton.frame = CGRectMake(0, 0, 44, 44); [backButton setTitle:@"" forState:UIControlStateNormal]; [backButton setImage:[UIImage imageNamed:@"back_black_icon"] forState:UIControlStateNormal]; [backButton setImageEdgeInsets:UIEdgeInsetsMake(0, -22, 0, 0)]; [backButton addTarget:self action:@selector(backEvent) forControlEvents:UIControlEventTouchUpInside]; UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)]; [leftView addSubview:backButton]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftView]; } }
全部的這些選擇,其實就是爲了給ViewController減負。難點就是怎麼去拆分。通俗點講就是ViewController代碼行數不多,拆分出來的部分能複用,而且邏輯清晰。設計模式
viewController的做用就是數據請求,處理數據,顯示在View上。數組
數據請求是指從服務端或者本地文件,數據庫取數據,VC不須要知道從哪裏取,只須要數據,咱們的作法統一是:mvc
ViewController.m - (void)configData { [CTPlanDataManager configPlanJsonDataWithPlanId:planId success:^(NSDictionary *dict) { } failure:^(NSError *error) { }]; } - (void)viewDidLoad { [super viewDidLoad]; [self configData]; } CTPlanDataManager.m - (void)configPlanJsonDataWithPlanId:(NSUInteger) planId success:(RequestOSSSuccessDictBlock) success failure:(RequestOSSFailureBlock) failure { if ([self planJsonFileExistsWithPlanId:planId]) { //判斷本地有沒有 NSDictionary *dict = [self readPlanJsonFromFileWithPlanId:planId]; if (success) { success(dict); } } else { [self downloadPlanJsonFileWithPlanId:planId progress:nil success:^(NSDictionary *dict) { //從阿里雲上取 if (success) { success(dict); } } failure:^(NSError *error) { if (failure) { failure(error); } }]; } }
處理數據的邏輯所有放在model裏,經過model直接獲取須要展示的數據。app
model.h @property (nonatomic, strong) NSArray<NSString *> *serviceArray; //從服務端獲取的 @property (nonatomic, strong) NSArray< NSString *> *handleArray; //model處理過的 model.m - (void)setServiceArray:(NSArray *) serviceArray { _serviceArray = serviceArray; NSMutableArray< NSString *> *handleArray = [[NSMutableArray alloc] init]; for(NSString *value in _serviceArray) { //一些邏輯處理 handleValue = [value doSomething]; [handleArray addObject:handleValue]; } _handleArray = handleArray; }
把處理後的數據顯示在View上,這個比較容易,主要就是自定義View,只留出初始化方法和賦值方法。
主要須要注意的地方賦值的時候要分離model和view,能夠用category來實現賦值函數。mvvm
@implementation CTHeaderView (ConfigureForInfor) - (void)configureForInfor:(CTInfor *) myInfor { self.nameTitleLabel.text = myInfor.name; NSString* date = [self.dateFormatter stringFromDate: myInfor.birthday]; self.dateLabel.text = date; ...... } @end
這兩個View是最經常使用的比較重的View。比較複雜的UI通常都用到他們。這個時候cell比較多,viewController比較臃腫,因此必須規範。
//點擊的反饋 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { ..... self.selectedBackgroundView = self.selectView; } //高亮狀態的行爲 - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; if (highlighted) { ...... } else { ...... } }
typedef NS_ENUM(NSUInteger, ProgressCellTag) { ProgressDateCellTag = kMinTag, ProgressBlankCellTag, ProgressTrainNoticeCellTag, ProgressTimeNoticeCellTag, ProgressActionCellTag, }; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { switch (self.dataSource[indexPath.row].integerValue) { case ProgressActionCellTag: return [self tableView:tableView actionCellForRowAtIndexPath:indexPath]; break; case ProgressDateCellTag: return [self tableView:tableView dateCellForRowAtIndexPath:indexPath]; break; case ProgressTimeNoticeCellTag: return [self tableView:tableView timeNoticeCellForRowAtIndexPath:indexPath]; break; case ProgressTrainNoticeCellTag: return [self tableView:tableView trainNoticeCellForRowAtIndexPath:indexPath]; break; case ProgressBlankCellTag: return [self tableView:tableView blankCellForRowAtIndexPath:indexPath]; break; default: break; } return nil; } #pragma mark - Cell Getter - (UITableViewCell *)tableView:(UITableView *)tableView actionCellForRowAtIndexPath:(NSIndexPath *)indexPath { // } - (UITableViewCell *)tableView:(UITableView *)tableView dateCellForRowAtIndexPath:(NSIndexPath *)indexPath { // } - (UITableViewCell *)tableView:(UITableView *)tableView timeNoticeCellForRowAtIndexPath:(NSIndexPath *)indexPath { // } - (UITableViewCell *)tableView:(UITableView *)tableView trainNoticeCellForRowAtIndexPath:(NSIndexPath *)indexPath { // } - (UITableViewCell *)tableView:(UITableView *)tableView blankCellForRowAtIndexPath:(NSIndexPath *)indexPath { // }
統一的風格和方式,使咱們的邏輯更加清晰。尤爲是改別人的代碼時,定位問題很是快,只須要理解他的處理邏輯,基本上就是改本身的代碼。