Model
CHSModel.h
//
// CHSModel.h
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <Mantle/Mantle.h>
NS_ASSUME_NONNULL_BEGIN
@interface CHSModel:MTLModel <MTLJSONSerializing>
@property (nonatomic,strong) NSString *idMax;
@property (nonatomic,strong) NSString *ctime;
@property (nonatomic,strong) NSString *title;
@property (nonatomic,strong) NSString *descriptionMax;
@property (nonatomic,strong) NSString *source;
@property (nonatomic,strong) NSString *picUrl;
@property (nonatomic,strong) NSString *url;
@end
NS_ASSUME_NONNULL_END
CHSModel.m
//
// CHSModel.m
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import "CHSModel.h"
@implementation CHSModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{
@"idMax": @"id",
@"descriptionMax": @"description",
@"ctime": @"ctime",
@"title": @"title",
@"source": @"source",
@"picUrl": @"picUrl",
@"url": @"url"
};
}
@end
NS_ASSUME_NONNULL_END
ViewModel
CHSViewModel .h
//
// CHSViewModel.h
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CHSModel.h"
#import "MGRefreshHeader.h"
#import "MGRefreshFooter.h"
#import <ReactiveObjC/ReactiveObjC.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^CHSFetchModelDatasHandle)(NSMutableArray<CHSModel *> *models);
@interface CHSViewModel : NSObject
@property (nonatomic, strong) RACSubject *subject;
- (void)fetchModelDatas:(MGRefreshHeader *)header withFooter:(MGRefreshFooter *)footer withPageSize:(double )pageSize withPageIndex:(double )pageIndex withHandle:(CHSFetchModelDatasHandle )handle withLocalData:(BOOL )localData;
//使用RAC:
- (void)signalModelDatas:(MGRefreshHeader *)header withFooter:(MGRefreshFooter *)footer withPageSize:(double )pageSize withPageIndex:(double )pageIndex withLocalData:(BOOL )localData;
@end
NS_ASSUME_NONNULL_END
ViewModel
CHSViewModel .m
//
// CHSViewModel.m
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import "CHSViewModel.h"
#import "MGRequestManager.h"
@interface CHSViewModel()
@property (nonatomic, strong) NSMutableArray<CHSModel *> *dataArrays;
@end
@implementation CHSViewModel
#pragma mark - Public Method
#pragma mark -
- (void)fetchModelDatas:(MGRefreshHeader *)header withFooter:(MGRefreshFooter *)footer withPageSize:(double )pageSize withPageIndex:(double )pageIndex withHandle:(CHSFetchModelDatasHandle )handle withLocalData:(BOOL )localData {
NSMutableDictionary *paras = [[NSMutableDictionary alloc] init];
[paras setObject:@"2dc95ae745b0d989a4f6cf8b53dcb930" forKey:@"key"];
[paras setObject:@(pageSize) forKey:@"num"];
[paras setObject:@(pageIndex) forKey:@"page"];
[MGRequestManager requestWithType:MGHttpRequestTypeGet withUrlString:HOMENEWSDATA withParaments:paras withSuccessBlock:^(NSDictionary *feedBacks) {
if ([[NSString stringWithFormat:@"%@",feedBacks[@"code"]] isEqualToString:@"200"]) {
if (pageIndex == 1) {
[self.dataArrays removeAllObjects];
[header endRefreshing];
}
if ([[NSString stringWithFormat:@"%lu",(unsigned long)[feedBacks[@"newslist"] count]] isEqualToString:@"0"]) {
if (pageIndex != 1) {
} else {
footer.hidden = YES;
[footer endRefreshingWithNoMoreData];
}
} else {
footer.hidden = NO;
if (pageIndex != 1) {
[footer endRefreshing];
}
for (int i = 0; i<[feedBacks[@"newslist"] count]; i++) {
NSError *error = nil;
CHSModel *model = [MTLJSONAdapter modelOfClass:CHSModel.class fromJSONDictionary:feedBacks[@"newslist"][i] error:&error];
[self.dataArrays addObject:model];
}
}
}
!handle ?: handle(self.dataArrays);
} withFailureBlock:^(NSError *error) {
!handle ?: handle([@[] mutableCopy]);
}];
}
//使用RAC:
- (void)signalModelDatas:(MGRefreshHeader *)header withFooter:(MGRefreshFooter *)footer withPageSize:(double )pageSize withPageIndex:(double )pageIndex withLocalData:(BOOL )localData {
NSMutableDictionary *paras = [[NSMutableDictionary alloc] init];
[paras setObject:@"2dc95ae745b0d989a4f6cf8b53dcb930" forKey:@"key"];
[paras setObject:@(pageSize) forKey:@"num"];
[paras setObject:@(pageIndex) forKey:@"page"];
[MGRequestManager requestWithType:MGHttpRequestTypeGet withUrlString:HOMENEWSDATA withParaments:paras withSuccessBlock:^(NSDictionary *feedBacks) {
if ([[NSString stringWithFormat:@"%@",feedBacks[@"code"]] isEqualToString:@"200"]) {
if (pageIndex == 1) {
[self.dataArrays removeAllObjects];
[header endRefreshing];
}
if ([[NSString stringWithFormat:@"%lu",(unsigned long)[feedBacks[@"newslist"] count]] isEqualToString:@"0"]) {
if (pageIndex != 1) {
} else {
footer.hidden = YES;
[footer endRefreshingWithNoMoreData];
}
} else {
footer.hidden = NO;
if (pageIndex != 1) {
[footer endRefreshing];
}
for (int i = 0; i<[feedBacks[@"newslist"] count]; i++) {
NSError *error = nil;
CHSModel *model = [MTLJSONAdapter modelOfClass:CHSModel.class fromJSONDictionary:feedBacks[@"newslist"][i] error:&error];
[self.dataArrays addObject:model];
}
}
}
[self.subject sendNext:self.dataArrays];
} withFailureBlock:^(NSError *error) {
[self.subject sendNext:[@[] mutableCopy]];
}];
}
#pragma mark - lazy load
#pragma mark -
- (NSMutableArray *)dataArrays {
if (_dataArrays == nil) {
_dataArrays = [[NSMutableArray alloc] init];
}
return _dataArrays;
}
//实例化rac:
- (RACSubject *)subject {
if (_subject == nil) {
_subject = [RACSubject subject];
}
return _subject;
}
@end
View
CHSView.h
//
// CHSView.h
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "CHSModel.h"
#import "MGRefreshHeader.h"
#import "MGRefreshFooter.h"
NS_ASSUME_NONNULL_BEGIN
@protocol CHSViewDelegate <NSObject>
@optional
- (void)pullFresh;
- (void)loadMore;
- (void)excuteLogic:(CHSModel *)model;
@end
@interface CHSView : UIView {
}
@property (nonatomic, weak)id<CHSViewDelegate> delegate;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray <CHSModel *> *dataArrays;
@property (nonatomic, strong) MGRefreshHeader *mjRefreshNormalHeader;
@property (nonatomic, strong) MGRefreshFooter *mjRefreshBackNormalFooter;
@end
NS_ASSUME_NONNULL_END
CHSView.m
//
// CHSView.m
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import "CHSView.h"
#import "CHSTableViewCell.h"
#import "MGRefreshHeader.h"
#import <Masonry/Masonry.h>
@interface CHSView()<UITableViewDataSource,UITableViewDelegate> {
}
@end
@implementation CHSView
#pragma mark - LifeCycle
#pragma mark -
- (id)init {
self = [super init ];//当前对象self
if (self !=nil) {
[self setUI];
}
return self;//返回一个已经初始化完毕的对象;
}
#pragma mark - Public Method
#pragma mark -
#pragma mark - Private Method
#pragma mark -
- (void)setUI {
[self addSubview:self.tableView];
[self setMas];
}
- (void)setMas {
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
}
#pragma mark - IB-Action
#pragma mark -
- (void)pullFresh {
if (self.delegate && [self.delegate respondsToSelector:@selector(pullFresh)]) {
[self.delegate pullFresh];
}
}
- (void)loadMore {
if (self.delegate && [self.delegate respondsToSelector:@selector(loadMore)]) {
[self.delegate loadMore];
}
}
#pragma mark - Notice
#pragma mark -
#pragma mark - UITableViewDelegate && UITableViewDataSource
#pragma mark -
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.dataArrays count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [[UIView alloc] init];
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return [[UIView alloc] init];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.01;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CHSTableViewCell *viewCell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CHSTableViewCell class])];
if (!viewCell) {
viewCell = [[CHSTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:NSStringFromClass([CHSTableViewCell class])];
viewCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
CHSModel *model = self.dataArrays[indexPath.row];
viewCell.model = model;
__weak typeof(self)weakSelf = self;
viewCell.handle = ^{
};
return viewCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CHSModel *model = [self.dataArrays objectAtIndex:indexPath.row];
if (self.delegate && [self.delegate respondsToSelector:@selector(excuteLogic:)]) {
[self.delegate excuteLogic:model];
}
}
#pragma mark - lazy load
#pragma mark -
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.backgroundColor = [UIColor whiteColor];
[_tableView registerClass:CHSTableViewCell.self forCellReuseIdentifier:NSStringFromClass([CHSTableViewCell class])];
_tableView.mj_header = self.mjRefreshNormalHeader;
_tableView.mj_footer = self.mjRefreshBackNormalFooter;
if (@available(iOS 11.0, *)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
}
return _tableView;
}
- (MGRefreshHeader *)mjRefreshNormalHeader {
if (!_mjRefreshNormalHeader) {
_mjRefreshNormalHeader = [MGRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(pullFresh)];
}
return _mjRefreshNormalHeader;
}
- (MGRefreshFooter *)mjRefreshBackNormalFooter {
if (!_mjRefreshBackNormalFooter) {
_mjRefreshBackNormalFooter = [MGRefreshFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMore)];
}
return _mjRefreshBackNormalFooter;
}
- (NSMutableArray *)dataArrays {
if (_dataArrays == nil) {
_dataArrays = [[NSMutableArray alloc] init];
}
return _dataArrays;
}
@end
CHSTableViewCell.h
//
// CHSTableViewCell.h
// CHSAPP
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "CHSModel.h"
typedef void (^CHSAPPRemoveHanlde)(void);
NS_ASSUME_NONNULL_BEGIN
@interface CHSTableViewCell : UITableViewCell
@property (nonatomic, copy) CHSAPPRemoveHanlde handle;
@property (nonatomic, strong) CHSModel *model;
@end
NS_ASSUME_NONNULL_END
CHSTableViewCell.m
//
// CHSTableViewCell.m
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import "CHSTableViewCell.h"
#import "CHSEnlargeButton.h"
#import <Masonry/Masonry.h>
#import <SDWebImage/SDWebImage.h>
#import <ReactiveObjC/ReactiveObjC.h>
@interface CHSTableViewCell()
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *bottomLabel;
@property (nonatomic, strong) UIImageView *picImageView;
@property (nonatomic, strong) CHSEnlargeButton *removeButton;
@end
@implementation CHSTableViewCell
#pragma mark - LifeCycle
#pragma mark -
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self setUI];
}
return self;
}
#pragma mark - Public Method
#pragma mark -
- (void)setModel:(CHSModel *)model {
[RACObserve(model,title) subscribeNext:^(NSString *x) {
self.titleLabel.text = x;
}];
[RACObserve(model,picUrl) subscribeNext:^(NSString *x) {
[self.picImageView sd_setImageWithURL:[NSURL URLWithString:x] placeholderImage:nil];
}];
self.bottomLabel.text = [NSString stringWithFormat:@"%@ 头条 %@",model.source,model.ctime];
}
#pragma mark - Private Method
#pragma mark -
- (void)setUI {
[self.contentView addSubview:self.titleLabel];
[self.contentView addSubview:self.bottomLabel];
[self.contentView addSubview:self.picImageView];
[self.contentView addSubview:self.removeButton];
[self setMas];
}
- (void)setMas {
[self.picImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contentView);
make.right.equalTo(self.contentView.mas_right).offset(-16);
make.size.equalTo(@(CGSizeMake(120, 80)));
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.picImageView.mas_top);
make.left.equalTo(self.contentView.mas_left).offset(16);
make.right.equalTo(self.picImageView.mas_left).offset(-16);
}];
[self.removeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.picImageView.mas_bottom);
make.right.equalTo(self.picImageView.mas_left).offset(-16);
}];
[self.bottomLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.removeButton);
make.left.equalTo(self.contentView.mas_left).offset(16);
make.right.equalTo(self.picImageView.mas_right).offset(-72);
}];
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
#pragma mark - IB-Action
#pragma mark -
- (void)toRemove:(UIButton *)sender {
!self.handle ?: self.handle();
}
#pragma mark - lazy load
#pragma mark -
- (UILabel *)titleLabel {
if(_titleLabel == nil) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.numberOfLines = 2;
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.font = [UIFont systemFontOfSize:14];
}
return _titleLabel;
}
- (UILabel *)bottomLabel {
if(_bottomLabel == nil) {
_bottomLabel = [[UILabel alloc] init];
_bottomLabel.numberOfLines = 1;
_bottomLabel.textColor = [UIColor grayColor];
_bottomLabel.font = [UIFont systemFontOfSize:10];
}
return _bottomLabel;
}
- (UIImageView *)picImageView {
if(_picImageView == nil) {
_picImageView = [[UIImageView alloc] init];
_picImageView.layer.cornerRadius = 6.0;
_picImageView.layer.masksToBounds = YES;
}
return _picImageView;
}
- (CHSEnlargeButton *)removeButton {
if (!_removeButton) {
_removeButton = [[CHSEnlargeButton alloc] init];
_removeButton.enlargeEdge_hsa = UIEdgeInsetsMake(5, 5, 5, 5);
[_removeButton setImage:[UIImage imageNamed:@"hsa_app_share_close"] forState:UIControlStateNormal];
[_removeButton addTarget:self action:@selector(toRemove:) forControlEvents:UIControlEventTouchUpInside];
}
return _removeButton;
}
@end
Controller
CHSViewController.h
//
// CHSViewController.h
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "CHSView.h"
@interface CHSViewController : UIViewController
@property (nonatomic, strong) CHSView *chsView;
@end
CHSViewController.m
//
// CHSViewController.m
// CHSAPP
//
// Created by stone on 2021/8/24.
// Copyright © 2021 stone. All rights reserved.
//
#import "CHSViewController.h"
#import <Masonry/Masonry.h>
#import "CHSModel.h"
#import "CHSViewModel.h"
@interface CHSViewController ()<CHSViewDelegate>
@property (nonatomic, strong) CHSViewModel *viewModel;
@property (nonatomic, assign) double pageIndex;
@property (nonatomic, assign) double pageSize;
@end
@implementation CHSViewController
#pragma mark - LifeCycle
#pragma mark -
- (instancetype)init {
self = [super init];
if (self) {
self.pageIndex = 1;
self.pageSize = 10;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//订阅信号:
[self subscribeSignal];
[self setUI];
[self fireRequest];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
#pragma mark - Public Method
#pragma mark -
#pragma mark - Private Method
#pragma mark -
- (void)setUI {
[self.view addSubview:self.chsView];
[self setMas];
}
- (void)setMas {
[self.chsView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(StatusBarH+NaviBarH);
make.bottom.equalTo(self.view.mas_bottom).offset(-TabBarH);
make.left.equalTo(self.view.mas_left);
make.right.equalTo(self.view.mas_right);
}];
}
- (void)subscribeSignal {
__weak typeof(self)weakSelf = self;
[self.viewModel.subject subscribeNext:^(id _Nullable x) {
NSMutableArray<CHSModel *> *models = (NSMutableArray<CHSModel *> *)x;
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.chsView.dataArrays = models;
[strongSelf.chsView.tableView reloadData];
}];
}
- (void)fireRequest {
// __weak typeof(self)weakSelf = self;
// [self.viewModel fetchModelDatas:self.chsView.mjRefreshNormalHeader withFooter:self.chsView.mjRefreshBackNormalFooter withPageSize:self.pageSize withPageIndex:self.pageIndex withHandle:^(NSMutableArray<CHSModel *> * _Nonnull models) {
// __strong typeof(weakSelf) strongSelf = weakSelf;
// strongSelf.chsView.dataArrays = models;
// [strongSelf.chsView.tableView reloadData];
// }];
[self.viewModel signalModelDatas:self.chsView.mjRefreshNormalHeader withFooter:self.chsView.mjRefreshBackNormalFooter withPageSize:self.pageSize withPageIndex:self.pageIndex];
}
#pragma mark - IB-Action
#pragma mark -
#pragma mark - CHSViewDelegate
#pragma mark -
#pragma mark - Delegate
#pragma mark -
- (void)pullFresh {
self.pageIndex = 1;
[self fireRequest];
}
- (void)loadMore {
self.pageIndex = self.pageIndex + 1;
[self fireRequest];
}
- (void)excuteLogic:(CHSModel *)model {
}
#pragma mark - Lazy load
- (CHSView *)chsView
{
if (!_chsView) {
_chsView = [[CHSView alloc] init];
_chsView.delegate = self; //将CHSViewController自己的实例作为委托对象
}
return _chsView;
}
- (CHSViewModel *)viewModel {
if (_viewModel == nil) {
_viewModel = [[CHSViewModel alloc] init];
}
return _viewModel;
}
@end