Good articles to my personal technology blog: https://cainluo.github.io/15009611814095.html


Recently some become lazy, the pace of learning slowed down a lot, it is estimated that play lazy body, even blog less write.

A guy asked me how to package a low-coupling, edible TableViewController. At that time, he didn’t say much, but he still had a lot of confusing questions for me, so he just wrote a blog post as a tutorial.

Reprint statement: if you need to reprint this article, please contact the author, and indicate the source, and can not modify this article without authorization.


The MVVM pattern

Since I’m encapsulating the MVVM pattern here, I’m going to talk about MVVM in a simple, informal, and easy-to-understand way.

Although I have heard of MVVM, I don’t know much about it. In fact, MVVM is not that complicated. Traditional MVC has Model, Views and Controller, while MVVM just adds a ViewModel on this basis. And weakened the function of Controller.

MVC: Model, Views, Controller MVVM: Model, Views, ViewModel

Then the weakened Controller acts as a glue, like Lego building blocks, to assemble Model, Views and ViewModel together into a module, while Model, Views and ViewModel are independent individuals, and no one can live without each other.

It is probably maozi, if there is a better statement, welcome to the old iron supplement ha ~~


Thinking before construction

CLTableViewController is a TableViewController that I packaged myself, and since I’m lazy, it integrates MJRefresh directly into it.

Here’s the idea. Because the TableView has a TableView datasource and a TableViewDelegate, we need to separate the two modules so that we don’t have code bloat.

Note: this does not include your business logic


Encapsulation TableViewDataSource

In addition to the TableViewDataSource, TableViewDelegate, there is also a ViewModel layer, which is used to request data.

Now let’s look at the TableViewDataSource:

#import <Foundation/Foundation.h>
#import "CLTableViewBaseModel.h"

@interface CLTableViewDataSource : NSObject <UITableViewDataSource>

@property (nonatomic.strong.readonly) CLTableViewBaseModel *cl_viewModel;

- (instancetype)initTableViewDataSourceWithViewModel:(CLTableViewBaseModel *)viewModel;

@end
Copy the code
#import "CLTableViewDataSource.h"

@interface CLTableViewDataSource(a)

@property (nonatomic.strong.readwrite) CLTableViewBaseModel *cl_viewModel;

@end

@implementation CLTableViewDataSource

- (instancetype)initTableViewDataSourceWithViewModel:(CLTableViewBaseModel *)viewModel {
    
    self = [super init];
    
    if (self) {
        
        self.cl_viewModel = viewModel;
    }
    
    return self;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    
    if(! cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:@"UITableViewCell"];
    }
    
    return cell;
}

@end
Copy the code

In the.h file, we provide only one initialization method for the specified ViewModel, and the internal implementation returns the number of data sources for the specified ViewModel array, and by default returns a system UITableViewCell, and that’s fine.


Encapsulation TableViewDelegate

The TableViewDelegate is more system specific, so there’s nothing written about the internal implementation:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CLTableViewBaseModel.h"

@interface CLTableViewDelegate : NSObject <UITableViewDelegate>

@property (nonatomic.strong.readonly) CLTableViewBaseModel *cl_viewModel;

- (instancetype)initTableViewDelegateWithViewModel:(CLTableViewBaseModel *)viewModel;

@end
Copy the code
#import "CLTableViewDelegate.h"

@interface CLTableViewDelegate(a)

@property (nonatomic.strong.readwrite) CLTableViewBaseModel *cl_viewModel;

@end

@implementation CLTableViewDelegate

- (instancetype)initTableViewDelegateWithViewModel:(CLTableViewBaseModel *)viewModel {
    
    self = [super init];
    
    if (self) {
        
        self.cl_viewModel = viewModel;
    }
    
    return self;
}

@end
Copy the code

Only one initialization method is defined that specifies the ViewModel.


Encapsulation CLTableViewBaseModel

For the ViewModel layer, HERE I provide three methods and two properties:

#import <Foundation/Foundation.h>
#import "CLTableViewController.h"

@interface CLTableViewBaseModel : NSObject

@property (nonatomic.strong) NSMutableArray *cl_dataSource;
@property (nonatomic.weak.readonly) CLTableViewController *cl_tableViewController;

- (instancetype)initTableViewBaseModelWithController:(CLTableViewController *)viewController;

/** Request data over HTTP */
- (void)cl_tableViewHTTPRequest;

/** Configure the partition line displayed by each Cell in the TableView */
- (void)cl_configTableViewWithDataSource;

@end
Copy the code
#import "CLTableViewBaseModel.h"

@interface CLTableViewBaseModel(a)

@property (nonatomic.weak.readwrite) CLTableViewController *cl_tableViewController;

@end

@implementation CLTableViewBaseModel

- (instancetype)initTableViewBaseModelWithController:(CLTableViewController *)viewController {
    
    self = [super init];
    
    if (self) {
        self.cl_tableViewController = viewController;
    }
    
    return self;
}

- (NSMutableArray *)cl_dataSource {
    
    if(! _cl_dataSource) { _cl_dataSource = [NSMutableArray array];
    }
    
    return _cl_dataSource;
}

- (void)cl_tableViewHTTPRequest {
    
}

- (void)cl_configTableViewWithDataSource {
    
    if (self.cl_dataSource.count > 0) {
        
        self.cl_tableViewController.cl_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; }}@end
Copy the code

And that’s it. Now it’s just a matter of assembling them.


Assemble CLTableViewController

We have just written the three modules, now we start to assemble:

#import "CLViewController.h"

NS_ASSUME_NONNULL_BEGIN
@interface CLTableViewController : CLViewController

@property (nonatomic.strong.readonly) UITableView *cl_tableView;

/** Initialize CLTableViewController @param style UITableViewStyle, default is UITableViewStylePlain @return CLTableViewController */
- (instancetype)initTableViewControllerWithStyle:(UITableViewStyle)style;

- (void)cl_removeRefresh;
- (void)cl_removeHeaderRefresh;
- (void)cl_removeFooterRefresh;

/** Pull down refresh method/pull up load method */
- (void)cl_dropDownRefresh;
- (void)cl_pullUpRefresh;

/** Start the drop-down operation/end the drop-down operation */
- (void)cl_dropDownBeginRefresh;
- (void)cl_dropDownEndRefresh;

/** Start pull-up operation/end pull-up operation */
- (void)cl_pullUpBeginRefresh;
- (void)cl_pullUpEndRefresh;

- (void)cl_setTableViewDelegate:(_Nullable id <UITableViewDelegate>)delegate
                     dataSource:(_Nullable id <UITableViewDataSource>)dataSource;
@end
NS_ASSUME_NONNULL_END
Copy the code
#import "CLTableViewController.h"
#import "MJRefresh.h"
#import "CLTableViewDelegate.h"
#import "CLTableViewBaseModel.h"

@interface CLTableViewController(a)

@property (nonatomic.assign) UITableViewStyle tableViewStyle;

@property (nonatomic.strong.readwrite) UITableView *cl_tableView;

@property (nonatomic.strong) CLTableViewDelegate *cl_tableViewDelegate;
@property (nonatomic.strong) CLTableViewBaseModel *cl_ableViewBaseModel;

@end

@implementation CLTableViewController

- (instancetype)initTableViewControllerWithStyle:(UITableViewStyle)style {
    
    self = [super init];
    
    if (self) {[self setTableViewStyle:style];
    }
    
    return self;
}

#pragma mark - View Did Load
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.opaque = YES;
    self.automaticallyAdjustsScrollViewInsets = NO;

    self.view.backgroundColor = [UIColor whiteColor];
    
    [self cl_addRefresh];
}

- (UITableView *)cl_tableView {
    
    if(! _cl_tableView) { _cl_tableView = [[UITableView alloc] initWithFrame:self.view.frame
                                                     style:self.tableViewStyle];
        
        if (self.tableViewStyle == UITableViewStylePlain) {
            
            _cl_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        }
        
        _cl_tableView.opaque = YES;
    }
    
    return _cl_tableView;
}

- (void)cl_setTableViewDelegate:(id<UITableViewDelegate>)delegate
                     dataSource:(id<UITableViewDataSource>)dataSource {
    
    self.cl_tableView.delegate   = delegate;
    self.cl_tableView.dataSource = dataSource;
}

#pragma mark - Table View Delegate
- (CLTableViewDelegate *)cl_tableViewDelegate {
    
    if(! _cl_tableViewDelegate) { _cl_tableViewDelegate = [[CLTableViewDelegate alloc] initTableViewDelegateWithViewModel:self.cl_ableViewBaseModel];
    }
    
    return _cl_tableViewDelegate;
}

#pragma mark - Table View Base Model
- (CLTableViewBaseModel *)cl_ableViewBaseModel {
    
    if(! _cl_ableViewBaseModel) { _cl_ableViewBaseModel = [[CLTableViewBaseModel alloc] initTableViewBaseModelWithController:self];
    }
    
    return _cl_ableViewBaseModel;
}

#pragma mark - Refresh
- (void)cl_addRefresh {
    
    MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
        [self cl_dropDownRefresh];
    }];
    
    self.cl_tableView.mj_header = header;
    
    MJRefreshBackNormalFooter *refreshFooter = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
        [self cl_pullUpEndRefresh];
    }];
    
    self.cl_tableView.mj_footer = refreshFooter;
    
    [self.view addSubview:self.cl_tableView];
}

- (void)cl_dropDownRefresh {}

- (void)cl_pullUpRefresh {}

- (void)cl_dropDownBeginRefresh {
    [self.cl_tableView.mj_header beginRefreshing];
}

- (void)cl_dropDownEndRefresh {
    [self.cl_tableView.mj_header endRefreshing];
}

- (void)cl_pullUpBeginRefresh {
    [self.cl_tableView.mj_footer beginRefreshing];
}

- (void)cl_pullUpEndRefresh {
    [self.cl_tableView.mj_footer endRefreshing];
}

- (void)cl_removeRefresh {
    
    self.cl_tableView.mj_header = nil;
    self.cl_tableView.mj_footer = nil;
}

- (void)cl_removeHeaderRefresh {
    self.cl_tableView.mj_header = nil;
}

- (void)cl_removeFooterRefresh {
    self.cl_tableView.mj_footer = nil;
}

@end
Copy the code

Complete!!!!!! Now we’ve wrapped a TableViewController of our own.


conclusion

Because the packaging here is relatively simple, if you have better suggestions, you can leave a private message, I will also follow up the optimization, convenient for everyone, of course, I have other frameworks packaged by myself, but there are too many to write one by one, if you like, you can Star my CLFramework, If you want to maintain this open source library together, you can also talk to me.

For detailed usage, you can find the SimpleProject project, which contains my detailed usage.


The last