iOS團隊風格的統一


不知不覺團隊已經有了4個iOS開發,大家的代碼風格完全不一樣,所以每次改起別人的代碼就頭疼,理解起來不是那么順暢,如鯁在喉。所以,就開了場分享會,把一些基本調用方法和代碼風格統一了一下。

前言

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

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。

有些人說storyboard是未來,是apple力推的。但是它不僅效率低,conflict還多。我們曾經分成很多很多小的storyboard減少conflict,但是最后做iPad版本時,整個布局變掉了,類似QQ空間的風格,它的復用性真的差,最后索性全部純代碼寫,然后重做iOS版,幾天就搞定了。所以只后就徹底拋棄了storyboard。

一些通用的邏輯或者頁面是否使用繼承來實現?

盡量不通過繼承,這也是設計模式中最常說的多用組合少用繼承。
很多情況可以使用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不需要知道從哪里取,只需要數據,我們的做法統一是:

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直接獲取需要展現的數據。

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來實現賦值函數。

@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 {
    //
}

總結

統一的風格和方式,使我們的邏輯更加清晰。尤其是改別人的代碼時,定位問題非常快,只需要理解他的處理邏輯,基本上就是改自己的代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM