iOS團隊風格的統一

来源:http://www.cnblogs.com/stevenfukua/archive/2016/11/24/6097601.html
-Advertisement-
Play Games

不知不覺團隊已經有了4個iOS開發,大家的代碼風格完全不一樣,所以每次改起別人的代碼就頭疼,理解起來不是那麼順暢,如鯁在喉。所以,就開了場分享會,把一些基本調用方法和代碼風格統一了一下。 前言 主要參考了: "view層的組織和調用方案" "更輕量的View Controllers" "整潔的Tab ...


不知不覺團隊已經有了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 {
    //
}

總結

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ...
  • 通過js實現DropDownList下拉框可以直接輸入信息也可以直接選擇 ...
  • 在Android開發中,我們經常會需要在Android界面上彈出一些對話框,比如詢問用戶或者讓用戶選擇。這些功能我們叫它Android Dialog對話框,AlertDialog實現方法為建造者模式。下麵我們簡單模擬一個挑媳婦的選擇確定對話框(單選)對話框,不同於示例二之處在於本次只要不確定就可以後 ...
  • 首先打開終端(在搜索裡面搜索Te即可出來) 然後輸入 cd /Library/Java/Home/bin/ 然後這步很關鍵,由於我們用的是當前用戶,所以沒有最高許可權,不能在Library文件夾下生成任何文件,所以照抄網上的方法是無法創建成功的,複製粘貼步驟4的內容。 keytool -genkey ...
  • 在Android開發中,我們經常會需要在Android界面上彈出一些對話框,比如詢問用戶或者讓用戶選擇。這些功能我們叫它Android Dialog對話框,AlertDialog實現方法為建造者模式。下麵我們簡單模擬一個選花魁的簡單普通選項(單選)對話框,如下圖: Layout界面代碼: Java功 ...
  • 一、ListView的理解 1.什麼ListView? 一種用來顯示多個可滑動項(Item)列表的的ViewGroup 需要使用Adapter將集合數據和每一個Item所對應的佈局動態適配到ListView中顯示 顯示列表: listView.setAdapter(adapter) 更新列表: ad ...
  • 前言 學習本系列內容需要具備一定 HTML 開發基礎,沒有基礎的朋友可以先轉至 "HTML快速入門(一)" 學習 本人接觸 React Native 時間並不是特別長,所以對其中的內容和性質瞭解可能會有所偏差,在學習中如果有錯會及時修改內容,也歡迎萬能的朋友們批評指出,謝謝 文章第一版出自簡書,如果 ...
  •   現在直播越來越火,儼然已經成為了下一個紅海。作為一個資深碼農(我只喜歡這樣稱呼自己,不喜歡別人這樣稱呼我),我必須趕上時代的潮流,開始研究視頻直播。發現視屏直播類的文章上來就講拉流、推流、採集、美顏等第三方類的使用,我是小白很不懂啊。所以我決定開始系統分享從音頻、視頻類到視頻 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...