From 37a256aabc2c9fbf8a1f82834ac28d59a2c972e3 Mon Sep 17 00:00:00 2001 From: Malaar Date: Fri, 5 Apr 2013 13:46:23 +0300 Subject: [PATCH] add Paging --- MUKit/MUPaging/MUPaging.h | 92 ++++++++++ MUKit/MUPaging/MUPaging.m | 266 ++++++++++++++++++++++++++++ MUKit/MUPaging/MUPagingConfig.h | 27 +++ MUKit/MUPaging/MUPagingConfig.m | 28 +++ MUKitTest.xcodeproj/project.pbxproj | 20 +++ Podfile | 1 + 6 files changed, 434 insertions(+) create mode 100644 MUKit/MUPaging/MUPaging.h create mode 100644 MUKit/MUPaging/MUPaging.m create mode 100644 MUKit/MUPaging/MUPagingConfig.h create mode 100644 MUKit/MUPaging/MUPagingConfig.m diff --git a/MUKit/MUPaging/MUPaging.h b/MUKit/MUPaging/MUPaging.h new file mode 100644 index 0000000..8800dc4 --- /dev/null +++ b/MUKit/MUPaging/MUPaging.h @@ -0,0 +1,92 @@ +// +// CTPaging.h +// Apps4All +// +// Created by Malaar on 19.02.13. +// Copyright (c) 2013 Caiguda. All rights reserved. +// + +#import +#import "MUPagingConfig.h" +#import "MUCompoundCell.h" +#import "MUGateway.h" +#import "MUFetchable.h" +#import "Reachability.h" +#import "MUTableDisposerModeled.h" + +@protocol MUPagingDelegate; +@protocol MUPagingMoreCellProtocol; +@protocol MUPagingMoreCellDataProtocol; + +@interface MUPaging : NSObject +{ + Reachability* reachability; + MUCell* moreCell; +} + +@property (nonatomic, readonly) MUPagingConfig* pagingConfig; +@property (nonatomic, readonly) UIViewController* controller; +@property (nonatomic, readonly) MUTableDisposerModeled* tableDisposer; + +@property (nonatomic, weak) id delegate; + +@property (nonatomic, assign) BOOL reloading; +@property (nonatomic, assign) BOOL loadingMore; +@property (nonatomic, strong) NSDate* lastUpdate; + +@property (nonatomic, readonly) MUGatewayTask* gatewayTask; +@property (nonatomic, readonly) NSUInteger pageOffset; +@property (nonatomic, readonly) NSMutableArray* models; +@property (nonatomic, readonly) NSMutableArray* compoundModels; + +#pragma mark - Init/Dealloc +- (id)initWithConfig:(MUPagingConfig*)aPagingConfig + controller:(UIViewController*)aController + tableDisposer:(MUTableDisposerModeled*)aTableDisposer; + +#pragma mark - Load/Reload data +- (void)reloadData; +- (void)loadMoreData; +- (MUGatewayTask*)fetchDataWithCallback:(MUGatewayCallback)aCallback; +- (NSArray*)didFetchData:(NSArray*)aData; +- (void)setupModels:(NSArray*)aModels; + +#pragma mark - For MuTableDisposer +- (void)tableDisposer:(MUTableDisposerModeled*)aTableDisposer didCreateCellData:(MUCellData*)aCellData; +- (void)tableDisposer:(MUTableDisposerModeled*)aTableDisposer didCreateCell:(MUCell*)aCell; +- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; + +#pragma mark - Notifications +- (void)reachabilityChangedNotification:(NSNotification*)aNotification; + +@end + + +@protocol MUPagingDelegate + +- (MUGatewayTask*)paging:(MUPaging*)aPaging fetchDataWithCallback:(MUGatewayCallback)aCallback; +- (MUCellData*)moreCellDataForPaging:(MUPaging*)aPaging; + +@optional + +- (BOOL)shouldFetchForPaging:(MUPaging*)aPaging; +- (void)willBeginFetchingForPaging:(MUPaging*)aPaging; +- (void)didCompleteFetchingForPaging:(MUPaging*)aPaging; + +- (NSArray*)paging:(MUPaging*)aPaging didFetchedDataWithSuccess:(NSArray*)aData; +- (void)paging:(MUPaging*)aPaging didFetchedDataWithFailure:(MUGatewayResponse*)aResponse; + +@end + +@protocol MUPagingMoreCellProtocol + +- (void)didBeginDataLoading; +- (void)didEndDataLoading; + +@end + +@protocol MUPagingMoreCellDataProtocol + +- (void)addTarget:(id)aTarget action:(SEL)anAction; + +@end \ No newline at end of file diff --git a/MUKit/MUPaging/MUPaging.m b/MUKit/MUPaging/MUPaging.m new file mode 100644 index 0000000..d82ca8e --- /dev/null +++ b/MUKit/MUPaging/MUPaging.m @@ -0,0 +1,266 @@ +// +// CTPaging.m +// Apps4All +// +// Created by Malaar on 19.02.13. +// Copyright (c) 2013 Caiguda. All rights reserved. +// + +#import "MUPaging.h" +#import "MUBOCompoundModel.h" + +@interface MUPaging () + +@property (nonatomic) MUGatewayTask* gatewayTask; +@property (nonatomic, strong) MUCell* moreCell; + +- (void)loadMoreDataPressed:(id*)aSender; + +@end + +@implementation MUPaging + +@synthesize needFetch; +@synthesize pagingConfig; +@synthesize controller; +@synthesize tableDisposer; +@synthesize delegate; + +@synthesize moreCell; +@synthesize reloading; +@synthesize loadingMore; +@synthesize lastUpdate; + +@synthesize pageOffset; +@synthesize gatewayTask; +@synthesize models; +@synthesize compoundModels; + +#pragma mark - Init/Dealloc + +- (id)initWithConfig:(MUPagingConfig*)aPagingConfig + controller:(UIViewController*)aController + tableDisposer:(MUTableDisposerModeled*)aTableDisposer +{ + self = [super init]; + if(self) + { + pagingConfig = aPagingConfig; + controller = aController; + tableDisposer = aTableDisposer; + + // defaults + needFetch = YES; + gatewayTask = nil; + pageOffset = 0; + reloading = YES; + loadingMore = NO; + lastUpdate = nil; + + // + models = [NSMutableArray new]; + compoundModels = [NSMutableArray new]; + + // + reachability = [Reachability reachabilityForInternetConnection]; + [reachability startNotifier]; + + // + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(reachabilityChangedNotification:) + name:kReachabilityChangedNotification object:nil]; + + if(pagingConfig.useCompoundCells) + { + [tableDisposer registerCellData:[MUCompoundCellData class] forModel:[MUBOCompoundModel class]]; + } + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Load/Reload data + +- (void)reloadData +{ + [gatewayTask cancel]; + gatewayTask = nil; + pageOffset = 0; + [models removeAllObjects]; + [compoundModels removeAllObjects]; + reloading = YES; + self.needFetch = YES; + [self fetchData]; +} + +- (void)loadMoreData +{ + pageOffset += pagingConfig.pageSize; + loadingMore = YES; + self.needFetch = YES; + [self fetchData]; +} + +- (MUGatewayTask*)fetchDataWithCallback:(MUGatewayCallback)aCallback +{ + return [delegate paging:self fetchDataWithCallback:aCallback]; +} + +- (NSArray*)didFetchData:(NSArray*)aData +{ + return aData; // override if you need +} + +- (void)setupModels:(NSArray*)aModels +{ + [tableDisposer removeAllSections]; + MUSectionReadonly* section = [MUSectionReadonly section]; + [tableDisposer addSection:section]; + + [models addObjectsFromArray:aModels]; + tableDisposer.tableView.hidden = models.count == 0; + + if(pagingConfig.useCompoundCells) + { + NSArray* compoundModelsNew = [MUBOCompoundModel compoundModelsFromModels:aModels + groupByCount:pagingConfig.compoundCellsGroupByCount]; + [compoundModels addObjectsFromArray:compoundModelsNew]; + [tableDisposer setupModels:compoundModels forSection:section]; + } + else + { + [tableDisposer setupModels:models forSection:section]; + } + + if(aModels.count == pagingConfig.pageSize) + { + MUCellData* moreCellData = [delegate moreCellDataForPaging:self]; + [moreCellData addTarget:self action:@selector(loadMoreDataPressed:)]; + [section addCellData:moreCellData]; + } + else + { + moreCell = nil; + } + [tableDisposer reloadData]; +} + +#pragma mark - MUFetchable +- (void)fetchData +{ + if(!self.needFetch) + return; + + if([delegate respondsToSelector:@selector(shouldFetchForPaging:)]) + { + if(![delegate shouldFetchForPaging:self]) + return; + } + + self.needFetch = NO; + + if(loadingMore) + [moreCell didBeginDataLoading]; + + if([delegate respondsToSelector:@selector(willBeginFetchingForPaging:)]) + [delegate willBeginFetchingForPaging:self]; + + [gatewayTask cancel]; + __weak MUPaging* __self = self; + gatewayTask = [self fetchDataWithCallback:^(MUGatewayResponse *aResponse, BOOL aSuccess) + { + __self.gatewayTask = nil; + __self.needFetch = aResponse.boArray.count == 0; + + if(__self.reloading) + { + if(aSuccess) + __self.lastUpdate = [NSDate date]; + } + + if([delegate respondsToSelector:@selector(didCompleteFetchingForPaging:)]) + [__self.delegate didCompleteFetchingForPaging:__self]; + + if(aSuccess) + { + NSArray* fetchedModels = [self didFetchData:aResponse.boArray]; + + if([__self.delegate respondsToSelector:@selector(paging:didFetchedDataWithSuccess:)]) + fetchedModels = [__self.delegate paging:self didFetchedDataWithSuccess:fetchedModels]; + + [__self setupModels:fetchedModels]; + } + else + { + [__self.moreCell didEndDataLoading]; + + if([__self.delegate respondsToSelector:@selector(paging:didFetchedDataWithFailure:)]) + [__self.delegate paging:__self didFetchedDataWithFailure:aResponse]; + } + + __self.reloading = NO; + __self.loadingMore = NO; + + }]; +} + +#pragma mark - For MuTableDisposer + +- (void)tableDisposer:(MUTableDisposerModeled*)aTableDisposer didCreateCellData:(MUCellData*)aCellData +{ + if(aTableDisposer != tableDisposer) + return; + + if(pagingConfig.useCompoundCells) + { + if([aCellData isKindOfClass:[MUCompoundCellData class]]) + { + [(MUCompoundCellData*)aCellData setTableDisposer:tableDisposer]; + } + } +} + +- (void)tableDisposer:(MUTableDisposerModeled*)aTableDisposer didCreateCell:(MUCell*)aCell +{ + if(aTableDisposer != tableDisposer) + return; + + if([aCell.cellData conformsToProtocol:@protocol(MUPagingMoreCellDataProtocol)]) + { + moreCell = (MUCell*)aCell; + } +} + +- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; +{ + if(tableDisposer.tableView != tableView) + return; + + if(pagingConfig.loadMoreDataType == MULoadMoreDataTypeAuto) + { + if(cell == moreCell) + { + [self loadMoreData]; + } + } +} + +#pragma mark - Actions + +- (void)loadMoreDataPressed:(id*)aSender +{ + [self loadMoreData]; +} + +#pragma mark - Notifications +- (void)reachabilityChangedNotification:(NSNotification*)aNotification +{ + if([reachability isReachable]) + [self reloadData]; +} + +@end diff --git a/MUKit/MUPaging/MUPagingConfig.h b/MUKit/MUPaging/MUPagingConfig.h new file mode 100644 index 0000000..54320ea --- /dev/null +++ b/MUKit/MUPaging/MUPagingConfig.h @@ -0,0 +1,27 @@ +// +// CTPagingConfig.h +// Apps4All +// +// Created by Malaar on 19.02.13. +// Copyright (c) 2013 Caiguda. All rights reserved. +// + +#import + +typedef enum _MULoadMoreDataType +{ + MULoadMoreDataTypeAuto, + MULoadMoreDataTypeManual + +} MULoadMoreDataType; + +@interface MUPagingConfig : NSObject + +@property (nonatomic, assign) NSUInteger pageSize; +//@property (nonatomic, assign) BOOL enablePullToRefresh; +//@property (nonatomic, assign) BOOL enablePaging; +@property (nonatomic, assign) MULoadMoreDataType loadMoreDataType; +@property (nonatomic, assign) BOOL useCompoundCells; +@property (nonatomic, assign) NSUInteger compoundCellsGroupByCount; + +@end diff --git a/MUKit/MUPaging/MUPagingConfig.m b/MUKit/MUPaging/MUPagingConfig.m new file mode 100644 index 0000000..417fe5a --- /dev/null +++ b/MUKit/MUPaging/MUPagingConfig.m @@ -0,0 +1,28 @@ +// +// CTPagingConfig.m +// Apps4All +// +// Created by Malaar on 19.02.13. +// Copyright (c) 2013 Caiguda. All rights reserved. +// + +#import "MUPagingConfig.h" + +@implementation MUPagingConfig + +- (id)init +{ + self = [super init]; + if(self) + { + _pageSize = 20; +// _enablePullToRefresh = YES; +// _enablePaging = YES; + _loadMoreDataType = MULoadMoreDataTypeAuto; + _useCompoundCells = NO; + _compoundCellsGroupByCount = 2; + } + return self; +} + +@end diff --git a/MUKitTest.xcodeproj/project.pbxproj b/MUKitTest.xcodeproj/project.pbxproj index 14d1b99..eaa22a2 100644 --- a/MUKitTest.xcodeproj/project.pbxproj +++ b/MUKitTest.xcodeproj/project.pbxproj @@ -74,6 +74,8 @@ 02EFC342155C1D7C00E2639C /* MUKeyboardAvoidingTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 02EFC33B155C1D7C00E2639C /* MUKeyboardAvoidingTableController.m */; }; 02EFC343155C1D7C00E2639C /* MUKeyboardAvoidingTableController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 02EFC33C155C1D7C00E2639C /* MUKeyboardAvoidingTableController.xib */; }; 02EFC344155C1D7C00E2639C /* TableViewWithMapedCells.m in Sources */ = {isa = PBXBuildFile; fileRef = 02EFC33F155C1D7C00E2639C /* TableViewWithMapedCells.m */; }; + 02F24EA3170EE14D00B0F1E4 /* MUPaging.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F24EA0170EE14D00B0F1E4 /* MUPaging.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; + 02F24EA4170EE14D00B0F1E4 /* MUPagingConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 02F24EA2170EE14D00B0F1E4 /* MUPagingConfig.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; E62CDA57152AF88300DACE8F /* warning_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = E62CDA55152AF88300DACE8F /* warning_icon.png */; }; E62CDA58152AF88300DACE8F /* warning_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E62CDA56152AF88300DACE8F /* warning_icon@2x.png */; }; E6337FDC153C6BE500EFBD6F /* MUKeyboardToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = E6337FDB153C6BE500EFBD6F /* MUKeyboardToolbar.m */; }; @@ -263,6 +265,10 @@ 02EFC33C155C1D7C00E2639C /* MUKeyboardAvoidingTableController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MUKeyboardAvoidingTableController.xib; sourceTree = ""; }; 02EFC33E155C1D7C00E2639C /* TableViewWithMapedCells.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewWithMapedCells.h; sourceTree = ""; }; 02EFC33F155C1D7C00E2639C /* TableViewWithMapedCells.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewWithMapedCells.m; sourceTree = ""; }; + 02F24E9F170EE14D00B0F1E4 /* MUPaging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MUPaging.h; sourceTree = ""; }; + 02F24EA0170EE14D00B0F1E4 /* MUPaging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MUPaging.m; sourceTree = ""; }; + 02F24EA1170EE14D00B0F1E4 /* MUPagingConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MUPagingConfig.h; sourceTree = ""; }; + 02F24EA2170EE14D00B0F1E4 /* MUPagingConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MUPagingConfig.m; sourceTree = ""; }; 99521E5F94A64C81B0626F2D /* Pods.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = SOURCE_ROOT; }; DB4464FCC8654B77A686DF9C /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; E62CDA55152AF88300DACE8F /* warning_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = warning_icon.png; sourceTree = ""; }; @@ -715,6 +721,17 @@ path = TableViewWithMapedCells; sourceTree = ""; }; + 02F24E9E170EE14D00B0F1E4 /* MUPaging */ = { + isa = PBXGroup; + children = ( + 02F24E9F170EE14D00B0F1E4 /* MUPaging.h */, + 02F24EA0170EE14D00B0F1E4 /* MUPaging.m */, + 02F24EA1170EE14D00B0F1E4 /* MUPagingConfig.h */, + 02F24EA2170EE14D00B0F1E4 /* MUPagingConfig.m */, + ); + path = MUPaging; + sourceTree = ""; + }; E62CDA53152AF88300DACE8F /* Resourse */ = { isa = PBXGroup; children = ( @@ -830,6 +847,7 @@ E656293B152308DC00CEC66C /* MUKit */ = { isa = PBXGroup; children = ( + 02F24E9E170EE14D00B0F1E4 /* MUPaging */, 02A20965152D07030010733B /* MUKit.h */, 027F8E5F15FB888F00201DD4 /* MUStrategy */, 0263608515F3BFCE00B655A1 /* MUCoreData */, @@ -1166,6 +1184,8 @@ 023B94A51700A5BC00C57B17 /* MUBOCompoundModel.m in Sources */, 023B94A61700A5BC00C57B17 /* MUCompoundCell.m in Sources */, 023B94D71701BD3700C57B17 /* MUModalView.m in Sources */, + 02F24EA3170EE14D00B0F1E4 /* MUPaging.m in Sources */, + 02F24EA4170EE14D00B0F1E4 /* MUPagingConfig.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Podfile b/Podfile index 779be2a..592b9be 100644 --- a/Podfile +++ b/Podfile @@ -2,3 +2,4 @@ platform :ios, '5.0' pod 'AFNetworking', '0.10.1' pod 'BlocksKit' +pod 'Reachability' \ No newline at end of file