iOS團隊風格的統一

不知不覺團隊已經有了4個iOS開發,你們的代碼風格徹底不同,因此每次改起別人的代碼就頭疼,理解起來不是那麼順暢,如鯁在喉。因此,就開了場分享會,把一些基本調用方法和代碼風格統一了一下。html

前言

主要參考了:
view層的組織和調用方案
更輕量的View Controllers
整潔的Table View代碼
由於每一個人的風格不同,有些地方很難定義哪一個好那個壞,可是一樣的風格很重要,對團隊有很大的好處。這些博客都詳細介紹了這樣作的緣由,我這裏就把他們的精髓吸收了,加了些本身的想法,就把格式直接定下來了。ios

ViewController代碼結構

  • 全部的屬性都使用Lazy Init,而且放在最後。這樣既美觀,對於數組之類的屬性也避免了崩潰
  • viewDidLoad:addSubview,configData,這樣會很美觀
  • viewWillAppear:佈局,佈局這個時候設好處不少,好比咱們iPad版相似qq空間,一個VC容器裏放兩個,frame在WillAppear時在肯定,這樣複用到iPhone版本就不用修改什麼。
    設置Nav,TabBar是否隱藏,Status顏色。在WillDisAppear在設回原來的狀態,這樣就不會影響別人的VC。
  • ViewDidAppear:添加Notification監聽,在DidDisappear裏remove掉。
  • 每個delegate都把對應的protocol名字帶上,delegate方法不要處處亂寫,寫到一塊區域裏面去
  • event response專門開一個代碼區域,全部button、gestureRecognizer的響應事件都放在這個區域裏面,不要處處亂放
  • private/public methods,private methods儘可能不要寫,可能之後別的地方會用到,作一個模塊或者category。

view的佈局和寫法

在一個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];
    }
}

MVC,MVVM,胖Model,瘦Model

全部的這些選擇,其實就是爲了給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

UITableview,UICollectionView

這兩個View是最經常使用的比較重的View。比較複雜的UI通常都用到他們。這個時候cell比較多,viewController比較臃腫,因此必須規範。

  • dataSource,delegate,UICollectionViewLayout等必須分離出去寫
  • 在cell內部控制cell的狀態。
//點擊的反饋
- (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 {
        ......
    }
}
  • 控制多個Cell類型的寫法風格
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 {
    //
}

總結

統一的風格和方式,使咱們的邏輯更加清晰。尤爲是改別人的代碼時,定位問題很是快,只須要理解他的處理邏輯,基本上就是改本身的代碼。

相關文章
相關標籤/搜索