iOS UIviewContoller瘦身大運動

  • 前言

通常一個項目中ViewController的代碼最多,復用度最低,隨著項目功能完善,一個臃腫的ViewContoller便出現了,如果這個時候來了新人接手項目!!!而你就是這個新人的話。Oh my god!看著可能多達上千行的代碼,相信我,你會有一種蛋蛋的憂傷。感覺無從下手...一種無力感油然而生,感覺身體被掏空=。=

被掏空

來來來小伙子,喝一瓶


好的你明天可以不用來上班了
  • 本例實現一個從網絡下載數據,顯示到tableView中的demo最終只需要在viewController中寫下代碼便可實現(可能不是最優的,如果您有更好的建議,歡迎討論,糾正我。):

 self.dataSouce = [PQTBDataSource dataSourceWith:_dataArray identifier:CELLIDENTIFIER cellConfigBlock:^(TableViewCell * _Nullable cell, id  _Nullable item) {
        [cell configCellWithIem:item];
    }];
    self.myTableView.dataSource = self.dataSouce;
    [self.myTableView registerNib:[UINib nibWithNibName:@"TableViewCell" bundle:nil] forCellReuseIdentifier:CELLIDENTIFIER];
    
    typeof(self) weakSelf = self;
    [NetWorkManager dataTaskWith:URL completionHandler:^(NSArray *itemsArray) {
        [weakSelf.dataSouce pq_updateWithArray:itemsArray];
        [weakSelf.myTableView reloadData];
    }];
  • 1 優化TableView的Datasource

. 先看優化前和優化之后的對比:
優化前

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _array.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    cell.textLabel.text = _array[indexPath.row];
    return cell;
}

優化后,且可復用

self.dataSouce = [PQTBDataSource dataSourceWith:_dataArray identifier:CELLIDENTIFIER cellConfigBlock:^(TableViewCell * _Nullable cell, id  _Nullable item) {
        //這里更新設置你的cell
    }];
    self.myTableView.dataSource = self.dataSouce;
  • 開始之前還扯一扯:在項目中如果你用到了兩個以上的tableView的時候你會發現你一定會存在重復的代碼,而且還是沒啥意義的。比如
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _array.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    cell.textLabel.text = _array[indexPath.row];
    
    return cell;
}

先不用看里面寫了啥!!!,基本雷同是不是?
你的tableview還會有

這里僅僅是開個玩笑哈 會報錯的。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return [@"fuck" integerValue];
}

對于上面的代碼,完全可以提取出來,讓你的ViewController看起來舒服點。
讓他減減肥...

開始干活了

.

  • 1 第一步,建一個類,并且實現datasource的方法。


    建一個類,名字你隨意,我干了
  • 2 第二步,繼承<UITableViewDataSource>
    既然把剛才上面列舉的方法提取出來,這里肯定是我們自己去實現了。
    先實現方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 在某組里面有多少個cell
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
   每個cell的樣式 還要從復用池取出cell需要Identifier
}

然后你就發現這里我們需要一個數組,所以這里需要

  • 一個數組

  • 一個identifier

在考慮一下有必要公開么?筆者認為是沒有必要公開的,所以就寫在.m文件中

//傳入之后不允許用戶在外面隨意更改數據
@property (nonatomic,strong,readwrite) NSMutableArray * _Nullable valuesArray;
@property (nonatomic,copy) NSString * identifier;

上面的代碼中你會發現有一個readwrite,這個是干啥的呢?主要是配合.h文件中的

@interface PQTBDataSource : NSObject <UITableViewDataSource>
//傳入之后不允許用戶在外面隨意更改數據
@property (nonatomic,strong,readonly) NSMutableArray * _Nullable valuesArray;

如果你還是不知道啥意思?看圖
![現在你只能讀,不能寫。但是又想在.m中使用self.valuesArray的話可以這樣寫](http://upload-images.jianshu.io/upload_images/1940927-db83b6518a22f1a6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
到目前為止,我們已經確定了兩個東西,一個是數組,一個是Identifier。
還有一個問題:
##### 這個時候每一個的cell要怎么設置,內部不知道,cell可以是很多種,自定義的xib畫的,類名都不一樣,里面完成不了對cell的設置。所以我們需要我們得交給外面去處理,這里你想使用delegate或者block隨便你,不過筆者比較喜歡block,代碼內聚。

到目前為止,我們需要的東西就知道了:
- 數組
- identifier
- 一個block
于是就可以寫一個類似于:

  • (nonnull instancetype)dataSourceWith:(nullable NSArray *)values identifier:(nullable NSString *)identifier cellConfigBlock:(nullable void(^)( id _Nullable cell,id _Nullable item))block;
  • (nonnull instancetype)initWithDataSource:(nullable NSArray *)values identifier:(nullable NSString *)identifier cellConfigBlock:(nullable void(^)(id _Nullable cell,id _Nullable item))block;
的方法。
到這里datasource基本上就設置好了,把方法都實現一下
  • (nonnull instancetype)dataSourceWith:(nullable NSArray *)values identifier:(nullable NSString *)identifier cellConfigBlock:(nullable void(^)( id _Nullable cell,id _Nullable item))block{
    return [[self alloc]initWithDataSource:values identifier:identifier cellConfigBlock:block];
    }
  • (nonnull instancetype)initWithDataSource:(nullable NSArray *)values identifier:(nullable NSString *)identifier cellConfigBlock:(nullable void(^)(id _Nullable cell,id _Nullable item))block
    {
    self = [super init];
    if (self) {
    self.identifier = identifier ;
    self.valuesArray = [NSMutableArray arrayWithArray:values];
    self.cellConfigBlock = [block copy];
    }
    return self;
    }

  • (id _Nullable )itemWithIndexPath:(NSIndexPath *)indexPath{
    return self.valuesArray[indexPath.row];
    }

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.valuesArray.count;
    }

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    id cell = [tableView dequeueReusableCellWithIdentifier:self.identifier forIndexPath:indexPath];
    id item = [self itemWithIndexPath:indexPath];
    self.cellConfigBlock(cell,item);
    return cell;
    }

于是你ViewController中的tableView就可以這樣子設置:

self.dataSouce = [PQTBDataSource dataSourceWith:_dataArray identifier:CELLIDENTIFIER cellConfigBlock:^(TableViewCell * _Nullable cell, id _Nullable item) {
//這里更新設置你的cell
}];
self.myTableView.dataSource = self.dataSouce;

這樣子你的ViewController就幾行代碼就實現了之前數十行要實現且沒有啥子意義的代碼,并且你還可以在其他的tableView中去使用。


 > - ### 2 封裝一下數據請求,這部分的代碼沒有viewController沒有必要去管理,他只需要一個結果就好了,

既然只需要一個結果,那么我們最終就放回一個結果(不管請求失敗還是成功都只是一個結果)。但是我們并不知道什么時候會請求完成或者失敗,不能一直等待,所以一定是異步進行請求,請求完成就通過block回調。
- 2.1 分析一下需要什么才可以請求一個數據
 -URL
這里我們不考慮太復雜的情況了,隨便請求一點數據好了。
所以可以封裝一個方法大致如下:

  • (void)dataTaskWith:(NSString *)url completionHandler:(void(^)(NSArray * itemsArray))block;
這里可能你會注意到,為什么我返回的是一個數組:是這樣子的,viewController同樣不需要知道我們數據轉化為模型的過程,一樣的理念,他只需要結果,一個有用的結果就行了。
實現代碼
  • (void)dataTaskWith:(NSString *)url completionHandler:(void(^)(NSArray * itemsArray))block{
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

    NSURL *URL = [NSURL URLWithString:url];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];

    NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    NSDictionary * dict = (NSDictionary *)responseObject;
    NSMutableArray * array = [NSMutableArray array];
    NSArray * objects = dict[@"subjects"];
    for (NSDictionary * dict in objects) {
    [array addObject:[TableViewModel tableViewModelWith:dict]];
    }
    if (block) {
    block(array);
    }
    }];
    [dataTask resume];
    }


在這里我們同時還生成了一個Model,最后我們只需要把模型返回就好。
模型
  • (instancetype)tableViewModelWith:(NSDictionary *)dict;
實現
  • (instancetype)tableViewModelWith:(NSDictionary *)dict{
    TableViewModel * model = [[self alloc]init];
    [model setValuesForKeysWithDictionary:dict];
    return model;
    }
  • (void)setValue:(id)value forUndefinedKey:(NSString *)key{

    if ([key isEqualToString:@"id"] == YES) {
    _ID = value;
    }
    }

  • (void)setImages:(NSDictionary *)images{
    _images = images[@"small"];
    }

到這里,我們基本上就已經實現了代碼啦!!!!
于是乎,我們在viewController中還要寫上:

typeof(self) weakSelf = self;
[NetWorkManager dataTaskWith:URL completionHandler:^(NSArray *itemsArray) {
[weakSelf.dataSouce pq_updateWithArray:itemsArray];
[weakSelf.myTableView reloadData];
}];

當我們的數據請求回來刷新tableView一次。

> - #### 3.最后一點,很多時候我們需要實現右滑功能,這里實現了一下,右滑刪除功能,同樣不用再viewController中去寫代碼,代碼量沒有增加,功能卻實現了,而且這樣子做的好處就是除了了問題就可以知道是datasource不用再viewController中苦苦尋找。

  • (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
    return YES;
    }
  • (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
    [self.valuesArray removeObjectAtIndex:indexPath.row];
    [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
The end
demo <https://github.com/codepgq/PQSeparationCode>
碼字不易,看完如果對你有幫助,贊一個就是對筆者莫大的鼓勵.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容