小说绘上架版本
This commit is contained in:
+180
@@ -0,0 +1,180 @@
|
||||
//
|
||||
// TFCoveragePageTurningController.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TFCoveragePageTurningController;
|
||||
|
||||
typedef NS_ENUM(NSUInteger ,TFPageRollingDirection) {
|
||||
TFPageRollingDirectionNone = 0, // 未知状态
|
||||
TFPageRollingDirectionLeft = 1, // 上一页
|
||||
TFPageRollingDirectionRight = 2 // 下一页
|
||||
};
|
||||
|
||||
// 注意:!!!!!!!!!!!!!!
|
||||
// 在以下所有提供的方法中 包含View和Controller的方法只需要实现其中之一就可以了
|
||||
// 比如:contentModeController = YES (默认)只需要实现所有的Controller方法即可
|
||||
// contentModeController = NO (可自己根据实际需求设置)只需要实现所有的View方法即可
|
||||
// 控件支持数据源分段加载 比如小说阅读时 可以先加载一部分展示后计算准确页数在reload
|
||||
|
||||
/**
|
||||
TFCoveragePageTurningController 数据源代理
|
||||
*/
|
||||
@protocol TFCoveragePageTurningControllerDataSource <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
总页数
|
||||
|
||||
@param pageController pageController
|
||||
@return 总页数
|
||||
*/
|
||||
- (NSInteger)numberOfPagesInPageController:(nonnull TFCoveragePageTurningController *)pageController;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
将要展示页需要的内容 contentModeController = YES
|
||||
|
||||
@param pageController pageController
|
||||
@param index 将要展示页的索引
|
||||
@return 展示内容的控制器
|
||||
*/
|
||||
- (nullable UIViewController *)pageController:(nonnull TFCoveragePageTurningController *)pageController controllerForIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
将要展示页需要的内容 contentModeController = NO
|
||||
|
||||
@param pageController pageController
|
||||
@param index 将要展示页的索引
|
||||
@return 展示内容的视图
|
||||
*/
|
||||
- (nullable UIView *)pageController:(nonnull TFCoveragePageTurningController *)pageController viewForIndex:(NSInteger)index direction:(TFPageRollingDirection)rollingDirection;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
TFCoveragePageTurningController 控制交互代理
|
||||
*/
|
||||
@protocol TFCoveragePageTurningControllerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
当前显示的Controller
|
||||
|
||||
@param pageController pageController
|
||||
@param currentController 当前显示的Controller
|
||||
@param currentIndex 当前页面的索引
|
||||
*/
|
||||
- (void)pageController:(nonnull TFCoveragePageTurningController *)pageController currentController:(nullable UIViewController *)currentController currentIndex:(NSInteger)currentIndex;
|
||||
|
||||
/**
|
||||
当前显示的View
|
||||
|
||||
@param pageController pageController
|
||||
@param currentView 当前显示的View
|
||||
@param currentIndex 当前页面的索引
|
||||
*/
|
||||
- (void)pageController:(nonnull TFCoveragePageTurningController *)pageController currentView:(nullable UIView *)currentView currentIndex:(NSInteger)currentIndex direction:(TFPageRollingDirection)rollingDirection;
|
||||
|
||||
|
||||
/**
|
||||
已切换到第一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)pageControllerDidSwitchToFirst:(nonnull TFCoveragePageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
已切换到最后一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)pageControllerDidSwitchToLast:(nonnull TFCoveragePageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
控制器不能切换到上一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)pageControllerSwitchToLastDisabled:(nonnull TFCoveragePageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
控制器不能切换到上一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)pageControllerSwitchToNextDisabled:(nonnull TFCoveragePageTurningController *)pageController;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TFCoveragePageTurningController : UIViewController
|
||||
/*! 数据源代理 */
|
||||
@property(nonatomic ,weak ,nullable) id<TFCoveragePageTurningControllerDataSource> dataSource;
|
||||
/*! 代理 */
|
||||
@property(nonatomic ,weak ,nullable) id<TFCoveragePageTurningControllerDelegate> delegate;
|
||||
/*! Controller内容模式 default YES / View内容模式 NO */
|
||||
@property(nonatomic) BOOL contentModeController;
|
||||
/*! 动画开启状态 default:YES */
|
||||
@property(nonatomic) BOOL switchAnimated;
|
||||
/*! 滑动切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchSlideEnabled;
|
||||
/*! 单击切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchTapEnabled;
|
||||
/*! 循环切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL circleSwitchEnabled;
|
||||
/*! 上一页切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchToLastEnabled;
|
||||
/*! 下一页切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchToNextEnabled;
|
||||
/*! 当前控制器 */
|
||||
@property(nonatomic ,strong ,readonly ,nullable) UIViewController *currentController;
|
||||
/*! 当前显示的View */
|
||||
@property(nonatomic ,strong ,readonly ,nullable) UIView *currentView;
|
||||
/*! 是否正在动画中 */
|
||||
@property(nonatomic ,readonly) BOOL isAnimating;
|
||||
|
||||
|
||||
#pragma mark - Method
|
||||
|
||||
/**
|
||||
数据源刷新
|
||||
在 (currentIndex != 0 && currentIndex < numberOfPages) 时此方法可以追加总页数而不影响当前页以前的内容
|
||||
否则 此方法强制刷新到第一页
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
数据源刷新 此方法强制刷新到第一页
|
||||
*/
|
||||
- (void)reloadDataToFirst;
|
||||
|
||||
/**
|
||||
判断能否切换到某一页
|
||||
|
||||
@param index 某一页索引
|
||||
@return BOOL结果
|
||||
*/
|
||||
- (BOOL)canSwitchToIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
切换到某一页 需要先做判断 即先调用canSwitchToIndex:index 否则可能发生崩溃
|
||||
|
||||
@param index 某一页索引
|
||||
@param animated 是否需要动画效果
|
||||
*/
|
||||
- (void)switchToIndex:(NSInteger)index animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+797
@@ -0,0 +1,797 @@
|
||||
//
|
||||
// TFCoveragePageTurningController.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFCoveragePageTurningController.h"
|
||||
|
||||
#define AnimateDuration 0.2
|
||||
|
||||
@interface TFCoveragePageTurningController ()
|
||||
/*** 左右拉手势 ***/
|
||||
@property(nonatomic ,strong) UIPanGestureRecognizer *pan;
|
||||
/*** 点击手势 ***/
|
||||
@property(nonatomic ,strong) UITapGestureRecognizer *tap;
|
||||
/*** 手势触发点在左边 辨认方向 左边拿上一个控制器 右边拿下一个控制器 ***/
|
||||
@property(nonatomic) BOOL isLeft;
|
||||
/*** 判断执行pan手势 ***/
|
||||
@property(nonatomic) BOOL isPan;
|
||||
/*** 手势是否重新开始识别 ***/
|
||||
@property(nonatomic) BOOL isPanBegin;
|
||||
|
||||
@property(nonatomic) BOOL isAnimating;
|
||||
/*** 临时控制器 通过代理获取回来的控制器 还没有完全展示出来的控制器 ***/
|
||||
@property(nonatomic ,strong ,nullable) UIViewController *tempController;
|
||||
/*** 临时View 通过代理获取回来的View 还没有完全展示出来的View ***/
|
||||
@property(nonatomic ,strong ,nullable) UIView *tempView;
|
||||
|
||||
@property(nonatomic) NSInteger numberOfPages;
|
||||
|
||||
@property(nonatomic) NSInteger currentIndex;
|
||||
|
||||
@property(nonatomic ,readonly) NSInteger lastIndex;
|
||||
|
||||
@property(nonatomic ,readonly) NSInteger nextIndex;
|
||||
/*** 内部切换控制 能否切换到上一页 ***/
|
||||
@property(nonatomic) BOOL scrollToLastEnabled;
|
||||
/*** 内部切换控制 能否切换到下一页 ***/
|
||||
@property(nonatomic) BOOL scrollToNextEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFCoveragePageTurningController
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// 移除手势
|
||||
[self.view removeGestureRecognizer:self.pan];
|
||||
[self.view removeGestureRecognizer:self.tap];
|
||||
|
||||
// 移除当前控制器
|
||||
if (self.currentController) {
|
||||
[self.currentController.view removeFromSuperview];
|
||||
[self.currentController removeFromParentViewController];
|
||||
_currentController = nil;
|
||||
}
|
||||
|
||||
// 移除临时控制器
|
||||
if (self.tempController) {
|
||||
[self.tempController.view removeFromSuperview];
|
||||
[self.tempController removeFromParentViewController];
|
||||
self.tempController = nil;
|
||||
}
|
||||
|
||||
if (self.currentView) {
|
||||
[self.currentView removeFromSuperview];
|
||||
_currentView = nil;
|
||||
}
|
||||
|
||||
if (self.tempView) {
|
||||
[self.tempView removeFromSuperview];
|
||||
self.tempView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
|
||||
self.contentModeController = YES;
|
||||
self.switchAnimated = YES;
|
||||
|
||||
self.pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGestureRecognizer:)];
|
||||
self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGestureRecognizer:)];
|
||||
|
||||
self.switchSlideEnabled = YES;
|
||||
self.switchTapEnabled = YES;
|
||||
|
||||
self.switchToLastEnabled = YES;
|
||||
self.switchToNextEnabled = YES;
|
||||
|
||||
self.circleSwitchEnabled = YES;
|
||||
self.scrollToLastEnabled = YES;
|
||||
self.scrollToNextEnabled = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.view.layer.masksToBounds = YES;
|
||||
|
||||
[self.view removeGestureRecognizer:self.tap];
|
||||
[self.view removeGestureRecognizer:self.pan];
|
||||
[self.view addGestureRecognizer:self.pan];
|
||||
[self.view addGestureRecognizer:self.tap];
|
||||
}
|
||||
|
||||
- (void)handlePanGestureRecognizer:(UIPanGestureRecognizer *)recognizer
|
||||
{
|
||||
CGPoint tempPoint = [recognizer translationInView:self.view];
|
||||
|
||||
if (recognizer.state == UIGestureRecognizerStateBegan) {
|
||||
if (self.isAnimating) return;
|
||||
|
||||
self.isAnimating = YES;
|
||||
self.isPan = YES;
|
||||
self.isPanBegin = YES;
|
||||
|
||||
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
|
||||
|
||||
if (self.isPanBegin) {
|
||||
self.isPanBegin = NO;
|
||||
self.isLeft = tempPoint.x > 0 ? YES: NO;
|
||||
|
||||
if (![self canHandlePanOrTap]) {
|
||||
self.isPan = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.contentModeController) {
|
||||
self.tempController = [self getNeedsController];
|
||||
[self addController:self.tempController];
|
||||
|
||||
} else {
|
||||
self.tempView = [self getNeedsView];
|
||||
[self addView:self.tempView];
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.isPan) return;
|
||||
|
||||
if (self.contentModeController) {
|
||||
if (!self.tempController) return;
|
||||
|
||||
if (self.isLeft) {
|
||||
self.tempController.view.frame = CGRectMake(tempPoint.x - self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.currentController.view.frame = CGRectMake(tempPoint.x > 0 ? 0 : tempPoint.x ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
} else {
|
||||
if (!self.tempView) return;
|
||||
|
||||
if (self.switchAnimated) {
|
||||
if (self.isLeft) {
|
||||
self.tempView.frame = CGRectMake(tempPoint.x - self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.currentView.frame = CGRectMake(tempPoint.x > 0 ? 0 : tempPoint.x ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { //!< 手势结束
|
||||
if (!self.isPan) return;
|
||||
|
||||
self.isPan = NO;
|
||||
|
||||
if (self.contentModeController) {
|
||||
if (!self.tempController) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL isSuccess = YES;
|
||||
|
||||
if (self.isLeft) {
|
||||
if (self.tempController.view.frame.origin.x <= -(self.view.width - self.view.width*0.18)) {
|
||||
|
||||
isSuccess = NO;
|
||||
}
|
||||
} else {
|
||||
if (self.currentController.view.frame.origin.x >= -self.view.width*0.18) {
|
||||
|
||||
isSuccess = NO;
|
||||
}
|
||||
}
|
||||
|
||||
[self GestureSuccess:isSuccess animated:self.switchAnimated];
|
||||
} else {
|
||||
if (!self.tempView) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
BOOL isSuccess = YES;
|
||||
|
||||
if (self.switchAnimated) {
|
||||
if (self.isLeft) {
|
||||
if (self.tempView.frame.origin.x <= -(self.view.width - self.view.width*0.18)) {
|
||||
|
||||
isSuccess = NO;
|
||||
}
|
||||
} else {
|
||||
if (self.currentView.frame.origin.x >= -self.view.width*0.18) {
|
||||
|
||||
isSuccess = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 手势结束
|
||||
[self GestureSuccess:isSuccess animated:self.switchAnimated];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleTapGestureRecognizer:(UITapGestureRecognizer *)recognizer
|
||||
{
|
||||
if (self.isAnimating) return;
|
||||
|
||||
self.isAnimating = YES;
|
||||
|
||||
CGPoint touchPoint = [recognizer locationInView:self.view];
|
||||
|
||||
self.isLeft = touchPoint.x < self.view.centerX ? YES: NO;
|
||||
|
||||
if (![self canHandlePanOrTap]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.contentModeController) {
|
||||
|
||||
self.tempController = [self getNeedsController];
|
||||
[self addController:self.tempController];
|
||||
|
||||
} else {
|
||||
self.tempView = [self getNeedsView];
|
||||
[self addView:self.tempView];
|
||||
}
|
||||
|
||||
[self GestureSuccess:YES animated:self.switchAnimated];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)canHandlePanOrTap
|
||||
{
|
||||
BOOL can = YES;
|
||||
if (!self.numberOfPages) {
|
||||
self.isAnimating = NO;
|
||||
return NO;
|
||||
}
|
||||
if (self.isLeft) {
|
||||
if (!self.switchToLastEnabled || !self.scrollToLastEnabled) {
|
||||
self.isAnimating = NO;
|
||||
can = NO;
|
||||
if ([self.delegate respondsToSelector:@selector(pageControllerSwitchToLastDisabled:)]) {
|
||||
[self.delegate pageControllerSwitchToLastDisabled:self];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!self.switchToNextEnabled || !self.scrollToNextEnabled) {
|
||||
self.isAnimating = NO;
|
||||
can = NO;
|
||||
if ([self.delegate respondsToSelector:@selector(pageControllerSwitchToNextDisabled:)]) {
|
||||
[self.delegate pageControllerSwitchToNextDisabled:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return can;
|
||||
}
|
||||
|
||||
|
||||
- (UIViewController *)getNeedsController
|
||||
{
|
||||
UIViewController *vc = nil;
|
||||
|
||||
if (![self.dataSource respondsToSelector:@selector(pageController:controllerForIndex:)]) {
|
||||
self.isAnimating = NO;
|
||||
return vc;
|
||||
}
|
||||
|
||||
if (self.isLeft) {
|
||||
vc = [self.dataSource pageController:self controllerForIndex:self.lastIndex];
|
||||
} else {
|
||||
vc = [self.dataSource pageController:self controllerForIndex:self.nextIndex];
|
||||
}
|
||||
|
||||
if (!vc) {
|
||||
self.isAnimating = NO;
|
||||
}
|
||||
|
||||
return vc;
|
||||
}
|
||||
|
||||
- (UIView *)getNeedsView
|
||||
{
|
||||
UIView *view = nil;
|
||||
|
||||
if (![self.dataSource respondsToSelector:@selector(pageController:viewForIndex:direction:)]) {
|
||||
self.isAnimating = NO;
|
||||
return view;
|
||||
}
|
||||
|
||||
if (self.isLeft) {
|
||||
view = [self.dataSource pageController:self viewForIndex:self.lastIndex direction:TFPageRollingDirectionLeft];
|
||||
} else {
|
||||
view = [self.dataSource pageController:self viewForIndex:self.nextIndex direction:TFPageRollingDirectionRight];
|
||||
}
|
||||
|
||||
if (!view) {
|
||||
self.isAnimating = NO;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
// 手势结束
|
||||
- (void)GestureSuccess:(BOOL)isSuccess animated:(BOOL)animated
|
||||
{
|
||||
if (self.contentModeController) {
|
||||
if (self.tempController) {
|
||||
if (self.isLeft) { // 左边
|
||||
if (animated) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
if (isSuccess) {
|
||||
weakSelf.tempController.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
weakSelf.tempController.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
|
||||
} else {
|
||||
if (isSuccess) {
|
||||
self.tempController.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.tempController.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
} else { // 右边
|
||||
if (animated) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
if (isSuccess) {
|
||||
weakSelf.currentController.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
weakSelf.currentController.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
} else {
|
||||
if (isSuccess) {
|
||||
self.currentController.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.currentController.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (self.tempView) {
|
||||
if (self.isLeft) { // 左边
|
||||
if (animated) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
if (isSuccess) {
|
||||
weakSelf.tempView.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
weakSelf.tempView.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
} else {
|
||||
if (isSuccess) {
|
||||
self.tempView.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.tempView.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
} else { // 右边
|
||||
if (animated) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
if (isSuccess) {
|
||||
weakSelf.currentView.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
weakSelf.currentView.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
} else {
|
||||
if (isSuccess) {
|
||||
self.currentView.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else {
|
||||
self.currentView.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 动画结束
|
||||
- (void)animateSuccess:(BOOL)isSuccess
|
||||
{
|
||||
if (self.contentModeController) {
|
||||
if (isSuccess) {
|
||||
|
||||
[self.currentController.view removeFromSuperview];
|
||||
[self.currentController removeFromParentViewController];
|
||||
|
||||
_currentController = self.tempController;
|
||||
|
||||
self.tempController = nil;
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (self.isLeft) {
|
||||
self.currentIndex --;
|
||||
} else {
|
||||
self.currentIndex ++;
|
||||
}
|
||||
|
||||
} else {
|
||||
[self.tempController.view removeFromSuperview];
|
||||
[self.tempController removeFromParentViewController];
|
||||
|
||||
self.tempController = nil;
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (self.isLeft) {
|
||||
[self.dataSource pageController:self viewForIndex:self.lastIndex direction:TFPageRollingDirectionRight];
|
||||
} else {
|
||||
[self.dataSource pageController:self viewForIndex:self.nextIndex direction:TFPageRollingDirectionLeft];
|
||||
}
|
||||
}
|
||||
|
||||
// 代理回调
|
||||
if ([self.delegate respondsToSelector:@selector(pageController:currentController:currentIndex:)]) {
|
||||
[self.delegate pageController:self currentController:self.currentController currentIndex:self.currentIndex];
|
||||
}
|
||||
|
||||
} else {
|
||||
if (isSuccess) {
|
||||
[self.currentView removeFromSuperview];
|
||||
|
||||
_currentView = self.tempView;
|
||||
|
||||
self.tempView = nil;
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (self.isLeft) {
|
||||
self.currentIndex --;
|
||||
} else {
|
||||
self.currentIndex ++;
|
||||
}
|
||||
} else {
|
||||
[self.tempView removeFromSuperview];
|
||||
|
||||
self.tempView = nil;
|
||||
self.isAnimating = NO;
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(pageController:currentView:currentIndex:direction:)]) {
|
||||
[self.delegate pageController:self currentView:self.currentView currentIndex:self.currentIndex direction:self.isLeft ? TFPageRollingDirectionRight : TFPageRollingDirectionLeft];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addController:(UIViewController * _Nullable)controller
|
||||
{
|
||||
if (controller) {
|
||||
[self addChildViewController:controller];
|
||||
if (self.isLeft) { // 左边
|
||||
[self.view addSubview:controller.view];
|
||||
controller.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} else { // 右边
|
||||
if (self.currentController) { // 有值
|
||||
[self.view insertSubview:controller.view belowSubview:self.currentController.view];
|
||||
} else { // 没值
|
||||
[self.view addSubview:controller.view];
|
||||
}
|
||||
controller.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
|
||||
if (self.switchAnimated) { // 添加阴影
|
||||
[self setShadowController:controller];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 设置显示View
|
||||
- (void)addView:(UIView * _Nullable)view
|
||||
{
|
||||
if (view) {
|
||||
if (self.isLeft) { // 左边
|
||||
[self.view addSubview:view];
|
||||
view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
|
||||
} else { // 右边
|
||||
if (self.currentView) {
|
||||
[self.view insertSubview:view belowSubview:self.currentView];
|
||||
} else {
|
||||
[self.view addSubview:view];
|
||||
}
|
||||
view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
}
|
||||
|
||||
if (self.switchAnimated) {
|
||||
[self setShadowView:view];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 给控制器添加阴影
|
||||
- (void)setShadowController:(UIViewController * _Nullable)controller
|
||||
{
|
||||
if (controller) {
|
||||
controller.view.layer.shadowColor = [UIColor blackColor].CGColor; // 阴影颜色
|
||||
controller.view.layer.shadowOffset = CGSizeMake(0 ,0); // 偏移距离
|
||||
controller.view.layer.shadowOpacity = 0.5; // 不透明度
|
||||
controller.view.layer.shadowRadius = 10.0; // 半径
|
||||
}
|
||||
}
|
||||
|
||||
// 给 View 添加阴影
|
||||
- (void)setShadowView:(UIView * _Nullable)view
|
||||
{
|
||||
if (view) {
|
||||
view.layer.shadowColor = [UIColor blackColor].CGColor; // 阴影颜色
|
||||
view.layer.shadowOffset = CGSizeMake(0 ,0); // 偏移距离
|
||||
view.layer.shadowOpacity = 0.5; // 不透明度
|
||||
view.layer.shadowRadius = 10.0; // 半径
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
if ([self.dataSource respondsToSelector:@selector(numberOfPagesInPageController:)]) {
|
||||
|
||||
_numberOfPages = [self.dataSource numberOfPagesInPageController:self];
|
||||
|
||||
if (_numberOfPages <= 0) return;
|
||||
|
||||
if (self.currentIndex && self.currentIndex < self.numberOfPages) {
|
||||
self.currentIndex = self.currentIndex;
|
||||
}
|
||||
self.currentIndex = 0;
|
||||
|
||||
if (self.contentModeController) {
|
||||
if ([self.dataSource respondsToSelector:@selector(pageController:controllerForIndex:)]) {
|
||||
self.tempController = [self.dataSource pageController:self controllerForIndex:self.currentIndex];
|
||||
[self initViewController];
|
||||
}
|
||||
} else {
|
||||
if ([self.dataSource respondsToSelector:@selector(pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionNone];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataToFirst
|
||||
{
|
||||
if ([self.dataSource respondsToSelector:@selector(numberOfPagesInPageController:)]) {
|
||||
|
||||
_numberOfPages = [self.dataSource numberOfPagesInPageController:self];
|
||||
|
||||
if (_numberOfPages <= 0) return;
|
||||
self.currentIndex = 0;
|
||||
|
||||
if (self.contentModeController) {
|
||||
if ([self.dataSource respondsToSelector:@selector(pageController:controllerForIndex:)]) {
|
||||
self.tempController = [self.dataSource pageController:self controllerForIndex:self.currentIndex];
|
||||
[self initViewController];
|
||||
}
|
||||
} else {
|
||||
if ([self.dataSource respondsToSelector:@selector(pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionNone];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initViewController
|
||||
{
|
||||
[self addController:self.tempController];
|
||||
|
||||
if (self.isAnimating) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
if (self.isLeft) {
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.tempController.view.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewControllerComplented];
|
||||
}];
|
||||
} else {
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.currentController.view.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewControllerComplented];
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.tempController.view.frame = self.view.bounds;
|
||||
|
||||
[self initViewControllerComplented];
|
||||
}
|
||||
|
||||
- (void)initViewControllerComplented
|
||||
{
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (_currentController) {
|
||||
[_currentController.view removeFromSuperview];
|
||||
[_currentController removeFromParentViewController];
|
||||
_currentController = nil;
|
||||
}
|
||||
|
||||
_currentController = self.tempController;
|
||||
|
||||
self.tempController = nil;
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(pageController:currentController:currentIndex:)]) {
|
||||
[self.delegate pageController:self currentController:self.currentController currentIndex:self.currentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initView
|
||||
{
|
||||
[self addView:self.tempView];
|
||||
|
||||
if (self.isAnimating) {
|
||||
__weak TFCoveragePageTurningController *weakSelf = self;
|
||||
|
||||
if (self.isLeft) {
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.tempView.frame = CGRectMake(0 ,0 ,self.view.width ,self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewComplented];
|
||||
}];
|
||||
} else {
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.currentView.frame = CGRectMake(-self.view.width ,0 ,self.view.width ,self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewComplented];
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.tempView.frame = self.view.bounds;
|
||||
[self initViewComplented];
|
||||
|
||||
}
|
||||
|
||||
- (void)initViewComplented
|
||||
{
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (_currentView) {
|
||||
[_currentView removeFromSuperview];
|
||||
_currentView = nil;
|
||||
}
|
||||
|
||||
_currentView = self.tempView;
|
||||
|
||||
self.tempView = nil;
|
||||
if ([self.delegate respondsToSelector:@selector(pageController:currentView:currentIndex:direction:)]) {
|
||||
[self.delegate pageController:self currentView:self.currentView currentIndex:self.currentIndex direction:TFPageRollingDirectionNone];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canSwitchToIndex:(NSInteger)index
|
||||
{
|
||||
if (index < 0) {
|
||||
return NO;
|
||||
}
|
||||
if (index > self.numberOfPages - 1) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)switchToIndex:(NSInteger)index animated:(BOOL)animated
|
||||
{
|
||||
if (self.currentIndex == index) {
|
||||
return;
|
||||
}
|
||||
self.isAnimating = animated;
|
||||
self.isLeft = index < self.currentIndex ? YES: NO;
|
||||
self.currentIndex = index;
|
||||
|
||||
if (self.contentModeController) {
|
||||
if ([self.dataSource pageController:self controllerForIndex:self.currentIndex]) {
|
||||
self.tempController = [self.dataSource pageController:self controllerForIndex:self.currentIndex];
|
||||
[self initViewController];
|
||||
}
|
||||
} else {
|
||||
if ([self.dataSource respondsToSelector:@selector(pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionRight];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - getter/setter
|
||||
- (void)setCurrentIndex:(NSInteger)currentIndex
|
||||
{
|
||||
if (self.circleSwitchEnabled) {
|
||||
if (currentIndex == -1) {
|
||||
currentIndex = self.numberOfPages - 1;
|
||||
} else if (currentIndex == self.numberOfPages) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
} else {
|
||||
self.scrollToNextEnabled = self.scrollToLastEnabled = YES;
|
||||
if (currentIndex == 0) {
|
||||
self.scrollToLastEnabled = NO;
|
||||
}
|
||||
if (currentIndex == self.numberOfPages - 1) {
|
||||
self.scrollToNextEnabled = NO;
|
||||
}
|
||||
}
|
||||
_currentIndex = currentIndex;
|
||||
|
||||
if (currentIndex == 0) {
|
||||
if ([self.delegate respondsToSelector:@selector(pageControllerDidSwitchToFirst:)]) {
|
||||
[self.delegate pageControllerDidSwitchToFirst:self];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentIndex == self.numberOfPages - 1) {
|
||||
if ([self.delegate respondsToSelector:@selector(pageControllerDidSwitchToLast:)]) {
|
||||
[self.delegate pageControllerDidSwitchToLast:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)lastIndex
|
||||
{
|
||||
NSInteger lastIndex = self.currentIndex - 1;
|
||||
if (!self.circleSwitchEnabled) return lastIndex;
|
||||
|
||||
if (lastIndex < 0) {
|
||||
lastIndex = self.numberOfPages - 1;
|
||||
}
|
||||
return lastIndex;
|
||||
}
|
||||
|
||||
- (NSInteger)nextIndex
|
||||
{
|
||||
NSInteger nextIndex = self.currentIndex + 1;
|
||||
if (!self.circleSwitchEnabled) return nextIndex;
|
||||
|
||||
if (nextIndex >= self.numberOfPages) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
- (void)setSwitchTapEnabled:(BOOL)switchTapEnabled
|
||||
{
|
||||
_switchTapEnabled = switchTapEnabled;
|
||||
|
||||
self.tap.enabled = switchTapEnabled;
|
||||
}
|
||||
|
||||
- (void)setSwitchSlideEnabled:(BOOL)switchSlideEnabled
|
||||
{
|
||||
_switchSlideEnabled = switchSlideEnabled;
|
||||
|
||||
self.pan.enabled = switchSlideEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// TFScorllPageTurningController.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TFCoveragePageTurningController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@class TFScorllPageTurningController;
|
||||
|
||||
// 注意:!!!!!!!!!!!!!!
|
||||
// 在以下所有提供的方法中 包含View和Controller的方法只需要实现其中之一就可以了
|
||||
// 比如:contentModeController = YES (默认)只需要实现所有的Controller方法即可
|
||||
// contentModeController = NO (可自己根据实际需求设置)只需要实现所有的View方法即可
|
||||
// 控件支持数据源分段加载 比如小说阅读时 可以先加载一部分展示后计算准确页数在reload
|
||||
|
||||
/**
|
||||
TFScorllPageTurningController 数据源代理
|
||||
*/
|
||||
@protocol TFScorllPageTurningControllerDataSource <NSObject>
|
||||
|
||||
/**
|
||||
总页数
|
||||
|
||||
@param pageController pageController
|
||||
@return 总页数
|
||||
*/
|
||||
- (NSInteger)dp_numberOfPagesInPageController:(nonnull TFScorllPageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
将要展示页需要的内容 contentModeController = YES
|
||||
|
||||
@param pageController pageController
|
||||
@param index 将要展示页的索引
|
||||
@return 展示内容的控制器
|
||||
*/
|
||||
- (nullable UIView *)dp_pageController:(nonnull TFScorllPageTurningController *)pageController viewForIndex:(NSInteger)index direction:(TFPageRollingDirection)rollingDirection;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
TFScorllPageTurningController 控制交互代理
|
||||
*/
|
||||
@protocol TFScorllPageTurningControllerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
当前显示的view
|
||||
|
||||
@param pageController pageController
|
||||
@param currentView 当前显示的view
|
||||
@param currentIndex 当前页面的索引
|
||||
*/
|
||||
- (void)dp_pageController:(nonnull TFScorllPageTurningController *)pageController currentView:(nullable UIView *)currentView currentIndex:(NSInteger)currentIndex;
|
||||
|
||||
|
||||
/**
|
||||
已切换到第一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)dp_pageControllerDidSwitchToFirst:(nonnull TFScorllPageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
已切换到最后一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)dp_pageControllerDidSwitchToLast:(nonnull TFScorllPageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
控制器不能切换到上一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)dp_pageControllerSwitchToLastDisabled:(nonnull TFScorllPageTurningController *)pageController;
|
||||
|
||||
/**
|
||||
控制器不能切换到上一页
|
||||
|
||||
@param pageController pageController
|
||||
*/
|
||||
- (void)dp_pageControllerSwitchToNextDisabled:(nonnull TFScorllPageTurningController *)pageController;
|
||||
|
||||
@end
|
||||
|
||||
@interface TFScorllPageTurningController : UIViewController
|
||||
/*! 数据源代理 */
|
||||
@property(nonatomic, weak, nullable) id<TFScorllPageTurningControllerDataSource> dataSource;
|
||||
/*! 代理 */
|
||||
@property(nonatomic, weak, nullable) id<TFScorllPageTurningControllerDelegate> delegate;
|
||||
/*! 动画开启状态 default:YES */
|
||||
@property(nonatomic) BOOL switchAnimated;
|
||||
/*! 滑动切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchSlideEnabled;
|
||||
/*! 单击切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchTapEnabled;
|
||||
/*! 循环切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL circleSwitchEnabled;
|
||||
/*! 上一页切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchToLastEnabled;
|
||||
/*! 下一页切换启用状态 default:YES */
|
||||
@property(nonatomic) BOOL switchToNextEnabled;
|
||||
|
||||
@property(nonatomic, strong, readonly, nullable) UIView *currentView;
|
||||
/*! 是否正在动画中 */
|
||||
@property(nonatomic, readonly) BOOL isAnimating;
|
||||
|
||||
|
||||
#pragma mark - Method
|
||||
|
||||
/**
|
||||
数据源刷新
|
||||
在 (currentIndex != 0 && currentIndex < numberOfPages) 时此方法可以追加总页数而不影响当前页以前的内容
|
||||
否则 此方法强制刷新到第一页
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
数据源刷新 此方法强制刷新到第一页
|
||||
*/
|
||||
- (void)reloadDataToFirst;
|
||||
|
||||
/**
|
||||
判断能否切换到某一页
|
||||
|
||||
@param index 某一页索引
|
||||
@return BOOL结果
|
||||
*/
|
||||
- (BOOL)canSwitchToIndex:(NSInteger)index;
|
||||
|
||||
/**
|
||||
切换到某一页 需要先做判断 即先调用canSwitchToIndex:index 否则可能发生崩溃
|
||||
|
||||
@param index 某一页索引
|
||||
@param animated 是否需要动画效果
|
||||
*/
|
||||
- (void)switchToIndex:(NSInteger)index animated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+617
@@ -0,0 +1,617 @@
|
||||
//
|
||||
// TFScorllPageTurningController.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFScorllPageTurningController.h"
|
||||
#import "TFReaderBookManager.h"
|
||||
|
||||
#define AnimateDuration 0.2
|
||||
|
||||
@interface TFScorllPageTurningController ()
|
||||
/*! 左拉右拉手势 */
|
||||
@property(nonatomic, strong) UIPanGestureRecognizer *pan;
|
||||
/*! 点击手势 */
|
||||
@property(nonatomic, strong) UITapGestureRecognizer *tap;
|
||||
/*! 手势触发点在左边 辨认方向 左边拿上一个控制器 右边拿下一个控制器 */
|
||||
@property(nonatomic) BOOL isLeft;
|
||||
/*! 判断执行pan手势 */
|
||||
@property(nonatomic) BOOL isPan;
|
||||
/*! 手势是否重新开始识别 */
|
||||
@property(nonatomic) BOOL isPanBegin;
|
||||
|
||||
@property(nonatomic) BOOL isAnimating;
|
||||
|
||||
@property (nonatomic, assign) BOOL haveNextPager;
|
||||
/*! 临时View 通过代理获取回来的View 还没有完全展示出来的View */
|
||||
@property(nonatomic, strong, nullable) UIView *tempView;
|
||||
|
||||
@property(nonatomic) NSInteger numberOfPages;
|
||||
|
||||
@property(nonatomic) NSInteger currentIndex;
|
||||
|
||||
@property(nonatomic, readonly) NSInteger lastIndex;
|
||||
|
||||
@property(nonatomic, readonly) NSInteger nextIndex;
|
||||
/*! 内部切换控制 能否切换到上一页 */
|
||||
@property(nonatomic) BOOL scrollToLastEnabled;
|
||||
/*! 内部切换控制 能否切换到下一页 */
|
||||
@property(nonatomic) BOOL scrollToNextEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFScorllPageTurningController
|
||||
|
||||
- (void)dealloc {
|
||||
// 移除手势
|
||||
[self.view removeGestureRecognizer:self.pan];
|
||||
[self.view removeGestureRecognizer:self.tap];
|
||||
|
||||
if (self.currentView) {
|
||||
[self.currentView removeFromSuperview];
|
||||
_currentView = nil;
|
||||
}
|
||||
|
||||
if (self.tempView) {
|
||||
[self.tempView removeFromSuperview];
|
||||
self.tempView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.switchAnimated = YES;
|
||||
|
||||
self.haveNextPager = [[TFReaderBookManager sharedManager] haveNextPager];
|
||||
|
||||
self.pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGestureRecognizer:)];
|
||||
self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGestureRecognizer:)];
|
||||
|
||||
self.switchSlideEnabled = YES;
|
||||
self.switchTapEnabled = YES;
|
||||
|
||||
self.switchToLastEnabled = YES;
|
||||
self.switchToNextEnabled = YES;
|
||||
|
||||
self.circleSwitchEnabled = YES;
|
||||
self.scrollToLastEnabled = YES;
|
||||
self.scrollToNextEnabled = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.view.layer.masksToBounds = YES;
|
||||
|
||||
[self.view removeGestureRecognizer:self.tap];
|
||||
[self.view removeGestureRecognizer:self.pan];
|
||||
[self.view addGestureRecognizer:self.pan];
|
||||
[self.view addGestureRecognizer:self.tap];
|
||||
}
|
||||
|
||||
#pragma mark - GestureRecognizerHandle
|
||||
- (void)handlePanGestureRecognizer:(UIPanGestureRecognizer *)recognizer {
|
||||
CGPoint tempPoint = [recognizer translationInView:self.view];
|
||||
|
||||
if (recognizer.state == UIGestureRecognizerStateBegan) {
|
||||
|
||||
if (self.isAnimating) return;
|
||||
self.isAnimating = YES;
|
||||
self.isPan = YES;
|
||||
self.isPanBegin = YES;
|
||||
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
|
||||
|
||||
if (self.isPanBegin) {
|
||||
self.isPanBegin = NO;
|
||||
|
||||
self.isLeft = tempPoint.x > 0 ? YES: NO;
|
||||
|
||||
if (![self canHandlePanOrTap]) {
|
||||
self.isPan = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
self.tempView = [self getNeedsView];
|
||||
[self addView:self.tempView];
|
||||
}
|
||||
|
||||
if (!self.isPan) return;
|
||||
|
||||
|
||||
if (!self.tempView) return;
|
||||
|
||||
if (self.switchAnimated) {
|
||||
if (self.isLeft) {
|
||||
[self.view bringSubviewToFront:self.currentView];
|
||||
self.tempView.frame = CGRectMake( - self.view.width + tempPoint.x, 0, self.view.width, self.view.height);
|
||||
self.currentView.frame = CGRectMake(tempPoint.x, 0, self.view.width, self.view.height);
|
||||
|
||||
} else if (self.haveNextPager) {
|
||||
self.currentView.frame = CGRectMake(tempPoint.x > 0 ? 0 : tempPoint.x, 0, self.view.width, self.view.height);
|
||||
self.tempView.frame = CGRectMake(self.view.width + tempPoint.x, 0, self.view.width, self.view.height);
|
||||
}
|
||||
}
|
||||
} else { //!< 手势结束
|
||||
if (!self.isPan) return;
|
||||
|
||||
self.isPan = NO;
|
||||
|
||||
if (!self.tempView) {
|
||||
self.isAnimating = NO;
|
||||
return;
|
||||
}
|
||||
BOOL isSuccess = YES;
|
||||
|
||||
if (self.switchAnimated) {
|
||||
if (self.isLeft) {
|
||||
|
||||
if (self.tempView.frame.origin.x <= -(self.view.width - self.view.width*0.18)) {
|
||||
isSuccess = NO;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (self.currentView.frame.origin.x >= -self.view.width*0.18) {
|
||||
isSuccess = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 手势结束
|
||||
[self GestureSuccess:isSuccess animated:self.switchAnimated];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleTapGestureRecognizer:(UITapGestureRecognizer *)recognizer {
|
||||
// 正在动画
|
||||
if (self.isAnimating) return;
|
||||
|
||||
self.isAnimating = YES;
|
||||
|
||||
CGPoint touchPoint = [recognizer locationInView:self.view];
|
||||
|
||||
self.isLeft = touchPoint.x < self.view.centerX ? YES: NO;
|
||||
|
||||
if (![self canHandlePanOrTap]) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.tempView = [self getNeedsView];
|
||||
if (self.tempView == nil) return;
|
||||
[self addView:self.tempView];
|
||||
|
||||
WS(weakSelf)
|
||||
if (self.isLeft) { // 左边
|
||||
[self.view bringSubviewToFront:self.currentView];
|
||||
self.tempView.frame = CGRectMake(- self.view.width, 0, self.view.width, self.view.height);
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
weakSelf.currentView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:YES];
|
||||
}];
|
||||
} else {
|
||||
if (self.haveNextPager) {
|
||||
self.tempView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.currentView.frame = CGRectMake(- self.view.width, 0, self.view.width, self.view.height);
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf animateSuccess:YES];
|
||||
}];
|
||||
} else {
|
||||
self.isAnimating = NO;
|
||||
}
|
||||
|
||||
}
|
||||
self.haveNextPager = [[TFReaderBookManager sharedManager] haveNextPager];
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)canHandlePanOrTap {
|
||||
|
||||
BOOL can = YES;
|
||||
if (!self.numberOfPages) {
|
||||
self.isAnimating = NO;
|
||||
return NO;
|
||||
}
|
||||
if (self.isLeft) {
|
||||
if (!self.switchToLastEnabled || !self.scrollToLastEnabled) {
|
||||
self.isAnimating = NO;
|
||||
can = NO;
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageControllerSwitchToLastDisabled:)]) {
|
||||
[self.delegate dp_pageControllerSwitchToLastDisabled:self];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!self.switchToNextEnabled || !self.scrollToNextEnabled) {
|
||||
self.isAnimating = NO;
|
||||
can = NO;
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageControllerSwitchToNextDisabled:)]) {
|
||||
[self.delegate dp_pageControllerSwitchToNextDisabled:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return can;
|
||||
}
|
||||
|
||||
- (UIView *)getNeedsView {
|
||||
UIView *view = nil;
|
||||
|
||||
if (![self.dataSource respondsToSelector:@selector(dp_pageController:viewForIndex:direction:)]) {
|
||||
self.isAnimating = NO;
|
||||
return view;
|
||||
}
|
||||
|
||||
if (self.isLeft) {
|
||||
view = [self.dataSource dp_pageController:self viewForIndex:self.lastIndex direction:TFPageRollingDirectionLeft];
|
||||
} else {
|
||||
view = [self.dataSource dp_pageController:self viewForIndex:self.nextIndex direction:TFPageRollingDirectionRight];
|
||||
}
|
||||
|
||||
if (!view) {
|
||||
self.isAnimating = NO;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手势结束
|
||||
*/
|
||||
- (void)GestureSuccess:(BOOL)isSuccess animated:(BOOL)animated
|
||||
{
|
||||
if (self.tempView) {
|
||||
|
||||
if (self.isLeft) { // 左边
|
||||
|
||||
if (animated) {
|
||||
__weak TFScorllPageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
|
||||
if (isSuccess) {
|
||||
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
weakSelf.currentView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
|
||||
} else {
|
||||
|
||||
weakSelf.tempView.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
weakSelf.currentView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
}
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
|
||||
} else {
|
||||
|
||||
if (isSuccess) {
|
||||
|
||||
self.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
|
||||
} else {
|
||||
|
||||
self.tempView.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
}
|
||||
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
|
||||
} else { // 右边
|
||||
|
||||
if (animated) {
|
||||
|
||||
__weak TFScorllPageTurningController *weakSelf = self;
|
||||
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
|
||||
if (isSuccess) {
|
||||
|
||||
weakSelf.currentView.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
|
||||
} else {
|
||||
|
||||
weakSelf.currentView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
weakSelf.tempView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
}
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
[weakSelf animateSuccess:isSuccess];
|
||||
}];
|
||||
|
||||
} else {
|
||||
|
||||
if (isSuccess) {
|
||||
|
||||
self.currentView.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
|
||||
} else {
|
||||
|
||||
self.currentView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
}
|
||||
|
||||
[self animateSuccess:isSuccess];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.haveNextPager = [[TFReaderBookManager sharedManager] haveNextPager];
|
||||
}
|
||||
|
||||
/**
|
||||
* 动画结束
|
||||
*/
|
||||
- (void)animateSuccess:(BOOL)isSuccess {
|
||||
if (isSuccess) {
|
||||
|
||||
[self.currentView removeFromSuperview];
|
||||
|
||||
_currentView = self.tempView;
|
||||
|
||||
self.tempView = nil;
|
||||
|
||||
self.isAnimating = NO;
|
||||
if (self.isLeft) {
|
||||
self.currentIndex --;
|
||||
} else {
|
||||
self.currentIndex ++;
|
||||
}
|
||||
} else {
|
||||
|
||||
[self.tempView removeFromSuperview];
|
||||
|
||||
self.tempView = nil;
|
||||
|
||||
self.isAnimating = NO;
|
||||
|
||||
if (self.isLeft) {
|
||||
[self.dataSource dp_pageController:self viewForIndex:self.lastIndex direction:TFPageRollingDirectionRight];
|
||||
} else {
|
||||
[self.dataSource dp_pageController:self viewForIndex:self.nextIndex direction:TFPageRollingDirectionLeft];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageController:currentView:currentIndex:)]) {
|
||||
[self.delegate dp_pageController:self currentView:self.currentView currentIndex:self.currentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 设置显示View
|
||||
|
||||
/**
|
||||
* 添加View
|
||||
*
|
||||
* @param view 显示View
|
||||
*/
|
||||
- (void)addView:(UIView * _Nullable)view {
|
||||
if (view) {
|
||||
|
||||
if (self.isLeft) { // 左边
|
||||
|
||||
[self.view addSubview:view];
|
||||
|
||||
// view.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
|
||||
} else { // 右边
|
||||
|
||||
if (self.currentView) { // 有值
|
||||
|
||||
[self.view insertSubview:view belowSubview:self.currentView];
|
||||
|
||||
} else { // 没值
|
||||
|
||||
[self.view addSubview:view];
|
||||
}
|
||||
|
||||
// view.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Method
|
||||
- (void)reloadData
|
||||
{
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(dp_numberOfPagesInPageController:)]) {
|
||||
_numberOfPages = [self.dataSource dp_numberOfPagesInPageController:self];
|
||||
|
||||
if (_numberOfPages <= 0) return;
|
||||
|
||||
if (self.currentIndex && self.currentIndex < self.numberOfPages) {
|
||||
self.currentIndex = self.currentIndex;
|
||||
// return;
|
||||
}
|
||||
self.currentIndex = 0;
|
||||
if ([self.dataSource respondsToSelector:@selector(dp_pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource dp_pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionNone];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataToFirst
|
||||
{
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(dp_numberOfPagesInPageController:)]) {
|
||||
_numberOfPages = [self.dataSource dp_numberOfPagesInPageController:self];
|
||||
|
||||
if (_numberOfPages <= 0) return;
|
||||
|
||||
self.currentIndex = 0;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(dp_pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource dp_pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionNone];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initView
|
||||
{
|
||||
// 添加
|
||||
[self addView:self.tempView];
|
||||
|
||||
if (self.isAnimating) {
|
||||
__weak TFScorllPageTurningController *weakSelf = self;
|
||||
|
||||
if (self.isLeft) {
|
||||
[self.view bringSubviewToFront:self.currentView];
|
||||
self.tempView.frame = CGRectMake(- self.view.width, 0, self.view.width, self.view.height);
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
weakSelf.currentView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewComplented];
|
||||
}];
|
||||
} else {
|
||||
self.tempView.frame = CGRectMake(self.view.width, 0, self.view.width, self.view.height);
|
||||
[UIView animateWithDuration:AnimateDuration animations:^{
|
||||
weakSelf.currentView.frame = CGRectMake(-self.view.width, 0, self.view.width, self.view.height);
|
||||
weakSelf.tempView.frame = CGRectMake(0, 0, self.view.width, self.view.height);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf initViewComplented];
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 修改frame
|
||||
self.tempView.frame = self.view.bounds;
|
||||
[self initViewComplented];
|
||||
|
||||
}
|
||||
|
||||
- (void)initViewComplented
|
||||
{
|
||||
self.isAnimating = NO;
|
||||
// 当前View有值 进行删除
|
||||
if (_currentView) {
|
||||
|
||||
[_currentView removeFromSuperview];
|
||||
|
||||
_currentView = nil;
|
||||
}
|
||||
|
||||
// 赋值记录
|
||||
_currentView = self.tempView;
|
||||
self.tempView = nil;
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageController:currentView:currentIndex:)]) {
|
||||
[self.delegate dp_pageController:self currentView:self.currentView currentIndex:self.currentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canSwitchToIndex:(NSInteger)index
|
||||
{
|
||||
if (index < 0) {
|
||||
return NO;
|
||||
}
|
||||
if (index > self.numberOfPages - 1) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)switchToIndex:(NSInteger)index animated:(BOOL)animated
|
||||
{
|
||||
if (self.currentIndex == index) {
|
||||
return;
|
||||
}
|
||||
self.isAnimating = animated;
|
||||
self.isLeft = NO;
|
||||
self.currentIndex = index;
|
||||
|
||||
if ([self.dataSource respondsToSelector:@selector(dp_pageController:viewForIndex:direction:)]) {
|
||||
self.tempView = [self.dataSource dp_pageController:self viewForIndex:self.currentIndex direction:TFPageRollingDirectionRight];
|
||||
[self initView];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - getter/setter
|
||||
- (void)setCurrentIndex:(NSInteger)currentIndex
|
||||
{
|
||||
if (self.circleSwitchEnabled) {
|
||||
if (currentIndex == -1) {
|
||||
currentIndex = self.numberOfPages - 1;
|
||||
} else if (currentIndex == self.numberOfPages) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
} else {
|
||||
self.scrollToNextEnabled = self.scrollToLastEnabled = YES;
|
||||
if (currentIndex == 0) {
|
||||
self.scrollToLastEnabled = NO;
|
||||
}
|
||||
if (currentIndex == self.numberOfPages - 1) {
|
||||
self.scrollToNextEnabled = NO;
|
||||
}
|
||||
}
|
||||
_currentIndex = currentIndex;
|
||||
|
||||
if (currentIndex == 0) {
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageControllerDidSwitchToFirst:)]) {
|
||||
[self.delegate dp_pageControllerDidSwitchToFirst:self];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentIndex == self.numberOfPages - 1) {
|
||||
if ([self.delegate respondsToSelector:@selector(dp_pageControllerDidSwitchToLast:)]) {
|
||||
[self.delegate dp_pageControllerDidSwitchToLast:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSInteger)lastIndex
|
||||
{
|
||||
NSInteger lastIndex = self.currentIndex - 1;
|
||||
|
||||
if (!self.circleSwitchEnabled) return lastIndex;
|
||||
|
||||
if (lastIndex < 0) {
|
||||
lastIndex = self.numberOfPages - 1;
|
||||
}
|
||||
|
||||
return lastIndex;
|
||||
}
|
||||
|
||||
- (NSInteger)nextIndex
|
||||
{
|
||||
NSInteger nextIndex = self.currentIndex + 1;
|
||||
|
||||
if (!self.circleSwitchEnabled) return nextIndex;
|
||||
|
||||
if (nextIndex >= self.numberOfPages) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
- (void)setSwitchTapEnabled:(BOOL)switchTapEnabled
|
||||
{
|
||||
_switchTapEnabled = switchTapEnabled;
|
||||
|
||||
self.tap.enabled = switchTapEnabled;
|
||||
}
|
||||
|
||||
- (void)setSwitchSlideEnabled:(BOOL)switchSlideEnabled
|
||||
{
|
||||
_switchSlideEnabled = switchSlideEnabled;
|
||||
|
||||
self.pan.enabled = switchSlideEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user