• preface

    ViewController usually has the most code in a project and the lowest reuse. With the completion of the project’s functions, a bloated ViewContoller will appear. If a new person takes over the project at this time!! And you’re the new guy. Oh my god! It can run to thousands of lines of code, and trust me, you get a little nutty blues. I don’t know where to start… A feeling of powerlessness arises, a feeling that the body is empty = =




A hollowed-out

Here we go, young man. Have a bottle




Okay, you don’t have to come in tomorrow

  • This example implements a demo that downloads data from the network, displays it in a tableView and ultimately just needs to write code in the viewController (probably not optimal, if you have better suggestions, welcome to discuss and correct me). :

    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]; }];Copy the code
  • 1 optimize the Datasource of TableView

    . Let’s look at the comparison between before and after optimization: before optimization

- (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; }Copy the code

After optimization, and reusable

self.dataSouce = [PQTBDataSource dataSourceWith:_dataArray identifier:CELLIDENTIFIER cellConfigBlock:^(TableViewCell * _Nullable cell, id _Nullable item) {// Update your cell}]; self.myTableView.dataSource = self.dataSouce;Copy the code
  • One more thing before we start: if you use more than two TableViews in a project, you’re bound to have duplicate code that doesn’t make much sense. Such as
- (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; }Copy the code

Don’t read what it says yet!! Pretty much the same, right? Your TableView will still have it

I'm just kidding here because I'm going to get an error. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return [@"fuck" integerValue]; }Copy the code

For the above code, you can extract it to make your ViewController look more comfortable. Get him to lose weight…

Let’s get to work

  • Step 1: Create a class and implement datasource’s methods.




    Create a class, name it whatever you want, and I’ll do it

  • 2 the second step, inheritance since the above listed methods have been extracted, here is certainly our own to achieve. Implement method first
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{return number of cells in a row} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{style for each cell You also need to fetch the cell from the reuse pool.Copy the code

And then you realize that we need an array here, so we need an array here

  • An array

  • An identifier

    Considering the need to disclose? I don’t think it is necessary to make it public, so I put it in the.m file

    / / don't allow the user to change outside after the incoming data @ property (nonatomic, strong, readwrite) NSMutableArray * _Nullable valuesArray; @property (nonatomic,copy) NSString * identifier;Copy the code

    In the above code you will find a readwrite. What does this do? It’s mainly in the.h file

    @interface PQTBDataSource : NSObject / / don't allow the user to change outside after the incoming data @ property (nonatomic, strong, readonly) NSMutableArray * _Nullable valuesArray;Copy the code

    If you still don’t know what that means? Look at the picture




    Now you can only read, not write. But if you want to use self.valuesArray in.m, you can write it like this

    So far, we’ve identified two things, an array and a Identifier. One more question:

    At this time, I do not know how to set each cell. There are many kinds of cells, which are drawn by custom XIB and have different class names. It is impossible to set the cell inside. So what we’re going to do is we’re going to have to take it outside, you can use delegate or block if you want, but I prefer blocks, code cohesion.

So far, all we need to know is:

  • An array of
  • identifier
  • A block can then write something like:
+ (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;Copy the code

Methods. At this point our datasource is basically set up and implements all the methods

+ (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;
}Copy the code

So you can set your tableView in your ViewController like this:

self.dataSouce = [PQTBDataSource dataSourceWith:_dataArray identifier:CELLIDENTIFIER cellConfigBlock:^(TableViewCell * _Nullable cell, id _Nullable item) {// Update your cell}]; self.myTableView.dataSource = self.dataSouce;Copy the code

That way your ViewController implements a few lines of code that didn’t make any sense at all, and you can use it in other TableViews.

  • 2 encapsulate the data request. This part of the code does not need to be managed by the viewController. It only needs a result.

Since only one result is needed, we end up putting back a result (whether the request failed or succeeded is just a result). But we don’t know when the request will complete or fail, we can’t wait forever, so the request must be made asynchronously, with a block callback when the request completes.

  • 2.1 Analyze what is needed to request a data-URL. Here we do not consider too complicated cases, just request some data. So you can encapsulate a method that looks like this:
+ (void)dataTaskWith:(NSString *)url completionHandler:(void(^)(NSArray * itemsArray))block;Copy the code

You might notice here why I’m returning an array: Well, the viewController also doesn’t need to know how our data went into the model, same idea, he just needs the result, a useful result. The implementation code

+ (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]; }Copy the code

Here we also generate a Model, and we just need to return the Model. model

+ (instancetype)tableViewModelWith:(NSDictionary *)dict;Copy the code
+ (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"];
}Copy the code

At this point, we have basically implemented the code !!!! So, we’re also going to say in the viewController:

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

Refresh the tableView once our data request comes back.

  • 3. Finally, a lot of times we need to implement right swipe, and here we do it, right swipe delete, and again we don’t have to write any code in the viewController, and the amount of code doesn’t increase, but the functionality is implemented, And the nice thing about this is that in addition to the problem you know it’s a datasource and you don’t have to look it up in the 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];
}Copy the code

The end demo github.com/codepgq/PQS… Code word is not easy, read if it is helpful to you, like a great encouragement to the author.