The two most commonly used design patterns are MVC and MVC MVVM, about the specific meaning and the advantages and disadvantages of these two patterns, there is no longer here, about the two model to explain a lot of online resources, but this thing about patterns, is relatively broad, just like your girlfriend to eat, you can go to a restaurant in the packaging one for her, also can go to vegetable market to buy vegetables come back, you can buy all cool shadow It may not taste as good as someone else’s cooking, but the end result is the same: you have to feed your girlfriend and keep her full.

So, specific use which model this is not dead, two patterns are each have each benefits, one thousand readers will have one thousand Hamlet, think oneself is involved in several small-scale projects, design patterns for these projects, I don’t think it’s necessary hard which model, how practical how to, how convenient extension how to, traditional MVC do you use No problem, you can use routing MVVM, you can interject viewModels into MVC, after all, requirements are like girlfriend temper, sometimes you can’t understand.

About the UITableView Usage, familiar, its function is very simple, is to show a list of functions, the most common function is pull refresh, drop-down load on the next page, the custom Cell, custom Cell can not say, very basic, but watch this stuff, in the process used in the development should be more, and function about the same, if the table logic implementation Put them in Controller, so there will be a lot of repetitive code in Controller, which is not beautiful and unnecessary. Combined with the open source code in Tencent IM, I think this control can simplify the table of network request in the project by using RAC and MVVM mode. Without further ado, the content will be shown below

So I’m going to bundle data with TableView, create a new class that inherits UITableView. The data semaphore attribute, dataSignal, is used to connect to network requests. Declare the data source property data, which is only used for presentation, so set it to read-only. Not all tables have pull-down refresh, and declare whether paging is supported. Declare a pageSize per page number required by the interface to be readable and writable. At this point, the properties that are required for the basic function are declared, and then you can add whatever you need for the subsequent properties, and we’ll talk about that later, okay

@property(nonatomic, strong) RACSignal *dataSignal; @property(nonatomic, strong, readonly) NSArray *data; @property(nonatomic, assign) BOOL paging; // Default is 10 @property(nonatomic, assign) NSInteger pageSize;Copy the code

Write the implementation in.m, because part of the data is set to read-only, so.m needs to set some private variables to accept the data, declare several properties and methods in.m

@property(nonatomic, strong) NSMutableArray *muArray; // If paging is supported, this is the maximum amount of data, get @property(nonatomic, assign) NSInteger maxNumber from the interface; @property(nonatomic, assign) NSInteger page; @property(nonatomic, copy) void(^startRefresh)(NSInteger page,NSInteger pageSize); // refresh - (void)beginRefresh;Copy the code

Rewrite the initialization method, some of the default variables are assigned, bind the MJRefreshHeader during initialization, and when refreshed, the page number returns to 1, and request data again

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{ if (self = [super initWithFrame:frame style:style]) { self.muArray = [NSMutableArray array]; self.page = 1; self.pageSize = 10; WeakSelf(self); self.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ weakself.page = 1; if (weakself.startRefresh) { / / notice semaphore, to request data, will be back to bring up the page number and quantity, do this in the Controller weakself. StartRefresh (weakself. Page, weakself. PageSize);} [weakself muArray removeAllObjects]; [weakself signalDataSource]; }]; } return self; }Copy the code

Set whether paging is supported or not, and if it is, bind MJTableView and also call back the page number and the number to request new data

- (void)setPaging:(BOOL)paging{ _paging = paging; if (! paging) { return; } WeakSelf(self); self.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{ if (weakself.muArray.count == Weakself. MaxNumber) {/ / if the current data source has already reached the maximum, do not drop down load data again [weakself. Mj_footer endRefreshingWithNoMoreData];} else { weakself.page++; if (weakself.startRefresh) { weakself.startRefresh(weakself.page,weakself.pageSize); } [weakself signalDataSource]; } }]; }Copy the code

The next step is to use RAC to pass data requested from the network

- (void)signalDataSource{ WeakSelf(self); [self.dataSignal subscribeNext:^(id _Nullable x) {// note that the format is fixed,x = @[obj1,obj2 Obj2 = [x objectOrNilAtIndex:1]; if (obj) {weakSelf. maxNumber = [obj integerValue]; } [weakself.muArray addObjectsFromArray:[x objectAtIndex:0]]; [weakself reloadData]; } error:^(NSError * _Nullable error) { [SVProgressHUD showImage:[UIImage imageNamed:@""] status:error.description]; } completed:^{ [weakself  endRefresh]; }]; } - (void)endRefresh{ [self.mj_header endRefreshing]; [self.mj_footer endRefreshing]; }Copy the code

Declare a read-only variable in.h, write the get method to get it, and use it in Controller

- (NSArray *)data{
    return self.muArray.copy;
}

- (void)beginRefresh{
    [self.mj_header beginRefreshing];
}
Copy the code

Now that the basic function is set up, let’s combine the interface data to perform the assignment. Take the current project in my hand as an example, write an interface, and the interface returns a semaphore

// @param areraId // @param priceSort // @param shelves /// @param status check status /// @param size size /// @param page page + (RACSignal *)getSecondHouseListWithCityId:(NSString *)cityId areraId:(NSString *)areraId sorted:(NSString *)priceSort shelves:(NSString *)shelves status:(NSString *)status pageSize:(NSInteger)size page:(NSInteger)page dataType:(NSString *)type;Copy the code

This is a paginated table data, let’s look at the implementation part

+ (RACSignal *)getSecondHouseListWithCityId:(NSString *)cityId areraId:(NSString *)areraId sorted:(NSString *)priceSort shelves:(NSString *)shelves status:(NSString *)status pageSize:(NSInteger)size page:(NSInteger)page dataType:(NSString *)type{ RequestModel *model = [RequestModel defaultModel]; model.keys = @[@"projectCityId",@"areaId",@"priceOrder",@"shelves",@"status",@"pageSize",@"pageNumber",@"dataType"]; model.values = @[cityId,areraId,priceSort,shelves,status,@(size),@(page),type]; RequestLink *link = [RequestLink initstanceStyle:RequestJsonPostStyle requestModel:model.json]; link.index = 1; link.link = @"/agent/sh/list/project"; return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [BaseModel requestByPostAtLink:link CallBack:^(BaseModel *base) { if (base.code.integerValue == 2000) { NSArray *data = [NSArray yy_modelArrayWithClass:SHSecondHouseModel.class json:base.result[@"records"]]; NSInteger max = [base.result[@"total"] IntegerValue]; // Fixed format [subscriber sendNext:@[data,@(Max)]]; [subscriber sendCompleted];}else{[subscriber sendError:base.error]; } }]; return [RACDisposable disposableWithBlock:^{ }]; }]; }Copy the code

Specific network framework content is not too much, is based on AFN and background requirements encapsulated framework.

So how do you use the controller? It’s very simple, you set the properties, you assign the semaphore, you refresh it and you do all of these things. Okay

    [self.view addSubview:self.mTableView];
    [self.mTableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(bottom);
        make.bottom.equalTo(self.view).offset(-HOME_INDICATOR_HEIGHT);
        make.left.right.equalTo(self.view);
    }];
    self.status = @"61";
    self.mTableView.paging = YES;
    self.mTableView.startRefresh = ^(NSInteger page, NSInteger pageSize) {
       weakself.mTableView.dataSignal = [Dao getSecondHouseListWithCityId:@"-1"
                                                                  areraId:@"-1"
                                                                   sorted:@"-1"
                                                                  shelves:@"-1"
                                                                   status:weakself.status
                                                                 pageSize:pageSize
                                                                     page:page
                                                                 dataType:@"2"];
    };
    [self.mTableView beginRefresh];
    
Copy the code

And then in the TableView proxy, all the data we need is available in the TableView, and in the Controller we don’t have to declare properties again and again

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.mTableView.data.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    SHSecondHouseTableViewCell *cell = [SHSecondHouseTableViewCell cellWithTableView:tableView andIndexPath:indexPath];
    [cell fillCellWithModel:self.mTableView.data[indexPath.row] status:YES personal:YES];
    return cell;;
}
Copy the code

Now UITableView has some ideas about MVVM, and if we don’t have the attributes we want, we can declare them in the base class, like we want to know the current page number, we need to call back after we refresh, and so on, we can encapsulate them in the base class, so let’s put in some common functions and attributes, and copy the whole code

#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface LQBaseTableView : */ / @property(nonatomic, strong) RACSignal *dataSignal; @property(nonatomic, strong, readonly) NSArray *data; @property(nonatomic, strong, readonly) id extraData; // Default is 10 @property(nonatomic, assign) NSInteger pageSize; @property(nonatomic, assign) BOOL paging; @property(nonatomic, assign, readOnly) NSInteger maxCount; @property(nonatomic, assign, readOnly) NSInteger currentPage; @property(nonatomic, copy) void(^startRefresh)(NSInteger page,NSInteger pageSize); @property(nonatomic, copy) void(^endHederRefresh)(LQBaseTableView *tableView); // refresh - (void)beginRefresh; // delete single row - (void)deleteRow:(NSIndexPath *)indexPath animation:(UITableViewRowAnimation)animation; // update a single row - (void)changeRow:(NSIndexPath *)indexPath obj:(id)obj; @end NS_ASSUME_NONNULL_ENDCopy the code

.m

@interface LQBaseTableView () @property(nonatomic, strong) NSMutableArray *muArray; @property(nonatomic, assign) NSInteger maxNumber; @property(nonatomic, assign) NSInteger page; @property(nonatomic, strong) id extra; @end @implementation LQBaseTableView - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{ if (self  = [super initWithFrame:frame style:style]) { self.muArray = [NSMutableArray array]; self.page = 1; self.pageSize = 10; EWWeakSelf; self.mj_header = [MJRefreshHeader headerWithRefreshingBlock:^{ weakSelf.page = 1; if (weakSelf.startRefresh) { weakSelf.startRefresh(weakSelf.page,weakSelf.pageSize); } [weakSelf.muArray removeAllObjects]; [weakSelf signalDataSource]; }]; } return self; } - (void)setPaging:(BOOL)paging{ _paging = paging; EWWeakSelf; self.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{ if (weakSelf.muArray.count == weakSelf.maxNumber) { [weakSelf.mj_footer endRefreshingWithNoMoreData]; }else{ weakSelf.page++; if (weakSelf.startRefresh) { weakSelf.startRefresh(weakSelf.page,weakSelf.pageSize); } [weakSelf signalDataSource]; } }]; } - (void)signalDataSource{ EWWeakSelf; [self.dataSignal subscribeNext:^(id _Nullable x) { id obj = [x objectOrNilAtIndex:1]; if (obj) { weakSelf.maxNumber = [obj integerValue]; } id extra = [x objectOrNilAtIndex:2]; weakSelf.extra = extra; [weakSelf.muArray AddObjectsFromArray :[x objectAtIndex:0]]; [weakSelf reloadData]; if (weakself. page == 1) {// End refresh, after refresh operation can execute if in this block (weakSelf.endHederRefresh) { weakSelf.endHederRefresh(weakSelf); } } } error:^(NSError * _Nullable error) { [SVProgressHUD showImage:[UIImage imageNamed:@""] status:error.description]; } completed:^{ [weakSelf endRefresh]; }]; } - (void)endRefresh{ [self.mj_header endRefreshing]; [self.mj_footer endRefreshing]; } - (NSArray *)data{ return self.muArray.copy; } - (id)extraData{ return self.extra; } - (NSInteger)maxCount{ return self.maxNumber; } - (NSInteger)currentPage{ return self.page; } - (void)deleteRow:(NSIndexPath *)indexPath animation:(UITableViewRowAnimation)animation{ if ([self.muArray objectOrNilAtIndex:indexPath.row]) { [self.muArray removeObjectAtIndex:indexPath.row]; [self deleteRow:indexPath.row inSection:indexPath.section withRowAnimation:animation]; [self reloadData]; } } - (void)changeRow:(NSIndexPath *)indexPath obj:(id)obj{ if ([self.muArray objectOrNilAtIndex:indexPath.row]) { self.muArray[indexPath.row] = obj; [self reloadRowAtIndexPath:indexPath withRowAnimation:UITableViewRowAnimationNone]; } } - (void)beginRefresh{ [self.mj_header beginRefreshing]; }Copy the code

conclusion

This idea is only put forward by myself in combination with the actual background specifications of the company and the project specifications of the company. It has advantages and disadvantages. The specific usage and encapsulation still need to be combined with the actual situation of the company’s background

Thanks for watching!!