《浅谈iOS开发效率》-时间都去哪儿了

·  阅读 667
《浅谈iOS开发效率》-时间都去哪儿了

时间都去哪儿了

时间都去哪儿了?这是我们自问最多的一个问题。每天有开不完的会写不完的页面调不完的接口加不完的班……

  • 当我们静下心来忽然发现,时间都浪费在copypaste上了。
  • 你是否会发现UITableViewUICollectionView在项目页面中的使用频率高达80-90%。
  • 你可能也会发现大部分页面代码结构相同或者相似,不同的地方可能在于Cell的展示效果有所差异。
  • 所以,你的时间都浪费编写重复代码上了。

如何拯救你的时间

  • 我们都知道面向对象语言三大特性:继承封装多态
  • 继承:子类继承自父类,父类提供模板,模板可以降低代码的重复性。
  • 封装:封装是对某一功能特性的函数或者方法进行封装,它可以提升代码的可维护性和降低耦合度。
  • 多态:一个类实例的相同方法在不同情形有不同表现形式。
  • 继承,封装,多态就是拯救我们时间的三把利器。

demo1.gif

UITableView案例

  • 大部分项目中,滚动列表页面都是基于UITableView来实现的,每个页面常规编码步骤大致如下:
    1. 创建ViewController
    1. 自定义TableViewCell
    1. 初始化UITableView
    1. 注册TableViewCell
    1. 遵从UITableViewDelegate, UITableViewDataSource代理
    1. 实现- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section以及- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath等代理
    1. 编写网络加载及数据加工模块
    1. 将数据与Cell进行绑定
  • ……
  • 基本上每个页面都是重复如上操作。

步骤分解

  • 哪些步骤可以封装到基类中呢?
  • 通过分析我们可以发现,步骤1,7,8是每次必须要做了,因为每个页面存在差异性,不适合封装;而2,3,5,6有共性,可以封装到基类中。步骤4应该封装到TableViewCell分类中。
  • Controller作为基类(模板)还是UIView作为基类呢? *Controller扮演的是协调者的工作,而UIView负责页面展示,所以UIView更适合作为基类。

代码实现如下:

  • 注册TableViewCell封装
//UITableViewCell分类的.h文件
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UITableViewCell (XBAdd)
/** 从xib加载cell,自动复用 */
+ (instancetype)cellFromXibWithTableView:(UITableView *)tableView;
/** 加载纯代码cell,自动复用 */
+ (instancetype)cellFromClassWithTableView:(UITableView *)tableView;
@end

NS_ASSUME_NONNULL_END

//UITableViewCell分类的.m文件
#import "UITableViewCell+XBAdd.h"
#import "UIView+XBAdd.h"

@implementation UITableViewCell (XBAdd)
/** 从xib加载cell,自动复用 */
+ (instancetype)cellFromXibWithTableView:(UITableView *)tableView {
    NSString *identifier = self.reusedId;
    UITableViewCell *cell =
        [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        NSLog(@"register cell");
        [tableView registerNib:[UINib nibWithNibName:identifier bundle:nil]
            forCellReuseIdentifier:identifier];
        cell = [self cellFromXibWithTableView:tableView];
    }
    return cell;
}

/** 加载纯代码cell,自动复用 */
+ (instancetype)cellFromClassWithTableView:(UITableView *)tableView {
    NSString *identifier = self.reusedId;
    UITableViewCell *cell =
        [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        NSLog(@"register cell");
        [tableView registerClass:NSClassFromString(identifier) forCellReuseIdentifier:identifier];
        cell = [self cellFromXibWithTableView:tableView];
    }
    return cell;
}
@end
复制代码
  • UITableViewHeaderFooter注册封装
//分类.h文件
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UITableViewHeaderFooterView (XBAdd)
/** 从xib加载, 自动复用 */
+ (instancetype)headerFooterViewFromXibWithTableView:(UITableView *)tableView;

/** 加载纯代码header footer view, 自动复用 */
+ (instancetype)headerFooterViewFromClassWithTableView:(UITableView *)tableView;
@end

NS_ASSUME_NONNULL_END

//分类.m文件
#import "UITableViewHeaderFooterView+XBAdd.h"
#import "UIView+XBAdd.h"

@implementation UITableViewHeaderFooterView (XBAdd)
/** 从xib加载 */
+ (instancetype)headerFooterViewFromXibWithTableView:(UITableView *)tableView {
    NSString *identifier = self.reusedId;

    UITableViewHeaderFooterView *view =
        [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
    if (!view) {
        NSLog(@"register header footer view");
        [tableView registerNib:[UINib nibWithNibName:identifier bundle:nil]
            forHeaderFooterViewReuseIdentifier:identifier];
        view = [self headerFooterViewFromXibWithTableView:tableView];
    }
    return view;
}

/** 加载纯代码header footer view, 自动复用 */
+ (instancetype)headerFooterViewFromClassWithTableView:(UITableView *)tableView {
    NSString *identifier = self.reusedId;

    UITableViewHeaderFooterView *view =
        [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
    if (!view) {
        NSLog(@"register header footer view");
        [tableView registerClass:[self class] forHeaderFooterViewReuseIdentifier:identifier];
        view = [self headerFooterViewFromXibWithTableView:tableView];
    }
    return view;
}
@end

复制代码
  • UIViewTableView列表基类的封装
//.h文件

#import <UIKit/UIKit.h>

@class BNBaseTableListView;

@protocol BNBaseTableListViewDelegate <NSObject>
 @optional
- (void)tableListView:(BNBaseTableListView *)listView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface BNBaseTableListView : UIView<UITableViewDataSource, UITableViewDelegate>
/** table view*/
@property (nonatomic, strong) UITableView *tableView;
/** dataSource*/
@property (nonatomic, strong) NSArray *dataSource;
/** tableViewStyle*/
@property (nonatomic, assign) UITableViewStyle tableViewStyle;
/** 构造方法*/
- (instancetype)initWithFrame:(CGRect)frame tableViewStyle:(UITableViewStyle)style;


/** BNBaseTableListViewDelegate*/
@property (nonatomic, weak) id<BNBaseTableListViewDelegate>delegate;

/** didSelectRowAtIndexPath actionHandle*/
@property(nonatomic, copy) void(^didSelectRowAtIndexPathHandler)(NSIndexPath *indexPath);

#pragma mark - refreshUI
- (void)refreshUI;

@end

//.m文件

#import "BNBaseTableListView.h"

@implementation BNBaseTableListView
- (instancetype)initWithFrame:(CGRect)frame tableViewStyle:(UITableViewStyle)style
{
    self = [super initWithFrame:frame];
    if (self) {
        self.tableViewStyle = self.tableViewStyle;
    }
    return self;
}

#pragma mark - ----------------SetupUI----------------
- (void)setupUI {
    self.backgroundColor = [UIColor whiteColor];
    if ([self.subviews containsObject:self.tableView]) {
        [self.tableView removeFromSuperview];
        self.tableView = nil;
    }
    
    [self addSubview:self.tableView];
    
    [self addLayouts];
}

#pragma mark - ----------------AddLayouts----------------
- (void)addLayouts {
    [self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_offset(0);
    }];
}

#pragma mark - ----------------Public Methods----------------
#pragma mark - ----------------refreshUI----------------
- (void)refreshUI {
    [self.tableView reloadData];
}

#pragma mark - -------------UICollectionViewDataSource----------------
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return [UITableViewCell cellFromClassWithTableView:tableView];
}

#pragma mark - -------------HeaderFooter----------------
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return nil;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return CGFLOAT_MIN;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN;
}

#pragma mark - ----------------UICollectionViewDelegate-------------------
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    !self.didSelectRowAtIndexPathHandler?:self.didSelectRowAtIndexPathHandler(indexPath);
    
    if ([self.delegate respondsToSelector:@selector(tableListView:didSelectRowAtIndexPath:)]) {
        [self.delegate tableListView:self didSelectRowAtIndexPath:indexPath];
    }
}

#pragma mark - ----------------Getter & Setter----------------
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:self.tableViewStyle];
        _tableView.backgroundColor = self.backgroundColor;
        _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        _tableView.dataSource = self;
        _tableView.delegate = self;
        
        _tableView.estimatedRowHeight = 0;
        _tableView.estimatedSectionFooterHeight = 0;
        _tableView.estimatedSectionHeaderHeight = 0;
        
        _tableView.rowHeight = UITableViewAutomaticDimension;
        
    }
    
    return _tableView;
}

- (void)setTableViewStyle:(UITableViewStyle)tableViewStyle {
    _tableViewStyle = tableViewStyle;
    [self setupUI];
}
@end

复制代码

UICollectionView案例

代码实现如下:

  • 注册CollectionCell封装
//分类.h文件
NS_ASSUME_NONNULL_BEGIN

@interface UICollectionViewCell (BNAdd)
/** 从xib加载cell,自动复用 */
+ (instancetype)cellFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;

/** 加载纯代码cell,自动复用 */
+ (instancetype)cellFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;
@end

NS_ASSUME_NONNULL_END

//分类.m文件
#import "UICollectionViewCell+BNAdd.h"

@implementation UICollectionViewCell (BNAdd)
/** 从xib加载cell,自动复用 */
+ (instancetype)cellFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    NSString *identifier = self.reusedId;
    NSMutableSet *identifierSet = [self setCellIdentifierSetForCollectionView:collectionView];
    
    if (![identifierSet containsObject:identifier]) {
        NSLog(@"register nib cell");
        [collectionView registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellWithReuseIdentifier:identifier];
        [identifierSet addObject:identifier];
    }
    
    return [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
}

/** 加载纯代码cell,自动复用 */
+ (instancetype)cellFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    NSString *identifier = self.reusedId;
    NSMutableSet *identifierSet = [self setCellIdentifierSetForCollectionView:collectionView];
    
    if (![identifierSet containsObject:identifier]) {
        NSLog(@"register class cell");
        [collectionView registerClass:[self class] forCellWithReuseIdentifier:identifier];
        [identifierSet addObject:identifier];
    }
    
    return [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
}

/** 关联 IdentifierSet*/
+ (NSMutableSet *)setCellIdentifierSetForCollectionView:(UICollectionView *)collectionView {
    if (![collectionView getAssociatedValueForKey:_cmd]) {
        NSMutableSet *identifierSet = [[NSMutableSet alloc] init];
        [collectionView setAssociateValue:identifierSet withKey:_cmd];
        return identifierSet;
    }
    
    return [collectionView getAssociatedValueForKey:_cmd];
}
@end

复制代码
  • 注册UICollectionReusableView封装
//分类.h文件
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UICollectionReusableView (BNAdd)

/// headerReusableViewFromXib
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)headerReusableViewFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;

/// headerReusableViewFromClass
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)headerReusableViewFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;

/// footerReusableViewFromXib
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)footerReusableViewFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;

/// footerReusableViewFromClass
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)footerReusableViewFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath;
@end

NS_ASSUME_NONNULL_END

//分类.m文件
#import "UICollectionReusableView+BNAdd.h"

@implementation UICollectionReusableView (BNAdd)
#pragma mark - ----------------Public Methods----------------
/// headerReusableViewFromXib
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)headerReusableViewFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    return [self reusableSupplementaryViewFromXibOfKind:UICollectionElementKindSectionHeader collectionView:collectionView forIndexPath:indexPath];
}

/// headerReusableViewFromClass
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)headerReusableViewFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    return [self reusableSupplementaryViewFromClassOfKind:UICollectionElementKindSectionHeader collectionView:collectionView forIndexPath:indexPath];
}

/// footerReusableViewFromXib
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)footerReusableViewFromXibWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    return [self reusableSupplementaryViewFromXibOfKind:UICollectionElementKindSectionFooter collectionView:collectionView forIndexPath:indexPath];
}

/// footerReusableViewFromClass
/// @param collectionView UICollectionView
/// @param indexPath indexPath NSIndexPath
+ (instancetype)footerReusableViewFromClassWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    return [self reusableSupplementaryViewFromClassOfKind:UICollectionElementKindSectionFooter collectionView:collectionView forIndexPath:indexPath];
}

#pragma mark - ----------------Private Methods----------------
+ (instancetype)reusableSupplementaryViewFromXibOfKind:(NSString *)elementKind collectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    NSString *identifier = [self.reusedId stringByAppendingString:elementKind];
    NSMutableSet *identifierSet = [self setReusableSupplementaryViewIdentifierSetForCollectionView:collectionView];
    
    if (![identifierSet containsObject:identifier]) {
        NSLog(@"register nib supplementary view");
        [collectionView registerNib:[UINib nibWithNibName:self.reusedId bundle:nil] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier];
        [identifierSet addObject:identifier];
    }
    
    return [collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath];
}

+ (instancetype)reusableSupplementaryViewFromClassOfKind:(NSString *)elementKind collectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {
    NSString *identifier = [self.reusedId stringByAppendingString:elementKind];;
    NSMutableSet *identifierSet = [self setReusableSupplementaryViewIdentifierSetForCollectionView:collectionView];
    
    if (![identifierSet containsObject:identifier]) {
        NSLog(@"register class supplementary view");
        [collectionView registerClass:[self class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier];
        [identifierSet addObject:identifier];
    }
    
    return [collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath];
    
}

/** 关联 IdentifierSet*/
+ (NSMutableSet *)setReusableSupplementaryViewIdentifierSetForCollectionView:(UICollectionView *)collectionView {
    if (![collectionView getAssociatedValueForKey:_cmd]) {
        NSMutableSet *identifierSet = [[NSMutableSet alloc] init];
        [collectionView setAssociateValue:identifierSet withKey:_cmd];
        return identifierSet;
    }
    
    return [collectionView getAssociatedValueForKey:_cmd];
}
@end

复制代码
  • UIViewUICollectionView列表基类的封装
//.h文件

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
@class BNBaseCollectionListView;

@protocol BNBaseCollectionListDelegate <NSObject>
 @optional
- (void)collectionListView:(BNBaseCollectionListView *)listView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface BNBaseCollectionListView : UIView<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
/** collection view*/
@property (nonatomic, strong) UICollectionView *collectionView;
/** flow layout*/
@property (nonatomic, strong) UICollectionViewFlowLayout *flowLayout;
/** dataSource*/
@property (nonatomic, strong) NSArray *dataSource;


/** delegate*/
@property (nonatomic, weak) id<BNBaseCollectionListDelegate>delegate;

/** Select item at indexPath action handler*/
@property(nonatomic, copy) void(^didSelectItemAtIndexPathHandler)(NSIndexPath *indexPath);

#pragma mark - RefreshUI
- (void)refreshUI;
@end

NS_ASSUME_NONNULL_END

//.m文件
#import "BNBaseCollectionListView.h"

@implementation BNBaseCollectionListView
- (instancetype)initWithFrame:(CGRect)frame {
    if ([super initWithFrame:frame]) {
        [self setupUI];
    }
    
    return self;
}

#pragma mark - ----------------Public Methods----------------
#pragma mark - ----------------RefreshUI----------------
- (void)refreshUI {
    [self.collectionView reloadData];
}

#pragma mark - ----------------Private Methods----------------
#pragma mark - setupUI
- (void)setupUI{
    self.backgroundColor = [UIColor whiteColor];
    
    [self addSubview:self.collectionView];
    [self addLayouts];
}

#pragma mark - ----------------AddLayouts----------------
- (void)addLayouts {
    [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_offset(0);
    }];
}

#pragma mark - -------------UICollectionViewDataSource----------------
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 0;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    return [UICollectionViewCell cellFromClassWithCollectionView:collectionView forIndexPath:indexPath];
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    return nil;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return CGSizeZero;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    return CGSizeZero;
}

#pragma mark - -------------UICollectionViewDelegate----------------
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    
    !self.didSelectItemAtIndexPathHandler?:self.didSelectItemAtIndexPathHandler(indexPath);
    
    if ([self.delegate respondsToSelector:@selector(collectionListView:didSelectItemAtIndexPath:)]) {
        [self.delegate collectionListView:self didSelectItemAtIndexPath:indexPath];
    }
}

#pragma mark - ----------------Getter & Setter----------------
- (UICollectionViewFlowLayout *)flowLayout {
    if (!_flowLayout) {
        _flowLayout = [UICollectionViewFlowLayout new];
        _flowLayout.sectionInset = UIEdgeInsetsZero;
        _flowLayout.itemSize = CGSizeMake(UIScreen.width, UIScreen.height);
        _flowLayout.minimumLineSpacing = 0.0;
        _flowLayout.minimumInteritemSpacing = 0.0;
        
    }
    
    return _flowLayout;
}

- (UICollectionView *)collectionView {
    if (!_collectionView) {
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.flowLayout];
        _collectionView.backgroundColor = [UIColor whiteColor];
        _collectionView.dataSource = self;
        _collectionView.delegate = self;

    }
    
    return _collectionView;
}
@end
复制代码
  • 以上就是对列表视图基类(UITableView, UICollectionView)的封装了,下面我看看具体如何使用。

UITableView基类的使用

页面效果如下:

Simulator Screen Shot - iPhone 11 Pro - 2021-05-22 at 14.57.17.png

  • ViewController代码(共91行)
#import "BNHomeDetailController.h"

#import "BNHomeDetailListView.h" //list view

#import "BNHomeDetailViewModel.h"  //view model

@interface BNHomeDetailController ()<BNBaseTableListViewDelegate>
/** table list view*/
@property (nonatomic, strong) BNHomeDetailListView *listView;
/** view model*/
@property (nonatomic, strong) BNHomeDetailViewModel *viewModel;
@end

@implementation BNHomeDetailController
- (void)dealloc
{
    NSLog(@"%s", __func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
   
    [self setupUI];
    [self requestData];
}


#pragma mark - setupUI
- (void)setupUI {
    self.title = @"home-detail";
    self.view.backgroundColor = [UIColor randomColor];
    
    [self.view addSubview:self.listView];
    
    [self addLayouts];
}

#pragma mark - ----------------AddLayouts----------------
- (void)addLayouts {
    [self.listView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_offset(0);
    }];
}

#pragma mark - ----------------Request----------------
- (void)requestData {
    __weak typeof(self) weakSelf = self;
    [self.viewModel requestDataWithCompletion:^(BOOL succeed, NSString *errorMsg) {
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf refreshUI];
    }];
}

#pragma mark - ----------------RefreshUI----------------
- (void)refreshUI {
    self.listView.dataSource = self.viewModel.listDatas;
    [self.listView refreshUI];
}

#pragma mark - ----------------BNBaseTableListViewDelegate----------------
- (void)tableListView:(BNBaseTableListView *)listView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __func__);
}

#pragma mark - ----------------Getter & Setter----------------
- (BNHomeDetailListView *)listView {
    if (!_listView) {
        _listView = [[BNHomeDetailListView alloc] initWithFrame:CGRectZero tableViewStyle:UITableViewStyleGrouped];
        _listView.tableView.backgroundColor = [UIColor lightGrayColor];
        _listView.delegate = self;
    }
    
    return _listView;
}

- (BNHomeDetailViewModel *)viewModel {
    if (!_viewModel) {
        _viewModel = [BNHomeDetailViewModel new];
    }
    
    return _viewModel;
}
@end

复制代码
  • 列表BNHomeDetailListView(继承自BNBaseTableListView基类)代码(共41行
#import "BNHomeDetailListView.h"

#import "BNHomeDetailTableViewCell.h"   //cell
#import "BNHomeDetailSectionHeaderView.h" //seation header

@implementation BNHomeDetailListView
#pragma mark - -------------UICollectionViewDataSource----------------
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.dataSource.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 10;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    BNHomeDetailTableViewCell *cell = [BNHomeDetailTableViewCell cellFromClassWithTableView:tableView];
    
    cell.text = self.dataSource[indexPath.row];
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    BNHomeDetailSectionHeaderView *header = [BNHomeDetailSectionHeaderView headerFooterViewFromXibWithTableView:tableView];
    header.titleLabel.text = [NSString stringWithFormat:@"section-%ld",section];
    
    return header;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 60;
}
@end

复制代码

UICollectionView基类的使用

效果如下

Simulator Screen Shot - iPhone 11 Pro - 2021-05-22 at 14.57.39.png

  • ViewController代码(共77行)
#import "BNMineController.h"

#import "BNMineCollectionListView.h"

@interface BNMineController ()<BNBaseCollectionListDelegate>
/** collection list view*/
@property (nonatomic, strong) BNMineCollectionListView *collectionListView;
@end

@implementation BNMineController
- (void)dealloc
{
    NSLog(@"%s", __func__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
   
    [self setupUI];
    [self requestData];
}


#pragma mark - setupUI
- (void)setupUI{
    self.fd_prefersNavigationBarHidden = YES;
    self.view.backgroundColor = [UIColor randomColor];
    
    [self.view addSubview:self.collectionListView];
    [self addLayouts];
}

#pragma mark - ----------------AddLayouts----------------
- (void)addLayouts {
    [self.collectionListView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_offset(0);
    }];
}

#pragma mark - ----------------Request----------------
- (void)requestData {
    //fetch data
    
    [self refreshUI];
}

#pragma mark - ----------------RefreshUI----------------
- (void)refreshUI {
    self.collectionListView.dataSource = @[];
    [self.collectionListView refreshUI];
}

#pragma mark - ----------------BNBaseCollectionListDelegate----------------
- (void)collectionListView:(BNBaseCollectionListView *)listView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __func__);
}

#pragma mark - ----------------Getter & Setter----------------
- (BNMineCollectionListView *)collectionListView {
    if (!_collectionListView) {
        _collectionListView = [[BNMineCollectionListView alloc] initWithFrame:CGRectZero];
        CGFloat width = floor(UIScreen.width * 0.5);
        _collectionListView.flowLayout.itemSize = CGSizeMake(width, width);
        _collectionListView.delegate = self;
    }
    
    return _collectionListView;
}
@end

复制代码
  • CollectionView列表视图(继承自BNBaseCollectionListView)(共56行
#import "BNMineCollectionListView.h"

//cell
#import "BNMineNormalCell.h"
#import "BMineImageCell.h"

//header footer
#import "BNMineHeaaderFooterReusableView.h"

@implementation BNMineCollectionListView
#pragma mark - -------------UICollectionViewDataSource----------------
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 50;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 2;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section % 2 == 0) {
        BNMineNormalCell *cell = [BNMineNormalCell cellFromClassWithCollectionView:collectionView forIndexPath:indexPath];
        cell.contentView.backgroundColor = [UIColor randomColor];
        cell.titleLabel.text = [NSString stringWithFormat:@"%ld - %ld",indexPath.section, indexPath.row];
        return cell;
    } else {
        BMineImageCell *cell = [BMineImageCell cellFromXibWithCollectionView:collectionView forIndexPath:indexPath];
        cell.contentView.backgroundColor = [UIColor randomColor];
        return cell;
    }
}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    BNMineHeaaderFooterReusableView *headerFooter = [BNMineHeaaderFooterReusableView headerReusableViewFromXibWithCollectionView:collectionView forIndexPath:indexPath];
    headerFooter.titleLabel.text = [kind isEqualToString:UICollectionElementKindSectionHeader]?@"Header":@"Footer";
    **headerFooter**.backgroundColor = [UIColor randomColor];
    
    return headerFooter;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return CGSizeMake(UIScreen.width, 50);
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    return CGSizeMake(UIScreen.width, 50);
}

@end

复制代码

总结

  • 继承及封装大大提升了编码效率。

  • 因为我们不需要每个页面都需要去创建一个UITableView或者UICollectionView,只要继承自基类,重写使用到的代理方法即可。

  • 同时免去了注册cell的过程,仅需要在使用到cell的地方,调用创建cell统一接口即可。

  • 大大提升了代码的可复用性及可维护性。

  • ViewController变得更加简洁,整体代码逻辑也不会显得太臃肿。

  • 最后附上Demo地址,欢迎大家前来拍砖~~~~~

分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改