MJRefresh 与 VIPER 架构:在大型项目中实现清晰的刷新逻辑

MJRefresh 与 VIPER 架构:在大型项目中实现清晰的刷新逻辑

【免费下载链接】MJRefresh An easy way to use pull-to-refresh. 【免费下载链接】MJRefresh 项目地址: https://gitcode.com/gh_mirrors/mj/MJRefresh

引言

在 iOS 开发中,下拉刷新和上拉加载更多是常见的交互需求。MJRefresh 作为一个优秀的第三方库,为开发者提供了便捷的实现方式。然而,在大型项目中,如果刷新逻辑与业务逻辑混杂在一起,会导致代码的可维护性和扩展性变差。VIPER 架构作为一种清晰的分层架构,可以帮助我们更好地组织代码,实现刷新逻辑与业务逻辑的分离。本文将介绍如何在 VIPER 架构中集成 MJRefresh,实现清晰的刷新逻辑。

MJRefresh 简介

MJRefresh 是一个功能强大的下拉刷新和上拉加载更多库,支持多种刷新样式,如普通样式、GIF 样式等。它通过分类的方式为 UIScrollView 及其子类(如 UITableView、UICollectionView)添加了刷新功能,使用起来非常方便。

MJRefresh 的核心类是 MJRefreshComponent,它是所有刷新控件的基类,定义了刷新控件的基本属性和方法。我们可以通过 UIScrollView 的分类方法来添加刷新控件,例如:

// 添加下拉刷新
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
    // 刷新逻辑
}];

// 添加上拉加载更多
self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
    // 加载更多逻辑
}];

VIPER 架构简介

VIPER 架构是一种基于组件化的架构模式,它将应用程序分为五个主要部分:

  • View(视图):负责显示用户界面和处理用户交互。
  • Interactor(交互器):负责处理业务逻辑和数据获取。
  • Presenter( presenter):作为 View 和 Interactor 之间的中间人,处理 View 的事件,调用 Interactor 的方法,并将结果返回给 View。
  • Entity(实体):表示应用程序中的数据模型。
  • Router(路由):负责页面之间的导航和跳转。

VIPER 架构的核心思想是关注点分离,每个组件只负责自己的职责,从而提高代码的可维护性、可测试性和可扩展性。

在 VIPER 架构中集成 MJRefresh

在 VIPER 架构中,我们可以将刷新逻辑的处理放在 Presenter 层,View 层只负责显示刷新状态,Interactor 层负责处理数据的加载和更新。

1. View 层

在 View 层,我们需要为 UIScrollView 及其子类添加 MJRefresh 控件,并将刷新事件通过协议传递给 Presenter 层。例如,在一个基于 UITableView 的 View 中:

// MJTableViewController.h
#import <UIKit/UIKit.h>
#import "MJTableViewPresenterProtocol.h"

@interface MJTableViewController : UIViewController

@property (nonatomic, weak) id<MJTableViewPresenterProtocol> presenter;

- (void)beginRefreshing;
- (void)endRefreshing;
- (void)endRefreshingWithNoMoreData;

@end

// MJTableViewController.m
#import "MJTableViewController.h"
#import <MJRefresh/MJRefresh.h>

@implementation MJTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 添加下拉刷新
    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self action:@selector(headerRefreshing)];
    
    // 添加上拉加载更多
    self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self action:@selector(footerRefreshing)];
}

- (void)headerRefreshing {
    if ([self.presenter respondsToSelector:@selector(refreshData)]) {
        [self.presenter refreshData];
    }
}

- (void)footerRefreshing {
    if ([self.presenter respondsToSelector:@selector(loadMoreData)]) {
        [self.presenter loadMoreData];
    }
}

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

- (void)endRefreshing {
    [self.tableView.mj_header endRefreshing];
    [self.tableView.mj_footer endRefreshing];
}

- (void)endRefreshingWithNoMoreData {
    [self.tableView.mj_footer endRefreshingWithNoMoreData];
}

@end

在上面的代码中,View 层通过协议与 Presenter 层进行通信。当用户触发刷新事件时,View 层会调用 Presenter 层的相应方法。同时,View 层还提供了一些方法来控制刷新状态,如 beginRefreshing、endRefreshing 等,这些方法由 Presenter 层调用。

2. Presenter 层

在 Presenter 层,我们需要处理 View 层传递过来的刷新事件,调用 Interactor 层的方法来获取数据,并在数据获取完成后通知 View 层更新界面。例如:

// MJTableViewPresenter.h
#import <Foundation/Foundation.h>
#import "MJTableViewPresenterProtocol.h"
#import "MJTableViewInteractorProtocol.h"
#import "MJTableViewProtocol.h"

@interface MJTableViewPresenter : NSObject <MJTableViewPresenterProtocol>

@property (nonatomic, weak) id<MJTableViewProtocol> view;
@property (nonatomic, strong) id<MJTableViewInteractorProtocol> interactor;

@end

// MJTableViewPresenter.m
#import "MJTableViewPresenter.h"

@implementation MJTableViewPresenter

- (void)refreshData {
    [self.interactor fetchDataWithPage:1 completion:^(NSArray *data, NSError *error) {
        if (error) {
            // 处理错误
            [self.view endRefreshing];
            return;
        }
        
        // 更新数据
        [self.view updateData:data];
        [self.view endRefreshing];
        
        // 如果没有更多数据,设置上拉加载更多状态
        if (data.count < kPageSize) {
            [self.view endRefreshingWithNoMoreData];
        }
    }];
}

- (void)loadMoreData {
    NSInteger currentPage = [self.view currentPage] + 1;
    [self.interactor fetchDataWithPage:currentPage completion:^(NSArray *data, NSError *error) {
        if (error) {
            // 处理错误
            [self.view endRefreshing];
            return;
        }
        
        // 添加数据
        [self.view addData:data];
        [self.view endRefreshing];
        
        // 如果没有更多数据,设置上拉加载更多状态
        if (data.count < kPageSize) {
            [self.view endRefreshingWithNoMoreData];
        }
    }];
}

@end

在上面的代码中,Presenter 层接收 View 层传递过来的刷新事件,调用 Interactor 层的方法来获取数据。当数据获取完成后,Presenter 层会通知 View 层更新界面,并根据数据情况设置刷新状态。

3. Interactor 层

在 Interactor 层,我们负责处理具体的业务逻辑和数据获取。例如,从网络或本地数据库获取数据:

// MJTableViewInteractor.h
#import <Foundation/Foundation.h>
#import "MJTableViewInteractorProtocol.h"

@interface MJTableViewInteractor : NSObject <MJTableViewInteractorProtocol>

@end

// MJTableViewInteractor.m
#import "MJTableViewInteractor.h"
#import "NetworkManager.h"
#import "DataModel.h"

@implementation MJTableViewInteractor

- (void)fetchDataWithPage:(NSInteger)page completion:(void (^)(NSArray *, NSError *))completion {
    // 从网络获取数据
    [NetworkManager fetchDataWithPage:page success:^(NSDictionary *responseObject) {
        NSArray *dataArray = [DataModel modelsWithArray:responseObject[@"data"]];
        completion(dataArray, nil);
    } failure:^(NSError *error) {
        completion(nil, error);
    }];
}

@end

在上面的代码中,Interactor 层通过网络请求获取数据,并将数据转换为实体模型后返回给 Presenter 层。

4. Entity 层

Entity 层表示应用程序中的数据模型,例如:

// DataModel.h
#import <Foundation/Foundation.h>

@interface DataModel : NSObject

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *content;

+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary;
+ (NSArray *)modelsWithArray:(NSArray *)array;

@end

// DataModel.m
#import "DataModel.h"

@implementation DataModel

+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
    DataModel *model = [[self alloc] init];
    model.title = dictionary[@"title"];
    model.content = dictionary[@"content"];
    return model;
}

+ (NSArray *)modelsWithArray:(NSArray *)array {
    NSMutableArray *models = [NSMutableArray array];
    for (NSDictionary *dictionary in array) {
        [models addObject:[self modelWithDictionary:dictionary]];
    }
    return models;
}

@end

5. Router 层

Router 层负责页面之间的导航和跳转,在本文中不涉及具体的导航逻辑,因此这里不再详细介绍。

示例代码

下面是一个完整的示例代码,展示了在 VIPER 架构中集成 MJRefresh 的基本流程:

View 层协议

// MJTableViewProtocol.h
#import <Foundation/Foundation.h>
#import "DataModel.h"

@protocol MJTableViewProtocol <NSObject>

- (void)updateData:(NSArray<DataModel *> *)data;
- (void)addData:(NSArray<DataModel *> *)data;
- (NSInteger)currentPage;
- (void)endRefreshing;
- (void)endRefreshingWithNoMoreData;

@end

Presenter 层协议

// MJTableViewPresenterProtocol.h
#import <Foundation/Foundation.h>

@protocol MJTableViewPresenterProtocol <NSObject>

- (void)refreshData;
- (void)loadMoreData;

@end

Interactor 层协议

// MJTableViewInteractorProtocol.h
#import <Foundation/Foundation.h>

@protocol MJTableViewInteractorProtocol <NSObject>

- (void)fetchDataWithPage:(NSInteger)page completion:(void (^)(NSArray *, NSError *))completion;

@end

总结

通过将 MJRefresh 与 VIPER 架构相结合,我们可以实现刷新逻辑与业务逻辑的分离,提高代码的可维护性和可扩展性。在 VIPER 架构中,View 层负责显示刷新状态,Presenter 层负责处理刷新事件和协调 Interactor 层,Interactor 层负责处理业务逻辑和数据获取。这种分层架构使得代码更加清晰,每个组件只负责自己的职责,便于单元测试和代码重构。

MJRefresh 提供了丰富的刷新样式和灵活的配置方式,可以满足不同的业务需求。在实际项目中,我们可以根据具体情况选择合适的刷新样式,并结合 VIPER 架构进行集成,从而打造出高质量的 iOS 应用。

【免费下载链接】MJRefresh An easy way to use pull-to-refresh. 【免费下载链接】MJRefresh 项目地址: https://gitcode.com/gh_mirrors/mj/MJRefresh

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值