小说绘上架版本
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// TFBasicViewController.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/11/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TFBasicNavBarView.h"
|
||||
#import "TFEmptyView.h"
|
||||
#import "SGPagingView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define TFPageView_H 44
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TFProductionType) {
|
||||
|
||||
TFProductionTypeNovel = 0,
|
||||
TFProductionTypeComic = 1,
|
||||
TFProductionTypeAudio = 2,
|
||||
TFProductionTypeAi = 3
|
||||
};
|
||||
|
||||
@interface TFBasicViewController : UIViewController
|
||||
#pragma mark - 属性
|
||||
@property (nonatomic ,strong) NSMutableArray *dataSourceArray;
|
||||
@property (nonatomic ,strong) NSMutableDictionary *advertDict;
|
||||
@property (nonatomic ,assign) int currentPageNumber;
|
||||
@property (nonatomic ,assign) TFProductionType productionType;
|
||||
@property (nonatomic ,assign) CGFloat pageViewHeight;
|
||||
|
||||
#pragma mark - 控件
|
||||
@property (nonatomic ,strong) TFBasicNavBarView *navigationBar;
|
||||
@property (nonatomic ,strong) UITableView *mainTableView;
|
||||
@property (nonatomic ,strong) UITableView *mainTableViewGroup;
|
||||
@property (nonatomic ,strong) UICollectionViewFlowLayout *mainCollectionViewFlowLayout;
|
||||
@property (nonatomic ,strong) UICollectionView *mainCollectionView;
|
||||
@property (nonatomic ,strong) SGPageTitleViewConfigure *pageConfigure;
|
||||
@property (nonatomic ,strong) TFEmptyView *emptyView;
|
||||
@property (nonatomic ,copy) NSString *navTitle;
|
||||
@property (nonatomic ,assign) BOOL isPresentState;
|
||||
|
||||
#pragma mark - 公共方法
|
||||
// 返回上一页
|
||||
- (void)popViewController;
|
||||
|
||||
// 是否可右滑返回
|
||||
- (void)navigationCanSlidingBack:(BOOL)canSlidingBack;
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *_Nullable)title tapBlock:(void(^)(void))tapBlock;
|
||||
|
||||
// 空白占位图,自定义Y值
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *_Nullable)title centerY:(CGFloat)centerY tapBlock:(void(^)(void))tapBlock;
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString * _Nullable)title buttonTitle:(NSString * _Nullable)buttonTitle tapBlock:(void(^)(void))tapBlock;
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString * _Nullable)title detailTitle:(NSString * _Nullable)detailTitle buttonTitle:(NSString * _Nullable)buttonTitle tapBlock:(void(^)(void))tapBlock;
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView imageName:(NSString * _Nullable)imageName title:(NSString *)title detailTitle:(NSString * _Nullable)detailTitle buttonTitle:(NSString * _Nullable)buttonTitle centerY:(CGFloat)centerY tapBlock:(void(^)(void))tapBlock;
|
||||
|
||||
// 取消cell左滑删除状态
|
||||
- (void)cancleTableViewCellEditingState;
|
||||
|
||||
#pragma mark - 导航栏设置
|
||||
// 隐藏导航条
|
||||
- (void)hiddenNavigationBar:(BOOL)hidden;
|
||||
|
||||
// 设置导航条背景色
|
||||
- (void)setNavigationBarBackgroundColor:(UIColor *)color;
|
||||
|
||||
// 设置导航条标题
|
||||
- (void)setNavigationBarTitle:(NSString *)title;
|
||||
|
||||
// 设置导航条标题颜色
|
||||
- (void)setNavigationBarTintColor:(UIColor *)tintColor;
|
||||
|
||||
// 隐藏返回按钮
|
||||
- (void)hiddenNavigationBarLeftButton;
|
||||
|
||||
// 设置导航栏右侧按钮
|
||||
- (void)setNavigationBarRightButton:(UIButton *)rightButton;
|
||||
|
||||
// 设置导航栏左侧按钮
|
||||
- (void)setNavigationBarLeftButton:(UIButton *)leftButton;
|
||||
|
||||
// 设置导航栏线条 - 无
|
||||
- (void)hiddenSeparator;
|
||||
|
||||
// 设置导航栏线条 - 细
|
||||
- (void)setNavSmallSeparator;
|
||||
|
||||
// 设置导航栏线条 - 粗
|
||||
- (void)setNavLargeSeparator;
|
||||
|
||||
// 隐藏home条
|
||||
- (void)hiddenHomeIndicator;
|
||||
|
||||
// 显示home条
|
||||
- (void)showHomeIndicator;
|
||||
|
||||
#pragma mark - 状态栏设置
|
||||
// 状态条白色
|
||||
- (void)setStatusBarLightContentStyle;
|
||||
|
||||
// 设置状态条黑色
|
||||
- (void)setStatusBarDefaultStyle;
|
||||
|
||||
#pragma mark - 接口数据缓存
|
||||
- (void)asyncCacheNetworkWithURLString:(NSString *)URLString response:(NSDictionary *)response;
|
||||
|
||||
- (NSDictionary *)getCacheWithURLString:(NSString *)URLString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,374 @@
|
||||
//
|
||||
// TFBasicViewController.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/11/21.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicViewController.h"
|
||||
#import "XHNetworkCache.h"
|
||||
|
||||
@interface TFBasicViewController ()
|
||||
{
|
||||
BOOL hiddenHomeIndicator;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TFBasicViewController
|
||||
|
||||
#pragma mark - 属性
|
||||
- (NSMutableArray *)dataSourceArray
|
||||
{
|
||||
if (!_dataSourceArray) {
|
||||
_dataSourceArray = [NSMutableArray array];
|
||||
}
|
||||
return _dataSourceArray;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)advertDict
|
||||
{
|
||||
if (!_advertDict) {
|
||||
_advertDict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return _advertDict;
|
||||
}
|
||||
|
||||
- (int)currentPageNumber
|
||||
{
|
||||
if (!_currentPageNumber) {
|
||||
_currentPageNumber = 1;
|
||||
}
|
||||
return _currentPageNumber;
|
||||
}
|
||||
|
||||
- (CGFloat)pageViewHeight
|
||||
{
|
||||
#if TF_Enable_PageControl
|
||||
_pageViewHeight = 44;
|
||||
#else
|
||||
_pageViewHeight = 0;
|
||||
#endif
|
||||
|
||||
if ([TFUtilsHelper getSiteState].count <= 1) {
|
||||
_pageViewHeight = 0;
|
||||
}
|
||||
|
||||
return _pageViewHeight;
|
||||
}
|
||||
|
||||
#pragma mark - 控件
|
||||
- (TFBasicNavBarView *)navigationBar
|
||||
{
|
||||
if (!_navigationBar) {
|
||||
_navigationBar = [[TFBasicNavBarView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, SCREEN_WIDTH, PUB_NAVBAR_HEIGHT)];
|
||||
_navigationBar.navCurrentController = self;
|
||||
_navigationBar.backgroundColor = [UIColor whiteColor];
|
||||
_navigationBar.userInteractionEnabled = YES;
|
||||
[_navigationBar setSmallSeparator];
|
||||
}
|
||||
return _navigationBar;
|
||||
}
|
||||
|
||||
- (UITableView *)mainTableView
|
||||
{
|
||||
if (!_mainTableView) {
|
||||
_mainTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_mainTableView.backgroundColor = [UIColor clearColor];
|
||||
_mainTableView.showsVerticalScrollIndicator = NO;
|
||||
_mainTableView.showsHorizontalScrollIndicator = NO;
|
||||
_mainTableView.estimatedRowHeight = 100;
|
||||
_mainTableView.sectionFooterHeight = 10;
|
||||
_mainTableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_mainTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
return _mainTableView;
|
||||
}
|
||||
|
||||
- (UITableView *)mainTableViewGroup
|
||||
{
|
||||
if (!_mainTableViewGroup) {
|
||||
_mainTableViewGroup = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
|
||||
_mainTableViewGroup.backgroundColor = [UIColor clearColor];
|
||||
_mainTableViewGroup.showsVerticalScrollIndicator = NO;
|
||||
_mainTableViewGroup.showsHorizontalScrollIndicator = NO;
|
||||
_mainTableViewGroup.estimatedRowHeight = 100;
|
||||
_mainTableViewGroup.sectionFooterHeight = 10;
|
||||
_mainTableViewGroup.rowHeight = UITableViewAutomaticDimension;
|
||||
_mainTableViewGroup.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_mainTableViewGroup.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
return _mainTableViewGroup;
|
||||
}
|
||||
|
||||
- (UICollectionView *)mainCollectionView
|
||||
{
|
||||
if (!_mainCollectionView) {
|
||||
_mainCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.mainCollectionViewFlowLayout];
|
||||
_mainCollectionView.userInteractionEnabled = YES;
|
||||
_mainCollectionView.backgroundColor = [UIColor clearColor];
|
||||
_mainCollectionView.showsVerticalScrollIndicator = NO;
|
||||
_mainCollectionView.showsHorizontalScrollIndicator = NO;
|
||||
_mainCollectionView.alwaysBounceVertical = YES;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_mainCollectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
}
|
||||
return _mainCollectionView;
|
||||
}
|
||||
|
||||
- (UICollectionViewFlowLayout *)mainCollectionViewFlowLayout
|
||||
{
|
||||
if (!_mainCollectionViewFlowLayout) {
|
||||
_mainCollectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
_mainCollectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
||||
}
|
||||
return _mainCollectionViewFlowLayout;
|
||||
}
|
||||
|
||||
- (SGPageTitleViewConfigure *)pageConfigure
|
||||
{
|
||||
if (!_pageConfigure) {
|
||||
_pageConfigure = [SGPageTitleViewConfigure pageTitleViewConfigure];
|
||||
_pageConfigure.bounces = NO;
|
||||
_pageConfigure.showBottomSeparator = YES;
|
||||
_pageConfigure.bottomSeparatorColor = kGrayLineColor;
|
||||
_pageConfigure.bottomSeparatorColor = [UIColor clearColor];
|
||||
_pageConfigure.titleFont = kFont15;
|
||||
_pageConfigure.titleColor = kBlackColor;
|
||||
_pageConfigure.titleSelectedColor = kMainColor;
|
||||
_pageConfigure.indicatorDynamicWidth = 10;
|
||||
_pageConfigure.indicatorStyle = SGIndicatorStyleDynamic;
|
||||
_pageConfigure.indicatorHeight = 4;
|
||||
_pageConfigure.indicatorCornerRadius = 2;
|
||||
_pageConfigure.indicatorToBottomDistance = 5;
|
||||
_pageConfigure.indicatorColor = kMainColor;
|
||||
}
|
||||
return _pageConfigure;
|
||||
}
|
||||
|
||||
#pragma mark - 公共方法
|
||||
// 返回上一页
|
||||
- (void)popViewController
|
||||
{
|
||||
[self.navigationBar popViewController];
|
||||
}
|
||||
|
||||
// 是否可右滑返回
|
||||
- (void)navigationCanSlidingBack:(BOOL)canSlidingBack;
|
||||
{
|
||||
if (self.navigationController) {
|
||||
((TFNavigationController *)(self.navigationController)).enableSlidingBack = canSlidingBack;
|
||||
}
|
||||
}
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *)title tapBlock:(void(^)(void))tapBlock
|
||||
{
|
||||
[self setEmptyOnView:scrollView imageName:@"" title:title detailTitle:@"" buttonTitle:@"" centerY:-1 tapBlock:tapBlock];
|
||||
}
|
||||
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *)title centerY:(CGFloat)centerY tapBlock:(void(^)(void))tapBlock {
|
||||
[self setEmptyOnView:scrollView imageName:@"" title:title detailTitle:@"" buttonTitle:@"" centerY:200 tapBlock:tapBlock];
|
||||
}
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *)title buttonTitle:(NSString *)buttonTitle tapBlock:(void(^)(void))tapBlock
|
||||
{
|
||||
[self setEmptyOnView:scrollView imageName:@"" title:title detailTitle:@"" buttonTitle:buttonTitle centerY:-1 tapBlock:tapBlock];
|
||||
}
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView title:(NSString *)title detailTitle:(NSString *)detailTitle buttonTitle:(NSString *)buttonTitle tapBlock:(void(^)(void))tapBlock
|
||||
{
|
||||
[self setEmptyOnView:scrollView imageName:@"" title:title detailTitle:detailTitle buttonTitle:buttonTitle centerY:-1 tapBlock:tapBlock];
|
||||
}
|
||||
|
||||
// 空白占位图
|
||||
- (void)setEmptyOnView:(UIScrollView *)scrollView imageName:(NSString *)imageName title:(NSString *)title detailTitle:(NSString *)detailTitle buttonTitle:(NSString *)buttonTitle centerY:(CGFloat)centerY tapBlock:(void(^)(void))tapBlock
|
||||
{
|
||||
if ([imageName isEqualToString:@""]) {
|
||||
imageName = @"public_no_data.png";
|
||||
}
|
||||
|
||||
TFEmptyView *emptyView = [TFEmptyView emptyActionViewWithImage:imageName title:title detail:detailTitle btnTitle:buttonTitle btnClickBlock:^{
|
||||
if (tapBlock) {
|
||||
tapBlock();
|
||||
}
|
||||
}];
|
||||
centerY = centerY == -1 ? 100 : centerY;
|
||||
emptyView.contentViewY = centerY;
|
||||
emptyView.imageSize = CGSizeMake(200, 200);
|
||||
emptyView.autoShowEmptyView = NO;
|
||||
emptyView.titleLabFont = kMainFont;
|
||||
emptyView.titleLabTextColor = kGrayTextColor;
|
||||
emptyView.promptImageView.tintColor = kMainColor;
|
||||
emptyView.actionBtnBorderWidth = 1;
|
||||
emptyView.actionBtnBorderColor = kMainColor;
|
||||
emptyView.actionBtnTitleColor = kMainColor;
|
||||
emptyView.actionBtnHeight = 35;
|
||||
emptyView.actionBtnHorizontalMargin = 20;
|
||||
scrollView.xtfei_emptyView = emptyView;
|
||||
|
||||
[scrollView xtfei_startLoading];
|
||||
|
||||
_emptyView = emptyView;
|
||||
}
|
||||
|
||||
// 取消cell左滑删除状态
|
||||
- (void)cancleTableViewCellEditingState
|
||||
{
|
||||
if (self.mainTableView.isEditing) {
|
||||
[self.mainTableView setEditing:NO animated:YES];
|
||||
}
|
||||
|
||||
if (self.mainTableViewGroup.isEditing) {
|
||||
[self.mainTableViewGroup setEditing:NO animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 导航栏设置
|
||||
- (void)hiddenNavigationBar:(BOOL)hidden
|
||||
{
|
||||
_navigationBar.hidden = hidden;
|
||||
|
||||
if (hidden) {
|
||||
[self hiddenSeparator];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNavigationBarBackgroundColor:(UIColor *)color
|
||||
{
|
||||
[_navigationBar setBackgroundColor:color];
|
||||
}
|
||||
|
||||
- (void)setNavigationBarTitle:(NSString *)title
|
||||
{
|
||||
[_navigationBar setNavigationBarTitle:title];
|
||||
}
|
||||
|
||||
- (void)setNavigationBarTintColor:(UIColor *)tintColor
|
||||
{
|
||||
[_navigationBar setNavigationBarTintColor:tintColor];
|
||||
}
|
||||
|
||||
- (void)hiddenNavigationBarLeftButton
|
||||
{
|
||||
[_navigationBar hiddenLeftBarButton];
|
||||
}
|
||||
|
||||
- (void)setNavigationBarRightButton:(UIButton *)rightButton
|
||||
{
|
||||
[_navigationBar setRightBarButton:rightButton];
|
||||
}
|
||||
|
||||
// 设置导航栏左侧按钮
|
||||
- (void)setNavigationBarLeftButton:(UIButton *)leftButton
|
||||
{
|
||||
[_navigationBar setLeftBarButton:leftButton];
|
||||
}
|
||||
|
||||
- (void)hiddenSeparator
|
||||
{
|
||||
[_navigationBar hiddenSeparator];
|
||||
}
|
||||
|
||||
- (void)setNavSmallSeparator
|
||||
{
|
||||
[_navigationBar setSmallSeparator];
|
||||
}
|
||||
|
||||
- (void)setNavLargeSeparator
|
||||
{
|
||||
[_navigationBar setLargeSeparator];
|
||||
}
|
||||
|
||||
#pragma mark - 状态栏设置
|
||||
- (void)setStatusBarLightContentStyle
|
||||
{
|
||||
[TFViewHelper setStateBarLightStyle];
|
||||
}
|
||||
|
||||
- (void)setStatusBarDefaultStyle
|
||||
{
|
||||
[TFViewHelper setStateBarDefaultStyle];
|
||||
}
|
||||
|
||||
// 隐藏home条
|
||||
- (void)hiddenHomeIndicator
|
||||
{
|
||||
if (is_iPhoneX) {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
hiddenHomeIndicator = YES;
|
||||
[self prefersHomeIndicatorAutoHidden];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 显示home条
|
||||
- (void)showHomeIndicator
|
||||
{
|
||||
if (is_iPhoneX) {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
hiddenHomeIndicator = NO;
|
||||
[self prefersHomeIndicatorAutoHidden];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 接口数据缓存
|
||||
- (void)asyncCacheNetworkWithURLString:(NSString *)URLString response:(NSDictionary *)response
|
||||
{
|
||||
[XHNetworkCache save_asyncJsonResponseToCacheFile:response andURL:URLString completed:^(BOOL result) {
|
||||
if (result) {
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)getCacheWithURLString:(NSString *)URLString
|
||||
{
|
||||
return [XHNetworkCache cacheJsonWithURL:URLString];
|
||||
}
|
||||
|
||||
#pragma mark - 生命周期
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = kWhiteColor;
|
||||
|
||||
[self.view addSubview:self.navigationBar];
|
||||
|
||||
[self hiddenSeparator];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.navigationBar && !self.navigationBar.hidden) {
|
||||
[self.view bringSubviewToFront:self.navigationBar];
|
||||
}
|
||||
((TFNavigationController *)(self.navigationController)).enableSlidingBack = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[TFPromptManager hiddenAlert];
|
||||
}
|
||||
|
||||
- (BOOL)prefersHomeIndicatorAutoHidden
|
||||
{
|
||||
return hiddenHomeIndicator;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TFNavigationController.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFNavigationController : UINavigationController
|
||||
|
||||
@property (nonatomic ,assign) BOOL enableSlidingBack;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// TFNavigationController.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFNavigationController.h"
|
||||
#import "UINavigationController+TFExtension.h"
|
||||
|
||||
@interface TFNavigationController ()<UINavigationControllerDelegate>
|
||||
|
||||
@property (nonatomic ,assign) BOOL isSwitching;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFNavigationController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.delegate = self;
|
||||
|
||||
// 使导航条有效
|
||||
[self setNavigationBarHidden:NO];
|
||||
[self.navigationBar setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)setEnableSlidingBack:(BOOL)enableSlidingBack
|
||||
{
|
||||
_enableSlidingBack = enableSlidingBack;
|
||||
|
||||
self.interactivePopGestureRecognizer.enabled = enableSlidingBack;
|
||||
}
|
||||
|
||||
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
if (self.viewControllers.count > 0 && !self.isTransition) {
|
||||
viewController.hidesBottomBarWhenPushed = YES;
|
||||
}
|
||||
|
||||
if (animated) {
|
||||
if (self.isSwitching) {
|
||||
return; // 1. 如果是动画,并且正在切换,直接忽略
|
||||
}
|
||||
self.isSwitching = YES; // 2. 否则修改状态
|
||||
}
|
||||
|
||||
[super pushViewController:viewController animated:animated];
|
||||
|
||||
self.isSwitching = NO;
|
||||
self.isTransition = NO;
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
BOOL isRootVC = viewController == navigationController.viewControllers.firstObject;
|
||||
|
||||
if (self.enableSlidingBack) {
|
||||
self.enableSlidingBack = !isRootVC;
|
||||
}
|
||||
self.isSwitching = NO; // 3. 还原状态
|
||||
}
|
||||
|
||||
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
|
||||
{
|
||||
self.isSwitching = NO;
|
||||
|
||||
return [super popViewControllerAnimated:animated];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TFTabbarViewHelper.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CYLTabBarController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFTabbarViewHelper : NSObject
|
||||
|
||||
@property (nonatomic ,strong ,readonly) CYLTabBarController *tabBarController;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// TFTabbarViewHelper.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFTabbarViewHelper.h"
|
||||
#import "TFBookRackViewController.h"
|
||||
#import "TFBookStoreViewController.h"
|
||||
#import "TFDiscoverViewController.h"
|
||||
#import "TFMineViewController.h"
|
||||
|
||||
@interface TFTabbarViewHelper () <UITabBarControllerDelegate>
|
||||
|
||||
@property (nonatomic ,strong ,readwrite) CYLTabBarController *tabBarController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFTabbarViewHelper
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
// 隐藏tabbar
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hiddenTabbar:) name:Notification_Hidden_Tabbar object:nil];
|
||||
|
||||
// 显示tabbar
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showTabbar:) name:Notification_Show_Tabbar object:nil];
|
||||
|
||||
// 改变审核状态
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeMagicState:) name:Notification_Review_State object:nil];
|
||||
|
||||
// 改变tabbar选项卡
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changTabbarIndex:) name:Notification_Change_Tabbar_Index object:nil];
|
||||
|
||||
// 切换语言状态
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(switchLanguage) name:Notification_Switch_Language object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CYLTabBarController *)tabBarController
|
||||
{
|
||||
if (!_tabBarController) {
|
||||
CYLTabBarController *tabBarController = nil;
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
tabBarController = [CYLTabBarController tabBarControllerWithViewControllers:self.viewControllers tabBarItemsAttributes:self.tabBarItemsAttributesForController imageInsets:UIEdgeInsetsMake(1, 0, 1, 0) titlePositionAdjustment:UIOffsetMake(0, -3)];
|
||||
[[UITabBar appearance] setUnselectedItemTintColor:kColorRGBA(217, 217, 217, 1)];
|
||||
[[UITabBar appearance] setTintColor:kMainColor];
|
||||
|
||||
} else {
|
||||
tabBarController = [CYLTabBarController tabBarControllerWithViewControllers:self.viewControllers tabBarItemsAttributes:self.tabBarItemsAttributesForController imageInsets:UIEdgeInsetsMake(0, 0, 0, 0) titlePositionAdjustment:UIOffsetMake(0, -3)];
|
||||
[[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:kMainColor} forState:UIControlStateSelected];
|
||||
[[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:kColorRGBA(217, 217, 217, 1)} forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
tabBarController.tabBarHeight = PUB_TABBAR_HEIGHT;
|
||||
tabBarController.tabBar.translucent = YES;
|
||||
tabBarController.delegate = self;
|
||||
tabBarController.tabBar.clipsToBounds = YES;
|
||||
[[UITabBar appearance] setShadowImage:[[UIImage alloc] init]];
|
||||
[[UITabBar appearance] setBackgroundImage:[TFViewHelper scaleImage:[UIImage imageNamed:@"tabbar_background"] toScale:1]];
|
||||
|
||||
if (![[NSUserDefaults standardUserDefaults] objectForKey:TF_TABBAR_SELECT_MEMORY]) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:1] forKey:TF_TABBAR_SELECT_MEMORY];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
NSNumber *selectIndex = [[NSUserDefaults standardUserDefaults] objectForKey:TF_TABBAR_SELECT_MEMORY];
|
||||
tabBarController.selectedIndex = [selectIndex integerValue];
|
||||
|
||||
_tabBarController = tabBarController;
|
||||
}
|
||||
return _tabBarController;
|
||||
}
|
||||
|
||||
- (NSArray *)tabBarItemsAttributesForController
|
||||
{
|
||||
NSMutableArray *tabbarItems = [NSMutableArray array];
|
||||
|
||||
for (int i = 0; i < self.titleArr.count; i ++) {
|
||||
[tabbarItems addObject:@{
|
||||
CYLTabBarItemTitle : self.titleArr[i],
|
||||
CYLTabBarItemImage : self.itemImageArr[i],
|
||||
CYLTabBarItemSelectedImage : self.itemImageSelectedArr[i],
|
||||
}];
|
||||
}
|
||||
|
||||
NSArray *tabBarItemsAttributes = [tabbarItems copy];
|
||||
return tabBarItemsAttributes;
|
||||
}
|
||||
|
||||
- (NSArray *)titleArr
|
||||
{
|
||||
return @[TFLocalizedString(@"书架"), TFLocalizedString(@"书城"), TFLocalizedString(@"发现"), TFLocalizedString(@"我的")];
|
||||
}
|
||||
|
||||
- (NSArray *)itemImageArr
|
||||
{
|
||||
return @[@"bookRack", @"bookMall", @"discover", @"mine"];
|
||||
}
|
||||
|
||||
- (NSArray *)itemImageSelectedArr
|
||||
{
|
||||
return @[@"bookRack_select", @"bookMall_select", @"discover_select", @"mine_select"];
|
||||
}
|
||||
|
||||
- (NSArray *)viewControllers
|
||||
{
|
||||
NSArray *t_viewControllers;
|
||||
|
||||
TFBookRackViewController *bookRackViewController = [[TFBookRackViewController alloc] init];
|
||||
TFBookStoreViewController *bookMallViewController = [[TFBookStoreViewController alloc] init];
|
||||
TFDiscoverViewController *discoverViewController = [[TFDiscoverViewController alloc] init];
|
||||
TFMineViewController *mineViewController = [[TFMineViewController alloc] init];
|
||||
|
||||
t_viewControllers = @[bookRackViewController, bookMallViewController, discoverViewController, mineViewController];
|
||||
|
||||
NSMutableArray *t_navigationControllers = [NSMutableArray array];
|
||||
|
||||
for (UIViewController *t_viewController in t_viewControllers) {
|
||||
|
||||
UINavigationController *t_navigationController = [[TFNavigationController alloc] initWithRootViewController:t_viewController];
|
||||
[t_navigationControllers addObject:t_navigationController];
|
||||
}
|
||||
|
||||
return [t_navigationControllers copy];
|
||||
}
|
||||
|
||||
- (void)tabBarController:(UITabBarController *)tabBarController didSelectControl:(UIControl *)control
|
||||
{
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
|
||||
[self saveTabbarSelectedIndex:tabBarController.selectedIndex];
|
||||
}
|
||||
|
||||
- (void)saveTabbarSelectedIndex:(NSUInteger)selectedIndex
|
||||
{
|
||||
if (selectedIndex == 0 || selectedIndex == 1) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedInteger:selectedIndex] forKey:TF_TABBAR_SELECT_MEMORY];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
// 隐藏tabbar
|
||||
- (void)hiddenTabbar:(NSNotification *)notification
|
||||
{
|
||||
self.tabBarController.tabBar.hidden = YES;
|
||||
|
||||
NSString *animationState = [NSString stringWithFormat:@"%@", notification.object];
|
||||
|
||||
if ([animationState isEqualToString:@"1"]) {
|
||||
self.tabBarController.tabBar.frame = CGRectMake(0, SCREEN_HEIGHT, self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.size.height);
|
||||
} else {
|
||||
[UIView animateWithDuration:kAnimatedDuration animations:^{
|
||||
self.tabBarController.tabBar.frame = CGRectMake(0, SCREEN_HEIGHT, self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.size.height);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 显示tabbar
|
||||
- (void)showTabbar:(NSNotification *)notification
|
||||
{
|
||||
NSString *animationState = [NSString stringWithFormat:@"%@", notification.object];
|
||||
self.tabBarController.tabBar.hidden = NO;
|
||||
|
||||
if ([animationState isEqualToString:@"animation"]) {
|
||||
[UIView animateWithDuration:kAnimatedDuration animations:^{
|
||||
self.tabBarController.tabBar.frame = CGRectMake(0, SCREEN_HEIGHT - self.tabBarController.tabBar.frame.size.height, self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.size.height);
|
||||
}];
|
||||
} else {
|
||||
self.tabBarController.tabBar.frame = CGRectMake(0, SCREEN_HEIGHT - self.tabBarController.tabBar.frame.size.height, self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
// 改变tabbar选项卡位置
|
||||
- (void)changTabbarIndex:(NSNotification *)notification
|
||||
{
|
||||
[self.tabBarController setSelectedIndex:[notification.object integerValue]];
|
||||
|
||||
NSUInteger selectIndex = [notification.object integerValue];
|
||||
[self saveTabbarSelectedIndex:selectIndex];
|
||||
}
|
||||
|
||||
// 改变tabbar结构
|
||||
- (void)changeMagicState:(NSNotification *)notification
|
||||
{
|
||||
// 如果本地状态与通知状态相同,就不更改界面
|
||||
if ([TFSystemInfoManager.magicStatus isEqualToString:notification.object]) {
|
||||
return;
|
||||
}
|
||||
|
||||
TFSystemInfoManager.magicStatus = notification.object;
|
||||
|
||||
self.tabBarController = nil;
|
||||
[kMainWindow setRootViewController:self.tabBarController];
|
||||
}
|
||||
|
||||
- (void)switchLanguage
|
||||
{
|
||||
if (![NSThread isMainThread]) {
|
||||
[self performSelectorOnMainThread:@selector(switchLanguage) withObject:nil waitUntilDone:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
self.tabBarController = nil;
|
||||
kMainWindow.rootViewController = self.tabBarController;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Change_Tabbar_Index object:@"1"];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// TFBasicNavBarView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFBasicNavBarView : UIView
|
||||
|
||||
@property (nonatomic ,weak) UIViewController *navCurrentController;
|
||||
// 标题
|
||||
@property (nonatomic ,strong) UILabel *navTitleLabel;
|
||||
@property (nonatomic ,assign) BOOL isPresentState;
|
||||
// 透过点击
|
||||
@property (nonatomic ,assign) BOOL touchEnabled;
|
||||
/**
|
||||
隐藏返回按钮
|
||||
*/
|
||||
- (void)hiddenLeftBarButton;
|
||||
|
||||
/**
|
||||
白色返回按钮
|
||||
*/
|
||||
- (void)setLightLeftButton;
|
||||
|
||||
/**
|
||||
设置返回按钮颜色
|
||||
*/
|
||||
- (void)setLeftButtonTintColor:(UIColor *)tintColor;
|
||||
|
||||
/**
|
||||
设置导航栏左侧按钮
|
||||
*/
|
||||
- (void)setLeftBarButton:(UIButton *)leftButton;
|
||||
|
||||
/**
|
||||
设置导航栏右侧按钮
|
||||
*/
|
||||
- (void)setRightBarButton:(UIButton *)rightButton;
|
||||
|
||||
/**
|
||||
分割线(细)
|
||||
*/
|
||||
- (void)setSmallSeparator;
|
||||
|
||||
/**
|
||||
分割线(粗)
|
||||
*/
|
||||
- (void)setLargeSeparator;
|
||||
|
||||
/**
|
||||
分割线(无)
|
||||
*/
|
||||
- (void)hiddenSeparator;
|
||||
|
||||
/**
|
||||
设置导航栏标题
|
||||
*/
|
||||
- (void)setNavigationBarTitle:(NSString *)title;
|
||||
|
||||
|
||||
/**
|
||||
设置导航栏标题颜色
|
||||
*/
|
||||
- (void)setNavigationBarTintColor:(UIColor *)tintColor;
|
||||
|
||||
|
||||
/**
|
||||
设置导航栏标题字号
|
||||
*/
|
||||
- (void)setNavigationBarTintFont:(UIFont *)font;
|
||||
|
||||
/*
|
||||
返回界面
|
||||
**/
|
||||
- (void)popViewController;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,224 @@
|
||||
//
|
||||
// TFBasicNavBarView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicNavBarView.h"
|
||||
#import "WXYZ_AudioSettingHelper.h"
|
||||
|
||||
@interface TFBasicNavBarView ()
|
||||
// 默认返回按钮
|
||||
@property (nonatomic ,strong) UIButton *defaultLeftButton;
|
||||
// 导航栏边线
|
||||
@property (nonatomic ,strong) UIImageView *navBottomLine;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFBasicNavBarView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self createSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubViews
|
||||
{
|
||||
[self addSubview:self.navTitleLabel];
|
||||
[self addSubview:self.defaultLeftButton];
|
||||
[self addSubview:self.navBottomLine];
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
/**
|
||||
隐藏返回按钮
|
||||
*/
|
||||
- (void)hiddenLeftBarButton
|
||||
{
|
||||
self.defaultLeftButton.hidden = YES;
|
||||
}
|
||||
|
||||
/**
|
||||
白色返回按钮
|
||||
*/
|
||||
- (void)setLightLeftButton
|
||||
{
|
||||
[self.defaultLeftButton setTintColor:kWhiteColor];
|
||||
}
|
||||
|
||||
/**
|
||||
设置返回按钮颜色
|
||||
*/
|
||||
- (void)setLeftButtonTintColor:(UIColor *)tintColor
|
||||
{
|
||||
[self.defaultLeftButton setTintColor:tintColor];
|
||||
}
|
||||
|
||||
/**
|
||||
设置导航栏左侧按钮
|
||||
*/
|
||||
- (void)setLeftBarButton:(UIButton *)leftButton
|
||||
{
|
||||
if (self.defaultLeftButton) {
|
||||
[self.defaultLeftButton removeFromSuperview];
|
||||
}
|
||||
|
||||
[self addSubview:leftButton];
|
||||
}
|
||||
|
||||
/**
|
||||
设置导航栏右侧按钮
|
||||
*/
|
||||
- (void)setRightBarButton:(UIButton *)rightButton
|
||||
{
|
||||
[self addSubview:rightButton];
|
||||
}
|
||||
|
||||
/**
|
||||
分割线(细)
|
||||
*/
|
||||
- (void)setSmallSeparator
|
||||
{
|
||||
self.navBottomLine.frame = CGRectMake(0, PUB_NAVBAR_HEIGHT, SCREEN_WIDTH, 1);
|
||||
self.navBottomLine.hidden = NO;
|
||||
}
|
||||
|
||||
/**
|
||||
分割线(粗)
|
||||
*/
|
||||
- (void)setLargeSeparator
|
||||
{
|
||||
self.navBottomLine.frame = CGRectMake(0, PUB_NAVBAR_HEIGHT, SCREEN_WIDTH, 5);
|
||||
self.navBottomLine.hidden = NO;
|
||||
}
|
||||
|
||||
/**
|
||||
分割线(无)
|
||||
*/
|
||||
- (void)hiddenSeparator
|
||||
{
|
||||
self.navBottomLine.frame = CGRectMake(0, PUB_NAVBAR_HEIGHT, SCREEN_WIDTH, 0);
|
||||
self.navBottomLine.hidden = YES;
|
||||
}
|
||||
|
||||
/**
|
||||
设置导航栏标题
|
||||
*/
|
||||
- (void)setNavigationBarTitle:(NSString *)title
|
||||
{
|
||||
[self.navTitleLabel setText:title];
|
||||
self.navTitleLabel.frame = CGRectMake((SCREEN_WIDTH - 240) / 2.0, PUB_NAVBAR_OFFSET + 20, 240, 44);
|
||||
}
|
||||
|
||||
/**
|
||||
设置导航栏标题颜色
|
||||
*/
|
||||
- (void)setNavigationBarTintColor:(UIColor *)tintColor
|
||||
{
|
||||
[self.navTitleLabel setTextColor:tintColor];
|
||||
}
|
||||
|
||||
/**
|
||||
设置导航栏标题字号
|
||||
*/
|
||||
- (void)setNavigationBarTintFont:(UIFont *)font
|
||||
{
|
||||
[self.navTitleLabel setFont:font];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
[super setBackgroundColor:backgroundColor];
|
||||
self.navTitleLabel.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (UILabel *)navTitleLabel
|
||||
{
|
||||
if (!_navTitleLabel) {
|
||||
_navTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_navTitleLabel.backgroundColor = [UIColor clearColor];
|
||||
_navTitleLabel.textColor = [UIColor blackColor];
|
||||
_navTitleLabel.numberOfLines = 1;
|
||||
_navTitleLabel.font = kFont16;
|
||||
_navTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
}
|
||||
return _navTitleLabel;
|
||||
}
|
||||
|
||||
- (UIButton *)defaultLeftButton
|
||||
{
|
||||
if (!_defaultLeftButton) {
|
||||
_defaultLeftButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_defaultLeftButton.backgroundColor = [UIColor clearColor];
|
||||
_defaultLeftButton.frame = CGRectMake(kHalfMargin, PUB_NAVBAR_OFFSET + 20, 44, 44);
|
||||
_defaultLeftButton.adjustsImageWhenHighlighted = NO;
|
||||
[_defaultLeftButton.titleLabel setFont:kMainFont];
|
||||
[_defaultLeftButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
|
||||
[_defaultLeftButton setImageEdgeInsets:UIEdgeInsetsMake(12, 6, 12, 18)];
|
||||
[_defaultLeftButton setImage:[[UIImage imageNamed:@"public_back"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
[_defaultLeftButton setTintColor:kBlackColor];
|
||||
[_defaultLeftButton addTarget:self action:@selector(popViewController) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _defaultLeftButton;
|
||||
}
|
||||
|
||||
- (UIImageView *)navBottomLine
|
||||
{
|
||||
if (!_navBottomLine) {
|
||||
_navBottomLine = [[UIImageView alloc] initWithFrame:CGRectMake(0, PUB_NAVBAR_HEIGHT, SCREEN_WIDTH, 5)];
|
||||
_navBottomLine.userInteractionEnabled = YES;
|
||||
_navBottomLine.image = [UIImage imageNamed:@"navbar_bottom_line"];
|
||||
}
|
||||
return _navBottomLine;
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
UIView *hitView = [super hitTest:point withEvent:event];
|
||||
if (self.touchEnabled && hitView == self) {
|
||||
return nil;
|
||||
}
|
||||
return hitView;
|
||||
}
|
||||
|
||||
- (void)popViewController
|
||||
{
|
||||
BOOL pop = NO;
|
||||
NSArray *viewcontrollers = self.navCurrentController.navigationController.viewControllers;
|
||||
if (viewcontrollers.count > 1) {
|
||||
if ([viewcontrollers objectAtIndex:viewcontrollers.count - 1] == self) {
|
||||
pop = YES;
|
||||
[self.navCurrentController.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
} else {
|
||||
if ([self.navCurrentController isKindOfClass:[NSClassFromString(@"WXYZ_BookAiPlayPageViewController") class]]) {
|
||||
[[WXYZ_AudioSettingHelper sharedManager] playPageViewShow:NO productionType:TFProductionTypeAi];
|
||||
}
|
||||
|
||||
if ([self.navCurrentController isKindOfClass:[NSClassFromString(@"TFAudioPlayViewController") class]]) {
|
||||
[[WXYZ_AudioSettingHelper sharedManager] playPageViewShow:NO productionType:TFProductionTypeAudio];
|
||||
}
|
||||
|
||||
[self.navCurrentController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
if (!pop) {
|
||||
[self.navCurrentController.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_navCurrentController) {
|
||||
[_navCurrentController willMoveToParentViewController:nil];
|
||||
[_navCurrentController.view removeFromSuperview];
|
||||
[_navCurrentController removeFromParentViewController];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TFBasicTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFBasicTableViewCell : UITableViewCell
|
||||
|
||||
@property (nonatomic ,strong) NSIndexPath *index;
|
||||
@property (nonatomic ,assign) BOOL hiddenEndLine;
|
||||
@property (nonatomic ,strong) UIView *lineView;
|
||||
|
||||
@property (nonatomic ,assign) TFProductionType productionType;
|
||||
|
||||
- (void)createSubviews;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// TFBasicTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
|
||||
@implementation TFBasicTableViewCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
self.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
self.lineView = [[UIView alloc] init];
|
||||
self.lineView.backgroundColor = kGrayLineColor;
|
||||
self.lineView.hidden = YES;
|
||||
[self.contentView addSubview:self.lineView];
|
||||
|
||||
[self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(kMargin);
|
||||
make.width.mas_equalTo(SCREEN_WIDTH - kMargin);
|
||||
make.height.mas_equalTo(kCellLineHeight);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom).with.offset(-kCellLineHeight);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setIndex:(NSIndexPath *)index
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
- (void)setHiddenEndLine:(BOOL)hiddenEndLine
|
||||
{
|
||||
_hiddenEndLine = hiddenEndLine;
|
||||
|
||||
self.lineView.hidden = hiddenEndLine;
|
||||
}
|
||||
|
||||
- (void)setProductionType:(TFProductionType)productionType
|
||||
{
|
||||
_productionType = productionType;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TFProductionListViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TFTagboardView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFProductionListViewCell : TFBasicTableViewCell
|
||||
|
||||
@property (nonatomic ,strong) TFProductionModel *productionModel;
|
||||
// 是否来自榜单
|
||||
@property (nonatomic ,assign) BOOL isRankList;
|
||||
// 书籍图片
|
||||
@property (nonatomic ,strong) TFProductionCoverView *coverView;
|
||||
@property (nonatomic ,strong) UILabel *titleLabel;
|
||||
@property (nonatomic ,strong) YYLabel *introductionLabel;
|
||||
@property (nonatomic ,strong) UILabel *authorLabel;
|
||||
@property (nonatomic ,strong) TFTagboardView *tagboardView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// TFProductionListViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFProductionListViewCell.h"
|
||||
|
||||
@interface TFProductionListViewCell ()
|
||||
|
||||
@property (nonatomic ,weak) UIImageView *indexImageView;
|
||||
@property (nonatomic ,weak) UILabel *indexLabel;
|
||||
@end
|
||||
|
||||
@implementation TFProductionListViewCell
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
self.backgroundColor = kWhiteColor;
|
||||
|
||||
// 图片
|
||||
self.coverView = [[TFProductionCoverView alloc] initWithProductionType:TFProductionTypeNovel coverDirection:TFProductionCoverDirectionVertical];
|
||||
[self.contentView addSubview:self.coverView];
|
||||
|
||||
[self.coverView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.contentView.mas_left).with.offset(kHalfMargin);
|
||||
make.top.mas_equalTo(self.contentView.mas_top).with.offset(kHalfMargin);
|
||||
make.width.mas_equalTo(BOOK_WIDTH_SMALL);
|
||||
make.height.mas_equalTo(BOOK_HEIGHT_SMALL);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom).with.offset(- kHalfMargin).priorityLow();
|
||||
}];
|
||||
|
||||
UIImageView *indexImageView = [[UIImageView alloc] init];
|
||||
indexImageView.backgroundColor = [UIColor clearColor];
|
||||
indexImageView.hidden = YES;
|
||||
indexImageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[self.coverView addSubview:indexImageView];
|
||||
self.indexImageView = indexImageView;
|
||||
|
||||
[indexImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.coverView).offset(-kQuarterMargin);
|
||||
make.top.equalTo(self.coverView);
|
||||
make.size.mas_equalTo(CGSizeMake(24.0, 23.0));
|
||||
}];
|
||||
|
||||
UILabel *indexLabel = [[UILabel alloc] init];
|
||||
indexLabel.textColor = [UIColor whiteColor];
|
||||
indexLabel.font = kFont12;
|
||||
[indexImageView addSubview:indexLabel];
|
||||
self.indexLabel = indexLabel;
|
||||
|
||||
[indexLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.equalTo(indexImageView).offset(-2.0f);
|
||||
make.centerX.equalTo(indexImageView);
|
||||
}];
|
||||
|
||||
// 书名
|
||||
self.titleLabel = [[UILabel alloc] init];
|
||||
self.titleLabel.numberOfLines = 1;
|
||||
self.titleLabel.backgroundColor = kWhiteColor;
|
||||
self.titleLabel.font = kMainFont;
|
||||
self.titleLabel.textAlignment = NSTextAlignmentLeft;
|
||||
[self addSubview:self.titleLabel];
|
||||
|
||||
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.coverView.mas_right).with.offset(kHalfMargin);
|
||||
make.top.mas_equalTo(self.coverView.mas_top);
|
||||
make.right.mas_equalTo(self.contentView.mas_right).with.offset(- kHalfMargin);
|
||||
make.height.mas_equalTo(BOOK_CELL_TITLE_HEIGHT / 2);
|
||||
}];
|
||||
|
||||
// 作者
|
||||
self.authorLabel = [[UILabel alloc] init];
|
||||
self.authorLabel.numberOfLines = 1;
|
||||
self.authorLabel.backgroundColor = kWhiteColor;
|
||||
self.authorLabel.font = kFont11;
|
||||
self.authorLabel.textColor = kColorRGBA(176, 176, 177, 1);
|
||||
self.authorLabel.textAlignment = NSTextAlignmentLeft;
|
||||
[self addSubview:self.authorLabel];
|
||||
|
||||
[self.authorLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.titleLabel.mas_left);
|
||||
make.bottom.mas_equalTo(self.coverView.mas_bottom);
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
make.height.mas_equalTo(self.titleLabel.mas_height);
|
||||
}];
|
||||
|
||||
// 标签
|
||||
self.tagboardView = [[TFTagboardView alloc] init];
|
||||
self.tagboardView.font = kFont10;
|
||||
self.tagboardView.textAlignment = TFTagboardTextAlignmentRight;
|
||||
self.tagboardView.borderStyle = TFTagboardBorderStyleBorder;
|
||||
self.tagboardView.cornerRadius = 3;
|
||||
self.tagboardView.spacing = 10;
|
||||
[self addSubview:self.tagboardView];
|
||||
|
||||
[self.tagboardView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.authorLabel.mas_right).with.offset(kHalfMargin);
|
||||
make.right.mas_equalTo(self.titleLabel.mas_right);
|
||||
make.centerY.mas_equalTo(self.authorLabel.mas_centerY);
|
||||
make.height.mas_equalTo(self.authorLabel.mas_height);
|
||||
}];
|
||||
|
||||
// 简介
|
||||
self.introductionLabel = [[YYLabel alloc] init];
|
||||
self.introductionLabel.numberOfLines = 3;
|
||||
self.introductionLabel.textVerticalAlignment = YYTextVerticalAlignmentTop;
|
||||
self.introductionLabel.backgroundColor = kWhiteColor;
|
||||
self.introductionLabel.font = kFont13;
|
||||
self.introductionLabel.textColor = kColorRGBA(102, 102, 102, 1);
|
||||
self.introductionLabel.textAlignment = NSTextAlignmentLeft;
|
||||
self.introductionLabel.layer.masksToBounds = YES;
|
||||
[self addSubview:self.introductionLabel];
|
||||
|
||||
[self.introductionLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.titleLabel.mas_left);
|
||||
make.right.mas_equalTo(self.titleLabel.mas_right);
|
||||
make.top.mas_equalTo(self.titleLabel.mas_bottom).with.offset(kHalfMargin);
|
||||
make.bottom.mas_equalTo(self.authorLabel.mas_top).with.offset(- kHalfMargin);
|
||||
}];
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
_productionModel = productionModel;
|
||||
|
||||
NSString *imageName;
|
||||
switch ([productionModel.display_no integerValue]) {
|
||||
case 0:
|
||||
imageName = @"book_list_one";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
imageName = @"book_list_two";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
imageName = @"book_list_three";
|
||||
break;
|
||||
|
||||
default:
|
||||
imageName = @"book_list_other";
|
||||
break;
|
||||
}
|
||||
|
||||
self.indexImageView.image = [UIImage imageNamed:imageName];
|
||||
self.indexImageView.hidden = [productionModel.display_no integerValue] > 19;
|
||||
|
||||
if (!self.isRankList) {
|
||||
self.indexImageView.hidden = YES;
|
||||
}
|
||||
|
||||
self.indexLabel.text = productionModel.display_no ? : @"";
|
||||
self.coverView.coverImageUrl = productionModel.cover;
|
||||
self.titleLabel.text = productionModel.name ? : @"";
|
||||
|
||||
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString: productionModel.production_descirption ? : @""];
|
||||
text.lineSpacing = 5;
|
||||
text.font = kFont13;
|
||||
text.color = kColorRGBA(118, 118, 118, 1);
|
||||
self.introductionLabel.attributedText = text;
|
||||
|
||||
self.authorLabel.text = productionModel.author ? : @"";
|
||||
[self.authorLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo([TFViewHelper getDynamicWidthWithLabel:self.authorLabel]);
|
||||
}];
|
||||
|
||||
self.tagboardView.tagboardArray = productionModel.tag;
|
||||
}
|
||||
|
||||
- (void)setProductionType:(TFProductionType)productionType
|
||||
{
|
||||
[super setProductionType:productionType];
|
||||
|
||||
self.coverView.productionType = productionType;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// AppDelegate+TFAISpeech.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFAISpeech)
|
||||
|
||||
- (void)initAISpeech;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// AppDelegate+TFAISpeech.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFAISpeech.h"
|
||||
#if TF_Enable_Ai
|
||||
#import "iflyMSC/IFlyMSC.h"
|
||||
#endif
|
||||
|
||||
@implementation AppDelegate (TFAISpeech)
|
||||
|
||||
- (void)initAISpeech
|
||||
{
|
||||
#if TF_Enable_Ai
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
NSString *initString = [[NSString alloc] initWithFormat:@"appid=%@", IFLY_App_ID];
|
||||
[IFlySpeechUtility createUtility:initString];
|
||||
[IFlySetting showLogcat:NO];
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// AppDelegate+TFAdvertising.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#if TF_Enable_Third_Party_Ad
|
||||
#import <BUAdSDK/BUAdSDK.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFAdvertising)
|
||||
#if TF_Enable_Third_Party_Ad
|
||||
<BUSplashAdDelegate>
|
||||
#endif
|
||||
|
||||
- (void)initLaunchADView;
|
||||
|
||||
- (void)initADManager;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// AppDelegate+TFAdvertising.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFAdvertising.h"
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
#import "XHLaunchAd.h"
|
||||
#import "TFWebViewController.h"
|
||||
#import "TFAudioDetailViewController.h"
|
||||
|
||||
@implementation AppDelegate (TFAdvertising)
|
||||
|
||||
- (void)initADManager
|
||||
{
|
||||
#if TF_Enable_Third_Party_Ad
|
||||
|
||||
if (BUA_App_Key.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
[BUAdSDKManager setAppID:BUA_App_Key];
|
||||
[BUAdSDKManager setIsPaidApp:NO];
|
||||
[BUAdSDKManager setLoglevel:BUAdSDKLogLevelError];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)initLaunchADView
|
||||
{
|
||||
#if TF_Launch_Advertising
|
||||
// 如果启动引导页将不加载广告启动图
|
||||
if ([self startTimes] == 1) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:TF_START_PAGE];
|
||||
|
||||
StartPage *start_page = [StartPage modelWithJSON:data];
|
||||
|
||||
if (start_page.skip_type == 5) { // 穿山甲开屏广告
|
||||
|
||||
} else { // 自带开屏广告
|
||||
[self createXLLaunchAdView:start_page];
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(7.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 设置固定时间强制显示状态栏
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)createXLLaunchAdView:(StartPage *)start_page
|
||||
{
|
||||
if (!start_page.image || start_page.image.length <= 0) {
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
[XHLaunchAd setLaunchSourceType:SourceTypeLaunchScreen];
|
||||
XHLaunchImageAdConfiguration *imageAdconfiguration = [XHLaunchImageAdConfiguration defaultConfiguration];
|
||||
imageAdconfiguration.duration = 5;
|
||||
imageAdconfiguration.openModel = start_page;
|
||||
imageAdconfiguration.imageNameOrURLString = start_page.image;
|
||||
imageAdconfiguration.showEnterForeground = YES;
|
||||
imageAdconfiguration.imageOption = XHLaunchAdImageRefreshCached;
|
||||
imageAdconfiguration.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[XHLaunchAd imageAdWithImageAdConfiguration:imageAdconfiguration delegate:self];
|
||||
}
|
||||
|
||||
#pragma mark - XHLaunchAdDelegate
|
||||
- (void)xhLaunchAd:(XHLaunchAd *)launchAd clickAndOpenModel:(id)openModel clickPoint:(CGPoint)clickPoint
|
||||
{
|
||||
if(openModel == nil) return;
|
||||
|
||||
if (![openModel isKindOfClass:[StartPage class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
StartPage *model = openModel;
|
||||
|
||||
//点击跳转类型 1-小说书籍,2-内部链接跳转,3-漫画 4-外部链接
|
||||
switch (model.skip_type) {
|
||||
case 1: {
|
||||
TFNovelDetailViewController *vc = [[TFNovelDetailViewController alloc] init];
|
||||
vc.book_id = [model.content integerValue];
|
||||
CYLTabBarController *rootVC = (CYLTabBarController *)[[UIApplication sharedApplication].delegate window].rootViewController;
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] pushViewController:vc animated:YES];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
TFWebViewController *vc = [[TFWebViewController alloc] init];
|
||||
vc.URLString = model.content;
|
||||
CYLTabBarController *rootVC = (CYLTabBarController *)[[UIApplication sharedApplication].delegate window].rootViewController;
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] pushViewController:vc animated:YES];
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: {
|
||||
TFComicDetailViewController *vc = [[TFComicDetailViewController alloc] init];
|
||||
vc.comic_id = [model.content integerValue];
|
||||
CYLTabBarController *rootVC = (CYLTabBarController *)[[UIApplication sharedApplication].delegate window].rootViewController;
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] pushViewController:vc animated:YES];
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:model.content] options:@{} completionHandler:nil];
|
||||
break;
|
||||
case 8: {
|
||||
TFAudioDetailViewController *vc = [[TFAudioDetailViewController alloc] init];
|
||||
vc.audio_id = [model.content integerValue];
|
||||
CYLTabBarController *rootVC = (CYLTabBarController *)[[UIApplication sharedApplication].delegate window].rootViewController;
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] pushViewController:vc animated:YES];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)xhLaunchAdShowFinish:(XHLaunchAd *)launchAd
|
||||
{
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
|
||||
#pragma mark - BUSplashAdDelegate
|
||||
#if TF_Enable_Third_Party_Ad
|
||||
- (void)splashAdDidClose:(BUSplashAdView *)splashAd
|
||||
{
|
||||
[splashAd.superview removeFromSuperview];
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
|
||||
- (void)splashAd:(BUSplashAdView *)splashAd didFailWithError:(NSError * _Nullable)error
|
||||
{
|
||||
[splashAd.superview removeFromSuperview];
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
|
||||
- (void)splashAdDidClick:(BUSplashAdView *)splashAd
|
||||
{
|
||||
if (@available(iOS 13.0, *)) {
|
||||
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDarkContent;
|
||||
} else {
|
||||
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// AppDelegate+TFAppSign.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFAppSign)
|
||||
|
||||
- (void)initUserSign;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// AppDelegate+TFAppSign.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFAppSign.h"
|
||||
#import "TFSignAlertView.h"
|
||||
#import "TFSignModel.h"
|
||||
#import "TFCollectionManager.h"
|
||||
|
||||
static NSArray *bookList;
|
||||
|
||||
@implementation AppDelegate (TFAppSign)
|
||||
|
||||
- (void)initUserSign
|
||||
{
|
||||
#if TF_Sign_Mode
|
||||
if (!TFUserInfoManager.isLogin) return;
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[TFNetworkTools POST:Sign_Click parameters:nil model:TFSignModel.class success:^(BOOL isSuccess, TFSignModel * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
if ((t_model.book.count > 0 || t_model.comic.count > 0) && t_model.award.length > 0 && t_model.tomorrow_award.length > 0) {
|
||||
|
||||
NSMutableArray *t_arr = [NSMutableArray array];
|
||||
|
||||
for (TFProductionModel *t in t_model.book) {
|
||||
t.productionType = TFProductionTypeNovel;
|
||||
[t_arr addObject:t];
|
||||
}
|
||||
|
||||
for (TFProductionModel *t in t_model.comic) {
|
||||
t.productionType = TFProductionTypeComic;
|
||||
[t_arr addObject:t];
|
||||
}
|
||||
|
||||
for (TFProductionModel *t in t_model.audio) {
|
||||
t.productionType = TFProductionTypeAudio;
|
||||
[t_arr addObject:t];
|
||||
}
|
||||
|
||||
|
||||
TFSignAlertView *alert = [[TFSignAlertView alloc] init];
|
||||
// alert.awardTitle = t_model.award;
|
||||
alert.alertTitle = t_model.award;
|
||||
alert.alertDetailContent = t_model.tomorrow_award;
|
||||
alert.contentLabel.font = kFont11;
|
||||
alert.bookList = [t_arr copy];
|
||||
[alert showAlertView];
|
||||
|
||||
bookList = [t_arr copy];
|
||||
}
|
||||
}
|
||||
} failure:nil];
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// AppDelegate+TFCheckSetting.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFCheckSetting)
|
||||
|
||||
- (void)initCheckSetting;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// AppDelegate+TFCheckSetting.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFCheckSetting.h"
|
||||
#import "AppDelegate+TFPopup.h"
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
#import "TFUpdateAlertView.h"
|
||||
|
||||
@implementation AppDelegate (TFCheckSetting)
|
||||
|
||||
- (void)initCheckSetting
|
||||
{
|
||||
if ([TFNetworkManager networkingStatus] == NO || [TFNetworkManager currentNetworkStatus] == kCTCellularDataRestrictedStateUnknown) {
|
||||
[TFNetworkManager networkingStatus:^(BOOL status) {
|
||||
if (status == YES) {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Restore_Network object:nil];
|
||||
[self initCheckSetting];
|
||||
});
|
||||
}
|
||||
}];
|
||||
return ;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkSettingRequestWithState:) name:Notification_Login_Success object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkSettingRequestWithState:) name:Notification_Recharge_Success object:nil];
|
||||
|
||||
[self checkSettingRequestWithState:1];
|
||||
}
|
||||
|
||||
// state == 1 是更新全部数据
|
||||
- (void)checkSettingRequestWithState:(NSInteger)state
|
||||
{
|
||||
WS(weakSelf)
|
||||
|
||||
self.checkSettingModel = [[TFCheckSettingModel alloc] init];
|
||||
|
||||
[TFNetworkTools POST:Check_Setting parameters:nil model:TFCheckSettingModel.class success:^(BOOL isSuccess, TFCheckSettingModel * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
weakSelf.checkSettingModel = t_model;
|
||||
if (state == 1) {
|
||||
// 更新提醒
|
||||
switch (weakSelf.checkSettingModel.version_update.status) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1: { // 弱更新
|
||||
TFUpdateAlertView *alert = [[TFUpdateAlertView alloc] init];
|
||||
alert.updateMessage = weakSelf.checkSettingModel.version_update.msg;
|
||||
alert.confirmButtonClickBlock = ^{
|
||||
if (weakSelf.checkSettingModel.version_update.url.length > 0) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:weakSelf.checkSettingModel.version_update.url] options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"更新地址错误")];
|
||||
}
|
||||
};
|
||||
[alert showAlertView];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: { // 强更新
|
||||
TFUpdateAlertView *alert = [[TFUpdateAlertView alloc] init];
|
||||
alert.updateMessage = weakSelf.checkSettingModel.version_update.msg ? : @"";
|
||||
alert.alertDisappearType = TFAlertViewDisappearTypeNever;
|
||||
alert.alertBtnType = TFAlertButtonTypeSingleConfirm;
|
||||
alert.confirmButtonClickBlock = ^{
|
||||
if (weakSelf.checkSettingModel.version_update.url.length > 0) {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:weakSelf.checkSettingModel.version_update.url] options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"更新地址错误")];
|
||||
}
|
||||
};
|
||||
[alert showAlertView];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 过审开关
|
||||
#if TF_Enable_Magic
|
||||
if (weakSelf.checkSettingModel.system_setting.check_status == 1) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Review_State object:@"1"];
|
||||
TFSystemInfoManager.magicStatus = @"1";
|
||||
} else {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Review_State object:@"0"];
|
||||
TFSystemInfoManager.magicStatus = @"0";
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Check_Setting_Update object:nil];
|
||||
}
|
||||
|
||||
// 系统设置参数保存
|
||||
TFSystemInfoManager.masterUnit = weakSelf.checkSettingModel.system_setting.currencyUnit;
|
||||
TFSystemInfoManager.subUnit = weakSelf.checkSettingModel.system_setting.subUnit;
|
||||
|
||||
// 启动页相关
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[weakSelf.checkSettingModel.start_page modelToJSONData] forKey:TF_START_PAGE];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
// 功能状态选择
|
||||
if (weakSelf.checkSettingModel.system_setting.project_type.length > 0) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:weakSelf.checkSettingModel.system_setting.project_type forKey:TF_SITE_STATE];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
// 新版站点状态选择
|
||||
if (weakSelf.checkSettingModel.system_setting.site_type.count > 0) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:weakSelf.checkSettingModel.system_setting.site_type forKey:TF_SITE_STATE];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
// ai开关状态
|
||||
if (weakSelf.checkSettingModel.system_setting.ai_switch == 1) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:TF_Ai_Switch];
|
||||
} else {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:TF_Ai_Switch];
|
||||
}
|
||||
|
||||
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// AppDelegate+TFDeviceID.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFDeviceID)
|
||||
|
||||
- (void)initDeviceInfo;
|
||||
|
||||
- (void)uploadDeviceID:(NSString *)device_id;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// AppDelegate+TFDeviceID.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFDeviceID.h"
|
||||
#import <MobPush/MobPush.h>
|
||||
|
||||
@implementation AppDelegate (TFDeviceID)
|
||||
|
||||
- (void)uploadDeviceID:(NSString *)device_id
|
||||
{
|
||||
if (kObjectIsEmpty(device_id)) return;
|
||||
|
||||
[TFNetworkTools POST:Upload_Device_Info parameters:@{@"device_id":device_id ? : @""} model:nil success:nil failure:nil];
|
||||
}
|
||||
|
||||
- (void)initDeviceInfo
|
||||
{
|
||||
[self syncDeviceInfo];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(syncDeviceInfo) name:Notification_Login_Success object:nil];
|
||||
}
|
||||
|
||||
- (void)syncDeviceInfo
|
||||
{
|
||||
[MobPush getRegistrationID:^(NSString *registrationID, NSError *error) {
|
||||
[TFNetworkTools POST:Upload_Device_Info parameters:@{@"device_id":registrationID?:@""} model:nil success:nil failure:nil];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// AppDelegate+TFEvaluation.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFEvaluation)
|
||||
|
||||
- (void)initAppStoreScore;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// AppDelegate+TFEvaluation.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFEvaluation.h"
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
#import <StoreKit/StoreKit.h>
|
||||
#import "TFEvaluationAlertView.h"
|
||||
|
||||
@implementation AppDelegate (TFEvaluation)
|
||||
|
||||
- (void)initAppStoreScore
|
||||
{
|
||||
#if TF_AppStore_Score
|
||||
if ([self startTimes] % 20 != 0 || [self startTimes] <= 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
TFEvaluationAlertView *alert = [[TFEvaluationAlertView alloc] init];
|
||||
alert.confirmButtonClickBlock = ^{
|
||||
if (@available(iOS 10.3, *)) {
|
||||
if([SKStoreReviewController respondsToSelector:@selector(requestReview)]){
|
||||
[[UIApplication sharedApplication].keyWindow endEditing:YES];
|
||||
[SKStoreReviewController requestReview];
|
||||
}
|
||||
}
|
||||
};
|
||||
alert.cancelButtonClickBlock = ^{
|
||||
NSURL *appReviewUrl = [NSURL URLWithString:TF_EvaluationAddress];
|
||||
[[UIApplication sharedApplication] openURL:appReviewUrl options:@{} completionHandler:nil];
|
||||
};
|
||||
[alert showAlertView];
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// AppDelegate+TFMobService.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/8.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFMobService)
|
||||
/** 分享 */
|
||||
- (void)initMobShare;
|
||||
|
||||
/** 推送 */
|
||||
- (void)initMobPushWithApplication:(UIApplication *)application launchOptions:(NSDictionary *)launchOptions;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// AppDelegate+TFMobService.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/8.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFMobService.h"
|
||||
#import "TFWebViewController.h"
|
||||
#import <ShareSDK/ShareSDK.h>
|
||||
#import <MobPush/MobPush.h>
|
||||
#import "AppDelegate+TFDeviceID.h"
|
||||
|
||||
@implementation AppDelegate (TFMobService)
|
||||
|
||||
- (void)initMobShare
|
||||
{
|
||||
[ShareSDK registPlatforms:^(SSDKRegister *platformsRegister) {
|
||||
#if TF_QQ_Share_Mode
|
||||
[platformsRegister setupQQWithAppId:Tencent_APPID appkey:Tencent_APPKey enableUniversalLink:YES universalLink:@"frlln.share2dlink.com/qq_conn/101918139"];
|
||||
#endif
|
||||
|
||||
#if TF_WeChat_Share_Mode
|
||||
[platformsRegister setupWeChatWithAppId:TF_WeChat_APPID appSecret:TF_WeChat_Secret universalLink:@"xiangyoupin.com/app/"];
|
||||
#endif
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)initMobPushWithApplication:(UIApplication *)application launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
//#if DEBUG
|
||||
[MobPush setAPNsForProduction:NO];
|
||||
//#else
|
||||
[MobPush setAPNsForProduction:YES];
|
||||
//#endif
|
||||
|
||||
MPushNotificationConfiguration *configuration = [[MPushNotificationConfiguration alloc] init];
|
||||
configuration.types = MPushAuthorizationOptionsBadge | MPushAuthorizationOptionsSound | MPushAuthorizationOptionsAlert;
|
||||
[MobPush setupNotification:configuration];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMessage:) name:MobPushDidReceiveMessageNotification object:nil];
|
||||
|
||||
WS(weakSelf)
|
||||
[MobPush getRegistrationID:^(NSString *registrationID, NSError *error) {
|
||||
[weakSelf uploadDeviceID:registrationID];
|
||||
}];
|
||||
|
||||
[MobPush clearBadge];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rigistrationClick) name:Notification_Switch_Language object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rigistrationClick) name:Notification_Login_Success object:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
/// 重新注册
|
||||
- (void)rigistrationClick
|
||||
{
|
||||
WS(weakSelf)
|
||||
[MobPush getRegistrationID:^(NSString *registrationID, NSError *error) {
|
||||
if ([[TFViewHelper getCurrentViewController] isKindOfClass:[self class]]) {
|
||||
[TFPromptManager stopAnimating];
|
||||
}
|
||||
[weakSelf uploadDeviceID:registrationID];
|
||||
}];
|
||||
}
|
||||
|
||||
/// 收到通知回调
|
||||
- (void)didReceiveMessage:(NSNotification *)notification
|
||||
{
|
||||
MPushMessage *message = notification.object;
|
||||
NSDictionary *msgInfo = message.notification.userInfo;
|
||||
|
||||
switch (message.messageType) {
|
||||
case MPushMessageTypeCustom: { // 自定义消息
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case MPushMessageTypeAPNs: { // APNs 回调
|
||||
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { // 前台
|
||||
|
||||
} else { // 后台
|
||||
[self pushVC:msgInfo];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MPushMessageTypeLocal: { // 本地通知回调
|
||||
}
|
||||
break;
|
||||
|
||||
case MPushMessageTypeClicked: {
|
||||
|
||||
[self pushVC:msgInfo];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushVC:(NSDictionary *)msgInfo
|
||||
{
|
||||
if(msgInfo.count > 0) {
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
|
||||
StartPage *model = [StartPage modelWithDictionary:msgInfo];
|
||||
|
||||
[self.tabBarControllerConfig.tabBarController.navigationController popToRootViewControllerAnimated:NO];
|
||||
[self.tabBarControllerConfig.tabBarController setSelectedIndex:1];
|
||||
|
||||
if(model.content.length>0&&(model.skip_type == 1 ||model.skip_type == 3)){
|
||||
CYLTabBarController *rootVC = (CYLTabBarController *)[[UIApplication sharedApplication].delegate window].rootViewController;
|
||||
|
||||
if (model.skip_type == 1) {
|
||||
|
||||
TFNovelDetailViewController *bookMallDetailVC = [[TFNovelDetailViewController alloc]init];
|
||||
bookMallDetailVC.book_id = [model.content integerValue];
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] cyl_pushViewController:bookMallDetailVC animated:NO];
|
||||
|
||||
} else if (model.skip_type == 3) {
|
||||
|
||||
TFComicDetailViewController *comicMallDetailVC = [[TFComicDetailViewController alloc]init];
|
||||
comicMallDetailVC.comic_id = [model.content integerValue];
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] cyl_pushViewController:comicMallDetailVC animated:NO];
|
||||
|
||||
} else {
|
||||
|
||||
TFWebViewController *webViewVC = [[TFWebViewController alloc]init];
|
||||
webViewVC.navTitle = model.title;
|
||||
webViewVC.URLString = model.content;
|
||||
[rootVC.childViewControllers[rootVC.selectedIndex] cyl_pushViewController:webViewVC animated:NO];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// AppDelegate+TFPopup.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFPopup)
|
||||
|
||||
- (void)initInsterestView;
|
||||
|
||||
- (void)updateInsterestData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// AppDelegate+TFPopup.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFPopup.h"
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
#import "TFInterimViewController.h"
|
||||
|
||||
@implementation AppDelegate (TFPopup)
|
||||
|
||||
- (void)initInsterestView
|
||||
{
|
||||
#if TF_Insterest_View
|
||||
if ([TFNetworkManager networkingStatus]) {
|
||||
if ([[NSUserDefaults standardUserDefaults] objectForKey:TF_Insterest_Switch] == nil) {
|
||||
[self.window.rootViewController presentViewController:[[TFInterimViewController alloc] init] animated:NO completion:^{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:TF_Insterest_Switch];
|
||||
}];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateInsterestData) name:Notification_Login_Success object:nil];
|
||||
}
|
||||
|
||||
- (void)updateInsterestData
|
||||
{
|
||||
#if TF_Insterest_View
|
||||
// 上传感兴趣的内容
|
||||
if (TFUserInfoManager.isLogin) {
|
||||
[TFNetworkTools POST:Save_Recommed parameters:@{@"gender":@([TFUserInfoManager shareInstance].gender)} model:nil success:nil failure:nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// AppDelegate+TFShortcutTouch.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFShortcutTouch)
|
||||
|
||||
- (void)initShortcutTouch;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// AppDelegate+TFShortcutTouch.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFShortcutTouch.h"
|
||||
#import "TFSearchViewController.h"
|
||||
|
||||
@implementation AppDelegate (TFShortcutTouch)
|
||||
|
||||
- (void)initShortcutTouch
|
||||
{
|
||||
#if TF_ShortcutTouch
|
||||
if (@available(iOS 9.0, *)) {
|
||||
UIApplicationShortcutIcon *searchIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"public_search@3x.png"];
|
||||
UIApplicationShortcutIcon *bookRackIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"public_quick_rack.png"];
|
||||
UIApplicationShortcutIcon *discoverIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:@"public_quick_discover.png"];
|
||||
|
||||
UIMutableApplicationShortcutItem *searchItem = [[UIMutableApplicationShortcutItem alloc] initWithType:@"search" localizedTitle:TFLocalizedString(@"搜索") localizedSubtitle:nil icon:searchIcon userInfo:nil];
|
||||
UIMutableApplicationShortcutItem *bookRackItem = [[UIMutableApplicationShortcutItem alloc] initWithType:@"bookRack" localizedTitle:TFLocalizedString(@"书架") localizedSubtitle:nil icon:bookRackIcon userInfo:nil];
|
||||
UIMutableApplicationShortcutItem *discoverItem = [[UIMutableApplicationShortcutItem alloc] initWithType:@"discover" localizedTitle:TFLocalizedString(@"发现") localizedSubtitle:nil icon:discoverIcon userInfo:nil];
|
||||
|
||||
[[UIApplication sharedApplication] setShortcutItems:@[searchItem,bookRackItem,discoverItem]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initShortcutTouch) name:Notification_Switch_Language object:nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler API_AVAILABLE(ios(9.0))
|
||||
{
|
||||
if (shortcutItem) {
|
||||
[self actionWithShortcutItem:shortcutItem];
|
||||
}
|
||||
|
||||
if (completionHandler) {
|
||||
completionHandler(YES);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)actionWithShortcutItem:(UIApplicationShortcutItem *)item API_AVAILABLE(ios(9.0))
|
||||
{
|
||||
if ([item.type isEqualToString:@"search"]) {
|
||||
|
||||
[self.tabBarControllerConfig.tabBarController setSelectedIndex:1];
|
||||
|
||||
TFSearchViewController *search = [[TFSearchViewController alloc] init];
|
||||
TFNavigationController *nav = [[TFNavigationController alloc] initWithRootViewController:search];
|
||||
[[TFViewHelper getCurrentViewController] presentViewController:nav animated:YES completion:nil];
|
||||
|
||||
} else if ([item.type isEqualToString:@"bookRack"]) {
|
||||
[self.tabBarControllerConfig.tabBarController setSelectedIndex:0];
|
||||
} else if ([item.type isEqualToString:@"discover"]) {
|
||||
[self.tabBarControllerConfig.tabBarController setSelectedIndex:2];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// AppDelegate+TFStartTimes.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppDelegate (TFStartTimes)
|
||||
|
||||
- (void)initStartTimes;
|
||||
|
||||
- (int)startTimes;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// AppDelegate+TFStartTimes.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/15.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
|
||||
@implementation AppDelegate (TFStartTimes)
|
||||
|
||||
- (void)initStartTimes
|
||||
{
|
||||
int startNum = [[[NSUserDefaults standardUserDefaults] objectForKey:@"xtfei_start_num"] intValue];
|
||||
|
||||
if (startNum != 0) {
|
||||
startNum ++;
|
||||
} else {
|
||||
startNum = 1;
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:startNum] forKey:@"xtfei_start_num"];
|
||||
}
|
||||
|
||||
- (int)startTimes
|
||||
{
|
||||
return [[[NSUserDefaults standardUserDefaults] objectForKey:@"xtfei_start_num"] intValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// TFAppleSignManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/4.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@class TFUserInfoManager;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TFAppleSignState) {
|
||||
TFAppleSignStateLogin,
|
||||
TFAppleSignStateBinding
|
||||
};
|
||||
|
||||
@protocol TFAppleSignDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
- (void)appleSignResponseSuccess:(TFUserInfoManager *)userData;
|
||||
|
||||
- (void)appleSignResponseFail:(NSString *)error;
|
||||
@end
|
||||
|
||||
@interface TFAppleSignManager : NSObject
|
||||
|
||||
@property (nonatomic ,weak) id <TFAppleSignDelegate> delegate;
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (void)tunedUpAppleSignWithState:(TFAppleSignState)state API_AVAILABLE(ios(13.0));
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// TFAppleSignManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/4.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFAppleSignManager.h"
|
||||
#import <AuthenticationServices/AuthenticationServices.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface TFAppleSignManager ()<ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFAppleSignManager
|
||||
|
||||
implementation_singleton(TFAppleSignManager)
|
||||
|
||||
- (void)tunedUpAppleSignWithState:(TFAppleSignState)state
|
||||
{
|
||||
ASAuthorizationAppleIDProvider *provider = [[ASAuthorizationAppleIDProvider alloc] init];
|
||||
ASAuthorizationAppleIDRequest *request = [provider createRequest];
|
||||
request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
|
||||
|
||||
ASAuthorizationController *authorization = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
|
||||
authorization.delegate = self;
|
||||
authorization.presentationContextProvider = self;
|
||||
[authorization performRequests];
|
||||
}
|
||||
|
||||
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0))
|
||||
{
|
||||
AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
|
||||
return app.window;
|
||||
}
|
||||
|
||||
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0))
|
||||
{
|
||||
if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
|
||||
[self requestTouristsLogin];
|
||||
}
|
||||
}
|
||||
|
||||
// 游客登录请求
|
||||
- (void)requestTouristsLogin
|
||||
{
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"正在登录")];
|
||||
|
||||
WS(weakSelf)
|
||||
[TFNetworkTools POST:Tourists_Login parameters:@{@"udid":[TFUtilsHelper getUDID]} model:TFUserInfoManager.class success:^(BOOL isSuccess, TFUserInfoManager * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
[TFPromptManager hiddenAlert];
|
||||
if (isSuccess) {
|
||||
[weakSelf appleSignResponseSuccess:t_model];
|
||||
} else {
|
||||
[weakSelf appleSignResponseFail:requestModel.msg];
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[TFPromptManager hiddenAlert];
|
||||
[weakSelf appleSignResponseFail:error.localizedFailureReason ?: TFLocalizedString(@"登录失败")];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0))
|
||||
{
|
||||
NSString *errorMsg = nil;
|
||||
|
||||
switch (error.code) {
|
||||
case ASAuthorizationErrorCanceled:
|
||||
errorMsg = TFLocalizedString(@"您已取消授权");
|
||||
break;
|
||||
case ASAuthorizationErrorFailed:
|
||||
errorMsg = TFLocalizedString(@"授权请求失败");
|
||||
break;
|
||||
case ASAuthorizationErrorInvalidResponse:
|
||||
errorMsg = TFLocalizedString(@"授权请求响应无效");
|
||||
break;
|
||||
case ASAuthorizationErrorNotHandled:
|
||||
errorMsg = TFLocalizedString(@"未能处理授权请求");
|
||||
break;
|
||||
case ASAuthorizationErrorUnknown:
|
||||
errorMsg = TFLocalizedString(@"授权失败");
|
||||
break;
|
||||
}
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:errorMsg];
|
||||
}
|
||||
|
||||
- (void)appleSignResponseSuccess:(TFUserInfoManager *)userData
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(appleSignResponseSuccess:)]) {
|
||||
[self.delegate appleSignResponseSuccess:userData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appleSignResponseFail:(NSString *)error
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(appleSignResponseFail:)]) {
|
||||
[self.delegate appleSignResponseFail:error ? : @""];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TFBannerActionManager.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/11.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFBannerActionManager : NSObject
|
||||
|
||||
+ (TFBasicViewController *_Nullable)getBannerActionWithBannerModel:(TFBannerModel *)bannerModel productionType:(TFProductionType)type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// TFBannerActionManager.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/11.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBannerActionManager.h"
|
||||
#import "TFMemberViewController.h"
|
||||
#import "TFTaskViewController.h"
|
||||
#import "TFTaskViewController.h"
|
||||
#import "TFRechargeViewController.h"
|
||||
#import "WXYZ_FeedbackSubViewController.h"
|
||||
#import "TFSetViewController.h"
|
||||
#import "TFWebViewController.h"
|
||||
|
||||
@implementation TFBannerActionManager
|
||||
|
||||
+ (TFBasicViewController *_Nullable)getBannerActionWithBannerModel:(TFBannerModel *)bannerModel productionType:(TFProductionType)type
|
||||
{
|
||||
switch (bannerModel.action) {
|
||||
case 1:
|
||||
switch (type) {
|
||||
#if TF_Enable_Book
|
||||
case TFProductionTypeNovel: {
|
||||
TFNovelDetailViewController *novelDetail = [[TFNovelDetailViewController alloc] init];
|
||||
novelDetail.book_id = [bannerModel.content integerValue];
|
||||
|
||||
return novelDetail;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if TF_Enable_Comic
|
||||
case TFProductionTypeComic: {
|
||||
TFComicDetailViewController *comicDetail = [[TFComicDetailViewController alloc] init];
|
||||
comicDetail.comic_id = [bannerModel.content integerValue];
|
||||
|
||||
return comicDetail;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if TF_Enable_Audio
|
||||
case TFProductionTypeAudio: {
|
||||
TFAudioDetailViewController *audioDetail = [[TFAudioDetailViewController alloc] init];
|
||||
audioDetail.audio_id = [bannerModel.content integerValue];
|
||||
return audioDetail;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: {
|
||||
if ([bannerModel.content isEqualToString:@"vip"]) { // 会员中心
|
||||
return [[TFMemberViewController alloc] init];
|
||||
|
||||
} else if ([bannerModel.content isEqualToString:@"task"]) { // 任务
|
||||
return [[TFTaskViewController alloc] init];
|
||||
|
||||
} else if ([bannerModel.content isEqualToString:@"sign"]) { // 签到
|
||||
return [[TFTaskViewController alloc] init];
|
||||
|
||||
} else if ([bannerModel.content isEqualToString:@"recharge"]) { // 金币充值
|
||||
return [[TFRechargeViewController alloc] init];
|
||||
|
||||
} else if ([bannerModel.content isEqualToString:@"feedback"]) { // 意见反馈
|
||||
return [[WXYZ_FeedbackSubViewController alloc] init];
|
||||
|
||||
} else if ([bannerModel.content isEqualToString:@"setting"]) { // 设置
|
||||
return [[TFSetViewController alloc] init];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: {
|
||||
TFWebViewController *vc = [[TFWebViewController alloc] init];
|
||||
vc.URLString = bannerModel.content;
|
||||
return vc;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:bannerModel.content] options:@{} completionHandler:nil];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// TFCollectionManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFCollectionManager : NSObject
|
||||
|
||||
+ (instancetype)shareManagerWithProductionType:(TFProductionType)productionType;
|
||||
|
||||
#pragma add
|
||||
// 添加收藏作品
|
||||
- (BOOL)addCollectionWithProductionModel:(TFProductionModel *)productionModel;
|
||||
|
||||
- (BOOL)addCollectionWithProductionModel:(TFProductionModel *)productionModel atIndex:(NSInteger)index;
|
||||
|
||||
#pragma delete
|
||||
// 删除收藏作品
|
||||
- (BOOL)removeCollectionWithProductionModel:(TFProductionModel *)productionModel;
|
||||
|
||||
- (BOOL)removeCollectionWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 删除全部收藏作品
|
||||
- (BOOL)removeAllCollection;
|
||||
|
||||
#pragma change
|
||||
// 修改本地作品记录
|
||||
- (BOOL)modificationCollectionWithProductionModel:(TFProductionModel *)productionModel;
|
||||
|
||||
// 移动当前作品记录到首位
|
||||
- (void)moveCollectionToTopWithProductionModel:(TFProductionModel *)productionModel;
|
||||
|
||||
#pragma check
|
||||
// 本地全部作品记录
|
||||
- (NSArray <TFProductionModel *> *)getAllCollection;
|
||||
|
||||
// 此作品是否已收藏
|
||||
- (TFProductionModel * _Nullable)isCollectedWithProductionModel:(TFProductionModel *)productionModel;
|
||||
|
||||
// 获取作品收藏记录
|
||||
- (TFProductionModel *)getCollectedProductionModelWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 此作品是否已收藏
|
||||
- (BOOL)isCollectedWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
//
|
||||
// TFCollectionManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/1.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFCollectionManager.h"
|
||||
|
||||
@interface TFCollectionManager ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFCollectionManager
|
||||
|
||||
static TFProductionType _productionType;
|
||||
static TFCollectionManager *_instanceCollection;
|
||||
|
||||
+ (instancetype)shareManagerWithProductionType:(TFProductionType)productionType
|
||||
{
|
||||
static dispatch_once_t once_token_collection;
|
||||
dispatch_once(&once_token_collection, ^{
|
||||
_instanceCollection = [[self alloc] init];
|
||||
});
|
||||
_productionType = productionType;
|
||||
return _instanceCollection;
|
||||
}
|
||||
|
||||
#pragma add
|
||||
// 添加收藏作品
|
||||
- (BOOL)addCollectionWithProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
return [self addCollectionWithProductionModel:productionModel atIndex:0];
|
||||
}
|
||||
|
||||
- (BOOL)addCollectionWithProductionModel:(TFProductionModel *)productionModel atIndex:(NSInteger)index
|
||||
{
|
||||
if (!productionModel) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 已存储
|
||||
if ([self isCollectedWithProductionModel:productionModel]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (self.collectionArray.count == 0) {
|
||||
[self.collectionArray addObject:productionModel];
|
||||
} else {
|
||||
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
} else if (index > self.collectionArray.count - 1) {
|
||||
index = self.collectionArray.count - 1;
|
||||
}
|
||||
[self.collectionArray insertObject:productionModel atIndex:index];
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
return [self writeToPlistFile];
|
||||
}
|
||||
|
||||
#pragma delete
|
||||
|
||||
// 删除收藏作品
|
||||
- (BOOL)removeCollectionWithProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
TFProductionModel *t_model = [self isCollectedWithProductionModel:productionModel];
|
||||
return [self removeCollectionWithProduction_id:t_model.production_id];
|
||||
}
|
||||
|
||||
// 删除收藏作品
|
||||
- (BOOL)removeCollectionWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
if (![self isCollectedWithProduction_id:production_id]) {
|
||||
// 本地无记录无法删除
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (TFProductionModel *t_model in self.collectionArray) {
|
||||
if (t_model.production_id == production_id) {
|
||||
[self.collectionArray removeObject:t_model];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 写入文件
|
||||
return [self writeToPlistFile];
|
||||
}
|
||||
|
||||
// 删除全部收藏作品
|
||||
- (BOOL)removeAllCollection
|
||||
{
|
||||
[self.collectionArray removeAllObjects];
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self cacheFilePath] error:nil];
|
||||
|
||||
// 写入文件
|
||||
return [self writeToPlistFile];
|
||||
}
|
||||
|
||||
#pragma change
|
||||
// 修改本地作品记录
|
||||
- (BOOL)modificationCollectionWithProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
// 本地记录不存在,无法删除
|
||||
if (![self isCollectedWithProductionModel:productionModel]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (int i = 0; i < self.collectionArray.count; i ++) {
|
||||
TFProductionModel *t_model = [self.collectionArray objectAtIndex:i];
|
||||
if (t_model.production_id == productionModel.production_id) {
|
||||
[self.collectionArray replaceObjectAtIndex:i withObject:productionModel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
return [self writeToPlistFile];
|
||||
}
|
||||
|
||||
// 移动当前作品记录到首位
|
||||
- (void)moveCollectionToTopWithProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
TFProductionModel *t_model = [self isCollectedWithProductionModel:productionModel];
|
||||
if (!t_model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.collectionArray.count == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TFProductionModel *tt_model in self.collectionArray) {
|
||||
if (tt_model.production_id == productionModel.production_id) {
|
||||
[self.collectionArray removeObject:tt_model];
|
||||
[self.collectionArray insertObject:tt_model atIndex:0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[self writeToPlistFile];
|
||||
}
|
||||
|
||||
#pragma check
|
||||
// 本地全部作品记录
|
||||
- (NSArray <TFProductionModel *> *)getAllCollection
|
||||
{
|
||||
return [self.collectionArray mutableCopy];
|
||||
}
|
||||
|
||||
// 此作品是否已收藏
|
||||
- (TFProductionModel * _Nullable)isCollectedWithProductionModel:(TFProductionModel *)productionModel
|
||||
{
|
||||
if (self.collectionArray.count <= 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!productionModel) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (TFProductionModel *t_model in self.collectionArray) {
|
||||
if (t_model.production_id == productionModel.production_id) {
|
||||
return t_model;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (TFProductionModel *)getCollectedProductionModelWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
if (self.collectionArray.count <= 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (production_id == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (TFProductionModel *t_model in self.collectionArray) {
|
||||
if (t_model.production_id == production_id) {
|
||||
return t_model;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 此作品是否已收藏
|
||||
- (BOOL)isCollectedWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
if (self.collectionArray.count <= 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (TFProductionModel *t_model in self.collectionArray) {
|
||||
if (t_model.production_id == production_id) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
static NSMutableDictionary *collectionDictionary;
|
||||
|
||||
- (NSMutableArray *)collectionArray
|
||||
{
|
||||
if (!collectionDictionary) {
|
||||
collectionDictionary = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSMutableArray *t_arr = [collectionDictionary objectForKey:[TFUtilsHelper formatStringWithInteger:_productionType]];
|
||||
if (!t_arr) {
|
||||
|
||||
NSMutableArray *localDataArray = [NSMutableArray arrayWithContentsOfFile:[self cacheFilePath]];
|
||||
|
||||
t_arr = [NSMutableArray array];
|
||||
for (NSString *modelJsong in localDataArray) {
|
||||
[t_arr addObject:[TFProductionModel modelWithJSON:modelJsong]];
|
||||
}
|
||||
|
||||
[collectionDictionary setObject:t_arr forKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:_productionType]]];
|
||||
|
||||
}
|
||||
|
||||
return t_arr;
|
||||
}
|
||||
|
||||
- (NSString *)cacheFilePath
|
||||
{
|
||||
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectOrNilAtIndex:0];
|
||||
NSString *rootFloderPath = [documentPath stringByAppendingPathComponent:[@"WXYZ_ProductionCollectionFileFloder" md5String]];
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:rootFloderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:rootFloderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
NSString *cacheFilePath = @"";
|
||||
switch (_productionType) {
|
||||
case TFProductionTypeNovel:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"BookCollectionFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeComic:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"ComicCollectionFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeAudio:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"AudioCollectionFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeAi:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"AiCollectionFile" md5String]]];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) {
|
||||
[@[] writeToFile:cacheFilePath atomically:NO];
|
||||
}
|
||||
|
||||
return cacheFilePath;
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
- (BOOL)writeToPlistFile
|
||||
{
|
||||
NSMutableArray *t_arr = [NSMutableArray array];
|
||||
|
||||
for (TFProductionModel *t_model in self.collectionArray) {
|
||||
[t_arr addObject:[t_model modelToJSONString]];
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (![t_arr writeToFile:[self cacheFilePath] atomically:NO]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// WXYZ_AudioDownloadManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/28.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_DownloadHelper.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_AudioDownloadManager : NSObject <WXYZ_DownloadManagerProtocol>
|
||||
|
||||
@property (nonatomic, copy) void (^downloadDeleteFinishBlock)(NSArray *success_chapter_ids, NSArray *fail_chapter);
|
||||
|
||||
// 章节下载状态改变
|
||||
@property (nonatomic, copy) void (^downloadChapterStateChangeBlock)(WXYZ_DownloadChapterState state, NSInteger production_id, NSInteger chapter_id);
|
||||
|
||||
// 总体任务下载完成
|
||||
@property (nonatomic, copy) void (^downloadMissionStateChangeBlock)(WXYZ_DownloadMissionState state, NSInteger production_id, NSArray *chapter_ids);
|
||||
|
||||
interface_singleton
|
||||
|
||||
// 获取已下载音频文件路径
|
||||
- (NSString *)chapterDownloadedFilePathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id chapter_update_time:(NSString *)chapter_update_time;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
//
|
||||
// WXYZ_AudioDownloadManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/28.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_AudioDownloadManager.h"
|
||||
|
||||
static dispatch_group_t url_session_manager_completion_group() {
|
||||
static dispatch_group_t af_url_session_manager_completion_group;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
af_url_session_manager_completion_group = dispatch_group_create();
|
||||
});
|
||||
|
||||
return af_url_session_manager_completion_group;
|
||||
}
|
||||
|
||||
@interface WXYZ_AudioDownloadManager () <NSURLSessionDataDelegate>
|
||||
|
||||
@property (nonatomic, assign) BOOL requesting;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *requestOrderArray;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_AudioDownloadManager
|
||||
|
||||
implementation_singleton(WXYZ_AudioDownloadManager)
|
||||
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 下载章节
|
||||
- (void)downloadChapterWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
[self downloadChaptersWithProductionModel:productionModel production_id:production_id chapter_ids:@[[TFUtilsHelper formatStringWithInteger:chapter_id]]];
|
||||
}
|
||||
|
||||
// 下载多个章节
|
||||
- (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids
|
||||
{
|
||||
[[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:productionModel productionType:TFProductionTypeAudio];
|
||||
|
||||
WS(weakSelf)
|
||||
[TFNetworkTools POST:Audio_Chapter_Download parameters:@{@"audio_id":[TFUtilsHelper formatStringWithInteger:production_id], @"chapter_id":[chapter_ids componentsJoinedByString:@","]} model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 任务开始
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionStart, production_id, chapter_ids);
|
||||
}
|
||||
// 任务加入任务队列
|
||||
NSArray *taskArray = [t_model objectForKey:@"data"];
|
||||
if (taskArray.count > 0) {
|
||||
|
||||
for (NSDictionary *t_dic in taskArray) {
|
||||
TFProductionChapterModel *imageCollectionModel = [TFProductionChapterModel modelWithDictionary:t_dic];
|
||||
if (imageCollectionModel) {
|
||||
// 开始下载
|
||||
[[self audioChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:identify_downloading forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.downloadChapterStateChangeBlock) {
|
||||
self.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadStart, imageCollectionModel.production_id, imageCollectionModel.chapter_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[weakSelf.requestOrderArray addObject:taskArray];
|
||||
|
||||
if (!weakSelf.requesting) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"已加入下载列表")];
|
||||
}
|
||||
|
||||
// 开始下载任务
|
||||
[weakSelf downloadTaskRequest];
|
||||
} else {
|
||||
// 任务失败
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
} else if (Compare_Json_isEqualTo(requestModel.code, 701)) { // 请求购买
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionShouldPay, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除本地已下载作品
|
||||
- (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
// 删除作品文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadProductionFolderWithProduction_id:production_id productionType:TFProductionTypeAudio];
|
||||
|
||||
// 删除缓存
|
||||
[[self audioChaptersRecordDictionaryWithProduction_id:production_id] removeAllObjects];
|
||||
|
||||
// 写入
|
||||
[self audioRecordWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 删除本地多个已下载章节
|
||||
- (void)removeDownloadChaptersWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *> *)chapter_ids
|
||||
{
|
||||
if (chapter_ids.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:production_id chapter_ids:chapter_ids productionType:TFProductionTypeAudio];
|
||||
|
||||
// 删除下载记录
|
||||
NSMutableDictionary *t_dic = [self audioChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
NSMutableArray *t_delete_arr = [NSMutableArray array];
|
||||
for (NSString *chapter_id in chapter_ids) {
|
||||
[t_delete_arr addObject:chapterRecordKey(production_id, [chapter_id integerValue])];
|
||||
}
|
||||
|
||||
[t_dic removeObjectsForKeys:t_delete_arr];
|
||||
|
||||
// 如果全部章节已经删除,则删除整体作品文件夹
|
||||
if ([self getDownloadChapterCountWithProduction_id:production_id] == 0) {
|
||||
[self removeDownloadProductionWithProduction_id:production_id];
|
||||
} else {
|
||||
[self audioRecordWriteToPlistWithProduction_id:production_id];
|
||||
}
|
||||
|
||||
// 删除回调
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.downloadDeleteFinishBlock) {
|
||||
self.downloadDeleteFinishBlock(chapter_ids, @[]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
查
|
||||
**/
|
||||
|
||||
// 获取下载章节数
|
||||
- (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] getDownloadChapterCountWithProduction_id:production_id productionType:TFProductionTypeAudio];
|
||||
}
|
||||
|
||||
// 获取全部已下载章节model
|
||||
- (NSArray *)getDownloadChapterModelArrayWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self audioChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
NSMutableArray *t_arr = [NSMutableArray array];
|
||||
for (id value in t_dic.allValues) {
|
||||
if ([value isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
[t_arr addObject:value];
|
||||
}
|
||||
}
|
||||
return [t_arr copy];
|
||||
}
|
||||
|
||||
// 获取已下载章节model
|
||||
- (id)getDownloadChapterModelWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self audioChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
id value = [t_dic objectForKey:chapterRecordKey(production_id, chapter_id)];
|
||||
|
||||
if ([value isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 获取章节下载状态
|
||||
- (WXYZ_ProductionDownloadState)getChapterDownloadStateWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self audioChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isEqual:identify_downloading]) {
|
||||
return WXYZ_ProductionDownloadStateDownloading;
|
||||
}
|
||||
|
||||
if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
return WXYZ_ProductionDownloadStateDownloaded;
|
||||
}
|
||||
|
||||
return WXYZ_ProductionDownloadStateNormal;
|
||||
}
|
||||
|
||||
// 作品章节是否下载
|
||||
- (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] isChapterDownloadedWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeAudio];
|
||||
}
|
||||
|
||||
// 获取文件地址
|
||||
- (NSString *)chapterDownloadedFilePathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id chapter_update_time:(NSString *)chapter_update_time
|
||||
{
|
||||
// 章节文件名称
|
||||
NSString *chapterFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@", [TFUtilsHelper formatStringWithInteger:production_id], [TFUtilsHelper formatStringWithInteger:chapter_id], [TFUtilsHelper formatStringWithObject:chapter_update_time]]];
|
||||
|
||||
NSString *chapterFilePath = @"";
|
||||
NSArray *extensionArr = @[@"mp3", @"wav", @"wma"];
|
||||
for (NSString *extension in extensionArr) {
|
||||
chapterFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeAudio] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", chapterFileName, extension]];
|
||||
if (chapterFilePath && chapterFilePath.length > 0 && [[NSFileManager defaultManager] fileExistsAtPath:chapterFilePath]) {
|
||||
return chapterFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
return chapterFilePath;
|
||||
}
|
||||
|
||||
- (void)downloadTaskRequest
|
||||
{
|
||||
if (self.requestOrderArray.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.requesting) {
|
||||
return;
|
||||
}
|
||||
self.requesting = YES;
|
||||
|
||||
NSArray *taskArray = [self.requestOrderArray firstObject];
|
||||
[self.requestOrderArray removeFirstObject];
|
||||
|
||||
WS(weakSelf)
|
||||
dispatch_group_async(url_session_manager_completion_group(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
dispatch_queue_t audio_queue = dispatch_queue_create("wxyz_audio_queue", DISPATCH_QUEUE_CONCURRENT);
|
||||
dispatch_group_t audio_group = dispatch_group_create();
|
||||
|
||||
for (NSDictionary *t_dic in taskArray) {
|
||||
dispatch_group_async(audio_group, audio_queue, ^{
|
||||
|
||||
dispatch_group_enter(audio_group);
|
||||
|
||||
TFProductionChapterModel *t_model = [TFProductionChapterModel modelWithDictionary:t_dic];
|
||||
|
||||
if (!t_model) {
|
||||
// 单个章节下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, t_model.production_id, t_model.chapter_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AFURLSessionManager *manager = [[AFURLSessionManager alloc] init];
|
||||
manager.operationQueue.maxConcurrentOperationCount = 2;
|
||||
NSURL *URL = [NSURL URLWithString:t_model.content];
|
||||
|
||||
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:[NSURLRequest requestWithURL:URL] progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
|
||||
|
||||
// 存储文件夹创建
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:t_model.production_id chapter_id:t_model.chapter_id productionType:TFProductionTypeAudio]]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:t_model.production_id chapter_id:t_model.chapter_id productionType:TFProductionTypeAudio] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// 获取图片名称
|
||||
NSString *audioFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@", [TFUtilsHelper formatStringWithInteger:t_model.production_id], [TFUtilsHelper formatStringWithInteger:t_model.chapter_id], [TFUtilsHelper formatStringWithObject:t_model.update_time]]];
|
||||
|
||||
// 图片地址
|
||||
NSString *audioFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:t_model.production_id chapter_id:t_model.chapter_id productionType:TFProductionTypeAudio] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", audioFileName, [TFViewHelper audioExtensionWithFormatString:[response MIMEType]]]];
|
||||
|
||||
return [NSURL fileURLWithPath:audioFilePath];
|
||||
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
|
||||
if (error) {
|
||||
// 删除出错章节文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:t_model.production_id chapter_ids:@[[TFUtilsHelper formatStringWithInteger:t_model.chapter_id]] productionType:TFProductionTypeAudio];
|
||||
[[weakSelf audioChaptersRecordDictionaryWithProduction_id:t_model.production_id] setObject:identify_fail forKey:chapterRecordKey(t_model.production_id, t_model.chapter_id)];
|
||||
[weakSelf audioRecordWriteToPlistWithProduction_id:t_model.production_id];
|
||||
|
||||
// 章节下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, t_model.production_id, t_model.chapter_id);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// 下载成功 覆盖临时下载记录
|
||||
[[weakSelf audioChaptersRecordDictionaryWithProduction_id:t_model.production_id] setObject:t_model forKey:chapterRecordKey(t_model.production_id, t_model.chapter_id)];
|
||||
[weakSelf audioRecordWriteToPlistWithProduction_id:t_model.production_id];
|
||||
|
||||
// 章节下载成功
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFinished, t_model.production_id, t_model.chapter_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dispatch_group_leave(audio_group);
|
||||
}];
|
||||
[downloadTask resume];
|
||||
});
|
||||
}
|
||||
dispatch_group_notify(audio_group, audio_queue, ^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.requesting = NO;
|
||||
if (weakSelf.requestOrderArray.count == 0) {
|
||||
|
||||
// 任务完成
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFinished, 0, @[]);
|
||||
}
|
||||
} else {
|
||||
[weakSelf downloadTaskRequest];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (NSMutableArray *)requestOrderArray
|
||||
{
|
||||
if (!_requestOrderArray) {
|
||||
_requestOrderArray = [NSMutableArray array];
|
||||
}
|
||||
return _requestOrderArray;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)audioChaptersRecordDictionaryWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] chaptersRecordDownloadDictionaryWithProduction_id:production_id productionType:TFProductionTypeAudio modelClass:[TFProductionChapterModel class]];
|
||||
}
|
||||
|
||||
- (BOOL)audioRecordWriteToPlistWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] writeToChapterPlistFileWithProduction_id:production_id productionType:TFProductionTypeAudio modelClass:[TFProductionChapterModel class]];
|
||||
}
|
||||
|
||||
@end
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// WXYZ_BBBBBBBManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/10.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WXYZ_DownloadHelper.h"
|
||||
#import "TFNovelDownloadTaskListModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_BookDownloadManager : NSObject <WXYZ_DownloadManagerEnumProtocol>
|
||||
|
||||
@property (nonatomic, copy) void (^downloadDeleteFinishBlock)(NSArray *success_chapter_ids, NSArray *fail_chapter);
|
||||
|
||||
// 总体任务下载回调
|
||||
@property (nonatomic, copy) void (^downloadMissionStateChangeBlock)(WXYZ_DownloadMissionState state, NSInteger production_id, TFDownloadTaskModel *downloadTaskModel, NSArray<NSNumber *> * _Nullable chapterIDArray);
|
||||
|
||||
interface_singleton
|
||||
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 下载章节(不支持自动订阅 && 支持多章下载)
|
||||
- (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel downloadTaskModel:(TFDownloadTaskModel *)downloadTaskModel production_id:(NSInteger)production_id start_chapter_id:(NSInteger)start_chapter_id downloadNum:(NSInteger)downloadNum;
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除本地已下载作品
|
||||
- (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
/*
|
||||
查
|
||||
**/
|
||||
// 获取已下载文件路径
|
||||
- (NSString *)getChapterFilePathWithChapterModel:(TFProductionChapterModel *)chapterModel;
|
||||
|
||||
// 获取章节内容
|
||||
- (NSString *)getFileContentsWithChapterModel:(TFProductionChapterModel * __nullable)chapterModel;
|
||||
|
||||
// 存储章节内容
|
||||
- (void)storingFilesWithChapterModel:(TFProductionChapterModel * __nullable)chapterModel storingCompletionHandler:(void (^)(BOOL finishStoring))completionHandler;
|
||||
|
||||
// 获取下载状态
|
||||
- (WXYZ_ProductionDownloadState)getDownloadMissionStateWithProduction_id:(NSInteger)production_id downloadTaskModel:(TFDownloadTaskModel *)downloadTaskModel;
|
||||
|
||||
// 获取某一已下载作品model
|
||||
- (TFNovelDownloadTaskListModel *)getDownloadProductionModelWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+470
@@ -0,0 +1,470 @@
|
||||
//
|
||||
// WXYZ_BBBBBBBManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/10.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_BookDownloadManager.h"
|
||||
#import "TFProductionChapterModel.h"
|
||||
|
||||
// 下载总任务列表model
|
||||
#define TaskListKey @"task_list_key"
|
||||
|
||||
@interface WXYZ_BookDownloadManager ()
|
||||
|
||||
@property (nonatomic, assign) BOOL requesting;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *requestOrderArray;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_BookDownloadManager
|
||||
|
||||
implementation_singleton(WXYZ_BookDownloadManager)
|
||||
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 下载多个章节
|
||||
- (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel downloadTaskModel:(TFDownloadTaskModel *)downloadTaskModel production_id:(NSInteger)production_id start_chapter_id:(NSInteger)start_chapter_id downloadNum:(NSInteger)downloadNum
|
||||
{
|
||||
|
||||
// 作品开始下载标记
|
||||
[[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:productionModel productionType:TFProductionTypeNovel];
|
||||
|
||||
// 任务加入任务队列
|
||||
/*
|
||||
@{
|
||||
production_id:@[起始章节id, 下载章节数量, 下载任务对象]
|
||||
}
|
||||
*/
|
||||
[self.requestOrderArray addObject:@{[TFUtilsHelper formatStringWithInteger:production_id]:@[[TFUtilsHelper formatStringWithInteger:start_chapter_id], [TFUtilsHelper formatStringWithInteger:downloadNum], downloadTaskModel]}];
|
||||
|
||||
if (!self.requesting) {
|
||||
// 开始下载任务
|
||||
[self downloadTaskRequest];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除本地整本已下载作品
|
||||
- (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
// 删除作品文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadProductionFolderWithProduction_id:production_id productionType:TFProductionTypeNovel];
|
||||
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeAllObjects];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
查
|
||||
**/
|
||||
|
||||
// 获取已下载小说文件路径
|
||||
- (NSString *)getChapterFilePathWithChapterModel:(TFProductionChapterModel *)chapterModel
|
||||
{
|
||||
// 章节图片名称
|
||||
NSString *fileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@%@", [TFUtilsHelper formatStringWithInteger:chapterModel.production_id], [TFUtilsHelper formatStringWithInteger:chapterModel.chapter_id], [TFUtilsHelper formatStringWithInteger:chapterModel.is_preview], chapterModel.update_time]];
|
||||
|
||||
NSString *filePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:chapterModel.production_id chapter_id:chapterModel.chapter_id productionType:TFProductionTypeNovel] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.txt", fileName]];
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
// 获取章节内容
|
||||
- (NSString *)getFileContentsWithChapterModel:(TFProductionChapterModel *)chapterModel
|
||||
{
|
||||
// 数据不正确
|
||||
if (!chapterModel || chapterModel.production_id == 0 || chapterModel.chapter_id == 0 || !chapterModel.update_time) {
|
||||
return k_Chapter_RequstFail;
|
||||
}
|
||||
|
||||
// 本地文件地址
|
||||
NSString *filePath = [self getChapterFilePathWithChapterModel:chapterModel];
|
||||
|
||||
// 如果文件存在则返回文件内容
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
||||
|
||||
NSString *body = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:filePath] usedEncoding:nil error:nil];
|
||||
if (body) {
|
||||
return body;
|
||||
}
|
||||
//如果之前不能解码,现在使用GBK解码
|
||||
body = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:filePath] encoding:0x80000632 error:nil];
|
||||
if (body) {
|
||||
return body;
|
||||
}
|
||||
//再使用GB18030解码
|
||||
body = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:filePath] encoding:0x80000631 error:nil];
|
||||
if (body) {
|
||||
return body;
|
||||
} else {
|
||||
return k_Chapter_RequstFail;
|
||||
}
|
||||
} else {
|
||||
return k_Chapter_RequstFail;
|
||||
}
|
||||
}
|
||||
|
||||
// 存储章节内容
|
||||
- (void)storingFilesWithChapterModel:(TFProductionChapterModel *)chapterModel storingCompletionHandler:(void (^)(BOOL finishStoring))completionHandler
|
||||
{
|
||||
// 数据不正确
|
||||
if (!chapterModel || chapterModel.production_id == 0 || chapterModel.chapter_id == 0 || !chapterModel.update_time) {
|
||||
if (completionHandler) {
|
||||
completionHandler(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 文件已存在
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[self getChapterFilePathWithChapterModel:chapterModel]]) {
|
||||
|
||||
// 如果当前获取的是非预览章节内容,则寻找是否有预览章节文件进行删除,节省空间
|
||||
chapterModel.is_preview = 1;
|
||||
if ([self getChapterFilePathWithChapterModel:chapterModel]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self getChapterFilePathWithChapterModel:chapterModel] error:nil];
|
||||
}
|
||||
|
||||
if (completionHandler) {
|
||||
completionHandler(NO);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 文件不存在则存储文件
|
||||
|
||||
// 解析标题
|
||||
NSString *chapter_title_string = @"";
|
||||
if (chapterModel.chapter_title && chapterModel.chapter_title.length > 0) {
|
||||
chapter_title_string = [NSString stringWithFormat:@"W$$X%@W$$X", chapterModel.chapter_title];
|
||||
chapter_title_string = [chapter_title_string stringByAppendingString:@"\n\n"];
|
||||
}
|
||||
|
||||
// 解析内容
|
||||
NSString *chapter_content_string = @"";
|
||||
if (chapterModel.content && chapterModel.content.length > 0) {
|
||||
chapter_content_string = chapterModel.content;
|
||||
}
|
||||
|
||||
// 文件内容
|
||||
NSString *chapter_file_content = [NSString stringWithFormat:@"%@%@",chapter_title_string, chapter_content_string];
|
||||
|
||||
if ([chapter_file_content writeToFile:[self getChapterFilePathWithChapterModel:chapterModel] atomically:NO encoding:NSUTF8StringEncoding error:nil]) {
|
||||
if (completionHandler) {
|
||||
completionHandler(YES);
|
||||
}
|
||||
} else {
|
||||
if (completionHandler) {
|
||||
completionHandler(NO);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取下载状态
|
||||
- (WXYZ_ProductionDownloadState)getDownloadMissionStateWithProduction_id:(NSInteger)production_id downloadTaskModel:(TFDownloadTaskModel *)downloadTaskModel
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self bookDownloadTaskDictionaryWithProduction_id:production_id];
|
||||
if ([[t_dic objectForKey:downloadTaskModel.file_name] isEqual:identify_downloading]) {
|
||||
return WXYZ_ProductionDownloadStateDownloading;
|
||||
}
|
||||
|
||||
if ([[t_dic objectForKey:downloadTaskModel.file_name] isKindOfClass:[TFDownloadTaskModel class]]) {
|
||||
return WXYZ_ProductionDownloadStateDownloaded;
|
||||
}
|
||||
|
||||
TFNovelDownloadTaskListModel *taskListModel = [self getDownloadProductionModelWithProduction_id:production_id];
|
||||
if (taskListModel.task_list.count > 0) {
|
||||
for (TFDownloadTaskModel *t_taskModel in taskListModel.task_list) {
|
||||
if (downloadTaskModel.start_order >= t_taskModel.start_order && downloadTaskModel.end_order <= t_taskModel.end_order) {
|
||||
return WXYZ_ProductionDownloadStateDownloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WXYZ_ProductionDownloadStateNormal;
|
||||
}
|
||||
|
||||
// 获取某一已下载作品model
|
||||
- (TFNovelDownloadTaskListModel *)getDownloadProductionModelWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
TFNovelDownloadTaskListModel *taskListModel = [[TFNovelDownloadTaskListModel alloc] init];
|
||||
taskListModel.productionModel = [[WXYZ_DownloadHelper sharedManager] getDownloadProductionModelWithProduction_id:production_id productionType:TFProductionTypeNovel];
|
||||
taskListModel.task_list = [[self getDownloadProductionArrayWithProduction_id:production_id] mutableCopy];
|
||||
return taskListModel;
|
||||
}
|
||||
|
||||
// 获取某一作品的全部下载model
|
||||
- (NSArray <TFDownloadTaskModel *> *)getDownloadProductionArrayWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSMutableArray *t_arr = [NSMutableArray array];
|
||||
for (id taskModel in [self bookDownloadTaskDictionaryWithProduction_id:production_id].allValues) {
|
||||
if ([taskModel isKindOfClass:[TFDownloadTaskModel class]]) {
|
||||
[t_arr addObject:taskModel];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < t_arr.count; i ++) {
|
||||
TFDownloadTaskModel *taskModel = [t_arr objectAtIndex:i];
|
||||
// 是否需要合并
|
||||
BOOL isMerged = NO;
|
||||
|
||||
// 无交集
|
||||
BOOL noIntersection = YES;
|
||||
|
||||
do {
|
||||
|
||||
isMerged = NO;
|
||||
|
||||
// 遍历数组 组成队列
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
for (NSInteger i = taskModel.start_order; i < taskModel.end_order + 3; i++) {
|
||||
[array addObject:[TFUtilsHelper formatStringWithInteger:i - 1]];
|
||||
}
|
||||
|
||||
for (int i = 0; i < t_arr.count; i ++) {
|
||||
TFDownloadTaskModel *t_task = [t_arr objectOrNilAtIndex:i];
|
||||
|
||||
if (t_task.start_order != taskModel.start_order || t_task.end_order != taskModel.end_order) {
|
||||
if ([array containsObject:[TFUtilsHelper formatStringWithInteger:t_task.start_order]]) {
|
||||
|
||||
TFDownloadTaskModel *tt_task = [[TFDownloadTaskModel alloc] init];
|
||||
tt_task.url = taskModel.url;
|
||||
tt_task.file_name = taskModel.file_name;
|
||||
tt_task.download_title = [NSString stringWithFormat:@"%@ - %@%@", [array objectOrNilAtIndex:1], [TFUtilsHelper formatStringWithInteger:t_task.end_order > taskModel.end_order?t_task.end_order:taskModel.end_order], TFLocalizedString(@"章")];
|
||||
tt_task.dateString = [TFUtilsHelper currentDateStringWithFormat:@"yyyy-MM-dd"];
|
||||
tt_task.file_size = taskModel.file_size + t_task.file_size;
|
||||
tt_task.start_order = [[array objectOrNilAtIndex:1] integerValue];
|
||||
tt_task.end_order = t_task.end_order > taskModel.end_order?t_task.end_order:taskModel.end_order;
|
||||
|
||||
[t_arr replaceObjectAtIndex:i withObject:tt_task];
|
||||
[t_arr removeObject:taskModel];
|
||||
isMerged = YES;
|
||||
noIntersection = NO;
|
||||
|
||||
} else if ([array containsObject:[TFUtilsHelper formatStringWithInteger:t_task.end_order]]) {
|
||||
TFDownloadTaskModel *tt_task = [[TFDownloadTaskModel alloc] init];
|
||||
tt_task.url = taskModel.url;
|
||||
tt_task.file_name = taskModel.file_name;
|
||||
tt_task.download_title = [NSString stringWithFormat:@"%@ - %@%@", [TFUtilsHelper formatStringWithInteger:t_task.start_order], [TFUtilsHelper formatStringWithInteger:t_task.end_order > taskModel.end_order?t_task.end_order:taskModel.end_order], TFLocalizedString(@"章")];
|
||||
tt_task.dateString = [TFUtilsHelper currentDateStringWithFormat:@"yyyy-MM-dd"];
|
||||
tt_task.file_size = taskModel.file_size + t_task.file_size;
|
||||
tt_task.start_order = t_task.start_order;
|
||||
tt_task.end_order = t_task.end_order > taskModel.end_order?t_task.end_order:taskModel.end_order;
|
||||
|
||||
[t_arr replaceObjectAtIndex:i withObject:tt_task];
|
||||
[t_arr removeObject:taskModel];
|
||||
isMerged = YES;
|
||||
noIntersection = NO;
|
||||
|
||||
}
|
||||
if (isMerged) {
|
||||
taskModel = [t_arr objectOrNilAtIndex:0];
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (t_task.start_order == taskModel.start_order && t_task.end_order == taskModel.end_order) {
|
||||
[t_arr replaceObjectAtIndex:i withObject:taskModel];
|
||||
noIntersection = NO;
|
||||
}
|
||||
}
|
||||
|
||||
} while (isMerged);
|
||||
|
||||
if (noIntersection) {
|
||||
[t_arr addObject:taskModel];
|
||||
}
|
||||
}
|
||||
|
||||
return [t_arr copy];
|
||||
}
|
||||
|
||||
- (void)downloadTaskRequest
|
||||
{
|
||||
if (self.requestOrderArray.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.requesting = YES;
|
||||
|
||||
NSDictionary *orderDic = [self.requestOrderArray firstObject];
|
||||
NSInteger production_id = [[orderDic.allKeys firstObject] integerValue];
|
||||
NSString *start_chapter_id = [orderDic objectForKey:[TFUtilsHelper formatStringWithInteger:production_id]][0];
|
||||
NSString *downloadNum = [orderDic objectForKey:[TFUtilsHelper formatStringWithInteger:production_id]][1];
|
||||
TFDownloadTaskModel *taskModel = [orderDic objectForKey:[TFUtilsHelper formatStringWithInteger:production_id]][2];
|
||||
|
||||
[self.requestOrderArray removeFirstObject];
|
||||
|
||||
WS(weakSelf)
|
||||
|
||||
[TFNetworkTools POST:Book_Download_Multiple_Chapters parameters:@{@"book_id":[TFUtilsHelper formatStringWithInteger:production_id], @"chapter_id":start_chapter_id, @"num":downloadNum} model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
if (weakSelf.requestOrderArray.count == 0) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"已加入下载队列")];
|
||||
}
|
||||
|
||||
// 开始下载
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] setObject:identify_downloading forKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务开始
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.downloadMissionStateChangeBlock) {
|
||||
self.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionStart, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
|
||||
// 下载章节合集文件
|
||||
AFURLSessionManager *manager = [[AFURLSessionManager alloc] init];
|
||||
manager.operationQueue.maxConcurrentOperationCount = 1;
|
||||
manager.completionQueue = dispatch_queue_create("com.wxyz.book_queue", NULL);
|
||||
NSURL *URL = [NSURL URLWithString:[requestModel.data objectForKey:@"file_url"]];
|
||||
|
||||
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:[NSURLRequest requestWithURL:URL] progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
|
||||
|
||||
// 创建临时存储文件
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadProductionFolderPathWithProduction_id:production_id productionType:TFProductionTypeNovel]]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadProductionFolderPathWithProduction_id:production_id productionType:TFProductionTypeNovel] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// 临时文件地址
|
||||
NSString *tempFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadProductionFolderPathWithProduction_id:production_id productionType:TFProductionTypeNovel] stringByAppendingPathComponent:[requestModel.data objectForKey:@"file_name"]];
|
||||
|
||||
return [NSURL fileURLWithPath:tempFilePath];
|
||||
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
|
||||
weakSelf.requesting = NO;
|
||||
if (error) {
|
||||
|
||||
// 任务下载失败删除记录
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeObjectForKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
NSString *fileContent = [NSString stringWithContentsOfFile:filePath.path encoding:NSUTF8StringEncoding error:nil];
|
||||
if (fileContent.length > 0) {
|
||||
NSArray *chapterModelArray = [NSArray modelArrayWithClass:[TFProductionChapterModel class] json:fileContent];
|
||||
|
||||
if (chapterModelArray.count > 0) {
|
||||
for (int i = 0; i < chapterModelArray.count; i ++) {
|
||||
|
||||
TFProductionChapterModel *chapterModel = [chapterModelArray objectAtIndex:i];
|
||||
|
||||
// 文件请求成功,解析文件内容
|
||||
[self storingFilesWithChapterModel:chapterModel storingCompletionHandler:^(BOOL finishStoring) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
// 存储下载任务列表
|
||||
taskModel.file_size = [TFUtilsHelper getFileSize:filePath.path];
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] setObject:taskModel forKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载完成
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
NSArray<NSNumber *> *t_arr = [chapterModelArray valueForKeyPath:@"chapter_id"];
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFinished, production_id, taskModel, t_arr);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
// 任务下载失败删除记录
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeObjectForKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移除临时文件
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath.path error:nil];
|
||||
}];
|
||||
[downloadTask resume];
|
||||
} else if (Compare_Json_isEqualTo(requestModel.code, 701)) {
|
||||
weakSelf.requesting = NO;
|
||||
|
||||
// 任务下载失败删除记录
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeObjectForKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionShouldPay, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
weakSelf.requesting = NO;
|
||||
|
||||
// 任务下载失败删除记录
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeObjectForKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
weakSelf.requesting = NO;
|
||||
|
||||
// 任务下载失败删除记录
|
||||
[[self bookDownloadTaskDictionaryWithProduction_id:production_id] removeObjectForKey:taskModel.file_name];
|
||||
[self bookWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
// 任务下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, taskModel, nil);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSMutableArray *)requestOrderArray
|
||||
{
|
||||
if (!_requestOrderArray) {
|
||||
_requestOrderArray = [NSMutableArray array];
|
||||
}
|
||||
return _requestOrderArray;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)bookDownloadTaskDictionaryWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] chaptersRecordDownloadDictionaryWithProduction_id:production_id productionType:TFProductionTypeNovel modelClass:[TFDownloadTaskModel class]];
|
||||
}
|
||||
|
||||
- (BOOL)bookWriteToPlistWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] writeToChapterPlistFileWithProduction_id:production_id productionType:TFProductionTypeNovel modelClass:[TFDownloadTaskModel class]];
|
||||
}
|
||||
|
||||
@end
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// WXYZ_ComicDownloadManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/29.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_DownloadHelper.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ComicDownloadManager : NSObject <WXYZ_DownloadManagerProtocol>
|
||||
|
||||
@property (nonatomic, copy) void (^downloadDeleteFinishBlock)(NSArray *success_chapter_ids, NSArray *fail_chapter);
|
||||
|
||||
// 章节下载状态改变
|
||||
@property (nonatomic, copy) void (^downloadChapterStateChangeBlock)(WXYZ_DownloadChapterState state, NSInteger production_id, NSInteger chapter_id);
|
||||
|
||||
// 总体任务下载完成
|
||||
@property (nonatomic, copy) void (^downloadMissionStateChangeBlock)(WXYZ_DownloadMissionState state, NSInteger production_id, NSArray *chapter_ids);
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (NSArray *)getDownloadChapterModelArrayWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 获取本地图片
|
||||
- (UIImage *)getDownloadLocalImageWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id image_id:(NSInteger)image_id image_update_time:(NSInteger)image_update_time;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
//
|
||||
// WXYZ_ComicDownloadManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/29.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ComicDownloadManager.h"
|
||||
|
||||
static dispatch_group_t url_session_manager_completion_group() {
|
||||
static dispatch_group_t af_url_session_manager_completion_group;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
af_url_session_manager_completion_group = dispatch_group_create();
|
||||
});
|
||||
|
||||
return af_url_session_manager_completion_group;
|
||||
}
|
||||
|
||||
@interface WXYZ_ComicDownloadManager ()
|
||||
|
||||
@property (nonatomic, assign) BOOL requesting;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *requestOrderArray;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ComicDownloadManager
|
||||
|
||||
implementation_singleton(WXYZ_ComicDownloadManager)
|
||||
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 下载章节
|
||||
- (void)downloadChapterWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
[self downloadChaptersWithProductionModel:productionModel production_id:production_id chapter_ids:@[[TFUtilsHelper formatStringWithInteger:chapter_id]]];
|
||||
}
|
||||
|
||||
// 下载多个章节
|
||||
- (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids
|
||||
{
|
||||
// 作品开始下载标记
|
||||
[[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:productionModel productionType:TFProductionTypeComic];
|
||||
|
||||
WS(weakSelf)
|
||||
[TFNetworkTools POST:Comic_Download parameters:@{@"comic_id":[TFUtilsHelper formatStringWithInteger:production_id], @"chapter_id":[chapter_ids componentsJoinedByString:@","]} model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 任务开始
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionStart, production_id, chapter_ids);
|
||||
}
|
||||
|
||||
|
||||
// 任务加入任务队列
|
||||
NSArray *taskArray = [t_model objectForKey:@"data"];
|
||||
if (taskArray.count > 0) {
|
||||
|
||||
for (NSDictionary *t_dic in taskArray) {
|
||||
TFProductionChapterModel *imageCollectionModel = [TFProductionChapterModel modelWithDictionary:t_dic];
|
||||
if (imageCollectionModel) {
|
||||
// 开始下载
|
||||
[[self comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:identify_downloading forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.downloadChapterStateChangeBlock) {
|
||||
self.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadStart, imageCollectionModel.production_id, imageCollectionModel.chapter_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[weakSelf.requestOrderArray addObject:taskArray];
|
||||
|
||||
if (!weakSelf.requesting) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"已加入下载列表")];
|
||||
}
|
||||
|
||||
// 开始下载任务
|
||||
[weakSelf downloadTaskRequestWithProduction_id:production_id];
|
||||
} else {
|
||||
// 任务失败
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else if (Compare_Json_isEqualTo(requestModel.code, 701)) { // 请求购买
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionShouldPay, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
|
||||
// 任务失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids);
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除本地整本已下载作品
|
||||
- (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
// 删除作品文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadProductionFolderWithProduction_id:production_id productionType:TFProductionTypeComic];
|
||||
|
||||
// 删除缓存
|
||||
[[self comicChaptersRecordDictionaryWithProduction_id:production_id] removeAllObjects];
|
||||
|
||||
// 写入
|
||||
[self comicRecordWriteToPlistWithProduction_id:production_id];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 删除本地多个已下载章节
|
||||
- (void)removeDownloadChaptersWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *> *)chapter_ids
|
||||
{
|
||||
if (chapter_ids.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:production_id chapter_ids:chapter_ids productionType:TFProductionTypeComic];
|
||||
|
||||
// 删除下载记录
|
||||
NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
NSMutableArray *t_delete_arr = [NSMutableArray array];
|
||||
for (NSString *chapter_id in chapter_ids) {
|
||||
[t_delete_arr addObject:chapterRecordKey(production_id, [chapter_id integerValue])];
|
||||
}
|
||||
|
||||
[t_dic removeObjectsForKeys:t_delete_arr];
|
||||
|
||||
// 如果全部章节已经删除,则删除整体作品文件夹
|
||||
if ([self getDownloadChapterCountWithProduction_id:production_id] == 0) {
|
||||
[self removeDownloadProductionWithProduction_id:production_id];
|
||||
} else {
|
||||
[self comicRecordWriteToPlistWithProduction_id:production_id];
|
||||
}
|
||||
|
||||
// 删除回调
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.downloadDeleteFinishBlock) {
|
||||
self.downloadDeleteFinishBlock(chapter_ids, @[]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
查
|
||||
**/
|
||||
|
||||
// 获取下载章节数
|
||||
- (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] getDownloadChapterCountWithProduction_id:production_id productionType:TFProductionTypeComic];
|
||||
}
|
||||
|
||||
// 获取全部已下载章节model
|
||||
- (NSArray *)getDownloadChapterModelArrayWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
NSMutableArray *t_arr = [NSMutableArray array];
|
||||
for (id value in t_dic.allValues) {
|
||||
if ([value isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
[t_arr addObject:value];
|
||||
}
|
||||
}
|
||||
return [t_arr copy];
|
||||
}
|
||||
|
||||
// 获取已下载章节model
|
||||
- (id)getDownloadChapterModelWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
|
||||
id value = [t_dic objectForKey:chapterRecordKey(production_id, chapter_id)];
|
||||
|
||||
if ([value isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
return value;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 获取下载状态
|
||||
- (WXYZ_ProductionDownloadState)getChapterDownloadStateWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id];
|
||||
if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isEqual:identify_downloading]) {
|
||||
return WXYZ_ProductionDownloadStateDownloading;
|
||||
}
|
||||
|
||||
if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isEqual:identify_fail]) {
|
||||
return WXYZ_ProductionDownloadStateFail;
|
||||
}
|
||||
|
||||
if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isKindOfClass:[TFProductionChapterModel class]]) {
|
||||
return WXYZ_ProductionDownloadStateDownloaded;
|
||||
}
|
||||
|
||||
return WXYZ_ProductionDownloadStateNormal;
|
||||
}
|
||||
|
||||
|
||||
// 作品章节是否下载
|
||||
- (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] isChapterDownloadedWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeComic];
|
||||
}
|
||||
|
||||
- (UIImage *)getDownloadLocalImageWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id image_id:(NSInteger)image_id image_update_time:(NSInteger)image_update_time
|
||||
{
|
||||
UIImage *t_image = [UIImage imageWithContentsOfFile:[self chapterImageFilePathWithProduction_id:production_id chapter_id:chapter_id image_id:image_id image_update_time:image_update_time]];
|
||||
if (!t_image) {
|
||||
return nil;
|
||||
}
|
||||
return t_image;
|
||||
}
|
||||
|
||||
- (NSString *)chapterImageFilePathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id image_id:(NSInteger)image_id image_update_time:(NSInteger)image_update_time
|
||||
{
|
||||
// 章节图片名称
|
||||
NSString *imageFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@%@", [TFUtilsHelper formatStringWithInteger:production_id], [TFUtilsHelper formatStringWithInteger:chapter_id], [TFUtilsHelper formatStringWithInteger:image_id], [TFUtilsHelper formatStringWithInteger:image_update_time]]];
|
||||
|
||||
NSString *imageFilePath = @"";
|
||||
NSArray *extensionArr = @[@"jpeg", @"png", @"gif", @"tiff", @"webp"];
|
||||
for (NSString *extension in extensionArr) {
|
||||
imageFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeComic] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageFileName, extension]];
|
||||
if (imageFilePath && imageFilePath.length > 0 && [[NSFileManager defaultManager] fileExistsAtPath:imageFilePath]) {
|
||||
return imageFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
return imageFilePath;
|
||||
}
|
||||
|
||||
- (void)downloadTaskRequestWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
if (self.requestOrderArray.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.requesting) {
|
||||
return;
|
||||
}
|
||||
self.requesting = YES;
|
||||
|
||||
NSArray *taskArray = [self.requestOrderArray firstObject];
|
||||
[self.requestOrderArray removeFirstObject];
|
||||
|
||||
WS(weakSelf)
|
||||
dispatch_group_async(url_session_manager_completion_group(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 记录出错章节
|
||||
NSMutableArray __block *errorChapter = [NSMutableArray array];
|
||||
|
||||
dispatch_semaphore_t collection_semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
// 记录章节model内容
|
||||
NSMutableArray *t_chapterModelArray = [NSMutableArray array];
|
||||
|
||||
for (NSDictionary *t_dic in taskArray) {
|
||||
dispatch_queue_t image_queue = dispatch_queue_create("wxyz_image_queue", DISPATCH_QUEUE_CONCURRENT);
|
||||
dispatch_group_t image_group = dispatch_group_create();
|
||||
|
||||
TFProductionChapterModel *imageCollectionModel = [TFProductionChapterModel modelWithDictionary:t_dic];
|
||||
|
||||
if (!imageCollectionModel) {
|
||||
|
||||
// 单个章节下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, imageCollectionModel.production_id, imageCollectionModel.chapter_id);
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
[t_chapterModelArray addObject:imageCollectionModel];
|
||||
|
||||
// 解析任务 下载一话内的图片组
|
||||
for (TFImageListModel *imageModel in imageCollectionModel.image_list) {
|
||||
dispatch_group_async(image_group, image_queue, ^{
|
||||
|
||||
dispatch_group_enter(image_group);
|
||||
|
||||
// 创建图片下载器
|
||||
AFURLSessionManager *manager = [[AFURLSessionManager alloc] init];
|
||||
manager.operationQueue.maxConcurrentOperationCount = 2;
|
||||
NSURL *URL = [NSURL URLWithString:imageModel.image];
|
||||
|
||||
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:[NSURLRequest requestWithURL:URL] progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
|
||||
|
||||
// 存储文件夹创建
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic]]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// 获取图片名称
|
||||
NSString *imageFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@%@", [TFUtilsHelper formatStringWithInteger:imageCollectionModel.production_id], [TFUtilsHelper formatStringWithInteger:imageCollectionModel.chapter_id], [TFUtilsHelper formatStringWithInteger:imageModel.image_id], [TFUtilsHelper formatStringWithInteger:imageModel.image_update_time]]];
|
||||
|
||||
// 图片地址
|
||||
NSString *imageFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageFileName, [TFViewHelper imageExtensionWithFormatString:[response MIMEType]]]];
|
||||
|
||||
return [NSURL fileURLWithPath:imageFilePath];
|
||||
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
|
||||
// 如果某一张出错就取消下载,删除该章节文件夹
|
||||
if (error) {
|
||||
[errorChapter addObject:[TFUtilsHelper formatStringWithInteger:imageCollectionModel.chapter_id]];
|
||||
}
|
||||
dispatch_group_leave(image_group);
|
||||
}];
|
||||
[downloadTask resume];
|
||||
});
|
||||
}
|
||||
dispatch_group_notify(image_group, image_queue, ^{
|
||||
|
||||
// 删除图片出错章节
|
||||
if (errorChapter.count > 0) {
|
||||
// 删除出错章节文件夹
|
||||
[[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:imageCollectionModel.production_id chapter_ids:errorChapter productionType:TFProductionTypeComic];
|
||||
[[weakSelf comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:identify_fail forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)];
|
||||
[weakSelf comicRecordWriteToPlistWithProduction_id:imageCollectionModel.production_id];
|
||||
|
||||
// 单个章节下载失败
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, imageCollectionModel.production_id, imageCollectionModel.chapter_id);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
// 下载成功 覆盖临时下载记录
|
||||
[[weakSelf comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:imageCollectionModel forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)];
|
||||
[weakSelf comicRecordWriteToPlistWithProduction_id:imageCollectionModel.production_id];
|
||||
|
||||
// 单个章节下载完成
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (weakSelf.downloadChapterStateChangeBlock) {
|
||||
weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFinished, imageCollectionModel.production_id, imageCollectionModel.chapter_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
dispatch_semaphore_signal(collection_semaphore);
|
||||
});
|
||||
|
||||
dispatch_semaphore_wait(collection_semaphore, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.requesting = NO;
|
||||
if (weakSelf.requestOrderArray.count == 0) {
|
||||
|
||||
// 替换已下载记录章节目录内容
|
||||
TFProductionModel *t_productionModel = [[WXYZ_DownloadHelper sharedManager] getDownloadProductionModelWithProduction_id:production_id productionType:TFProductionTypeComic];
|
||||
NSMutableArray *t_chapterList = [t_productionModel.chapter_list mutableCopy];
|
||||
for (int i = 0; i < t_chapterList.count; i++) {
|
||||
TFProductionChapterModel *t_chapterModel = [t_chapterList objectAtIndex:i];
|
||||
for (TFProductionChapterModel *tt_chapterModel in t_chapterModelArray) {
|
||||
if (tt_chapterModel.chapter_id == t_chapterModel.chapter_id) {
|
||||
[t_chapterList replaceObjectAtIndex:i withObject:tt_chapterModel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
t_productionModel.chapter_list = [t_chapterList copy];
|
||||
[[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:t_productionModel productionType:TFProductionTypeComic];
|
||||
|
||||
// 任务完成
|
||||
if (weakSelf.downloadMissionStateChangeBlock) {
|
||||
weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFinished, 0, @[]);
|
||||
}
|
||||
} else {
|
||||
[weakSelf downloadTaskRequestWithProduction_id:production_id];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (NSMutableArray *)requestOrderArray
|
||||
{
|
||||
if (!_requestOrderArray) {
|
||||
_requestOrderArray = [NSMutableArray array];
|
||||
}
|
||||
return _requestOrderArray;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)comicChaptersRecordDictionaryWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] chaptersRecordDownloadDictionaryWithProduction_id:production_id productionType:TFProductionTypeComic modelClass:[TFProductionChapterModel class]];
|
||||
}
|
||||
|
||||
- (BOOL)comicRecordWriteToPlistWithProduction_id:(NSUInteger)production_id
|
||||
{
|
||||
return [[WXYZ_DownloadHelper sharedManager] writeToChapterPlistFileWithProduction_id:production_id productionType:TFProductionTypeComic modelClass:[TFProductionChapterModel class]];
|
||||
}
|
||||
|
||||
@end
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// WXYZ_DownloadHelper.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/4/1.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WXYZ_DownloadManagerProtocol.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// 章节记录文件key
|
||||
#define chapterRecordKey(production_id, chapter_id) [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:production_id], [TFUtilsHelper formatStringWithInteger:chapter_id]]]
|
||||
|
||||
|
||||
@interface WXYZ_DownloadHelper : NSObject
|
||||
|
||||
interface_singleton
|
||||
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 作品下载记录
|
||||
- (void)recordDownloadProductionWithProductionModel:(TFProductionModel *)productionModel productionType:(TFProductionType)productionType;
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除作品文件夹
|
||||
- (void)removeDownloadProductionFolderWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 删除章节文件夹
|
||||
- (BOOL)removeDownloadChapterFolderWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids productionType:(TFProductionType)productionType;
|
||||
/*
|
||||
查
|
||||
**/
|
||||
|
||||
// 作品章节是否下载
|
||||
- (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 下载作品文件夹路径
|
||||
- (NSString *)getDownloadProductionFolderPathWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 下载章节文件夹路径
|
||||
- (NSString *)getDownloadChapterFolderPathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 下载作品记录文件路径
|
||||
- (NSString *)getDownloadProductionRecordPlistFilePathWithProductionType:(TFProductionType)productionType;
|
||||
|
||||
// 获取下载章节数
|
||||
- (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 获取类别下的所有下载作品model
|
||||
- (NSArray <TFProductionModel *> *)getDownloadProductionArrayWithProductionType:(TFProductionType)productionType;
|
||||
|
||||
// 获取某一已下载作品model
|
||||
- (TFProductionModel *)getDownloadProductionModelWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType;
|
||||
|
||||
// 章节记录缓存变量
|
||||
- (NSMutableDictionary *)chaptersRecordDownloadDictionaryWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType modelClass:(__nullable Class)modelClass;
|
||||
|
||||
// 写入章节记录文件
|
||||
- (BOOL)writeToChapterPlistFileWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType modelClass:(Class)modelClass;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
//
|
||||
// WXYZ_DownloadHelper.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/4/1.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
/* 下载缓存结构示意
|
||||
WXYZ_DownloadFileFolder ━┳━ BookDownloadFileFolder ━┳━ WXYZ_DownloadProductionRecordFile.plist
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┣━ 作品文件夹(production_id) ━┳━ WXYZ_DownloadChapterRecordFile.plist
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┣━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┗━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃ ┃
|
||||
┃ ┗━ 作品文件夹(production_id)
|
||||
┃
|
||||
┃
|
||||
┣━ ComicDownloadFileFolder ━┳━ WXYZ_DownloadProductionRecordFile.plist
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┣━ 作品文件夹(production_id) ━┳━ WXYZ_DownloadChapterRecordFile.plist
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┣━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┗━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃ ┃
|
||||
┃ ┗━ 作品文件夹(production_id)
|
||||
┃
|
||||
┗━ AudioDownloadFileFolder ━┳━ WXYZ_DownloadProductionRecordFile.plist
|
||||
┃
|
||||
┃
|
||||
┣━ 作品文件夹(production_id) ━┳━ WXYZ_DownloadChapterRecordFile.plist
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┣━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃ ┃
|
||||
┃ ┃
|
||||
┃ ┗━ 章节文件夹(production_id + chapter_id) ━━ 对应文件 比如.txt/.jpg/.png/.mp3
|
||||
┃
|
||||
┗━ 作品文件夹(production_id)
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#import "WXYZ_DownloadHelper.h"
|
||||
|
||||
@implementation WXYZ_DownloadHelper
|
||||
|
||||
implementation_singleton(WXYZ_DownloadHelper)
|
||||
|
||||
// 作品下载记录
|
||||
- (void)recordDownloadProductionWithProductionModel:(TFProductionModel *)productionModel productionType:(TFProductionType)productionType
|
||||
{
|
||||
if (!productionModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray *t_arr = [self productionRecordDownloadArrayWithProductionType:productionType];
|
||||
for (TFProductionModel *t_model in t_arr) {
|
||||
if (t_model.production_id == productionModel.production_id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[self productionRecordDownloadArrayWithProductionType:productionType] addObject:productionModel];
|
||||
[self writeToPlistFileWithProductionType:productionType];
|
||||
}
|
||||
|
||||
// 删除作品下载记录
|
||||
- (void)removeRecordDownloadProductionWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
NSMutableArray *t_arr = [[self productionRecordDownloadArrayWithProductionType:productionType] mutableCopy];
|
||||
for (TFProductionModel *t_model in t_arr) {
|
||||
if (t_model.production_id == production_id) {
|
||||
[[self productionRecordDownloadArrayWithProductionType:productionType] removeObject:t_model];
|
||||
[self writeToPlistFileWithProductionType:productionType];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除章节下载记录(返回是否成功,如果成功那么需要在对应的downloadManager中,重置记录变量,从本地重新获取)
|
||||
- (BOOL)removeRecordDownloadChapterWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids productionType:(TFProductionType)productionType
|
||||
{
|
||||
NSMutableDictionary *localCacheDic = [NSMutableDictionary dictionaryWithContentsOfFile:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterRecordPlistFilePathWithProduction_id:production_id productionType:productionType]];
|
||||
for (NSString *chapter_id in chapter_ids) {
|
||||
[localCacheDic removeObjectForKey:chapterRecordKey(production_id, [chapter_id integerValue])];
|
||||
}
|
||||
|
||||
NSMutableDictionary *save_dic = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in localCacheDic.allKeys) {
|
||||
|
||||
id value = [localCacheDic objectForKey:key];
|
||||
|
||||
if ([value isKindOfClass:[NSClassFromString(@"WXYZ_ComicReaderModel") class]]) {
|
||||
value = [value modelToJSONString];
|
||||
}
|
||||
[save_dic setObject:value forKey:key];
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (![save_dic writeToFile:[self getDownloadChapterRecordPlistFilePathWithProduction_id:production_id productionType:productionType] atomically:NO]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 删除作品文件夹
|
||||
- (void)removeDownloadProductionFolderWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
// 删除plist文件记录
|
||||
[self removeRecordDownloadProductionWithProduction_id:production_id productionType:productionType];
|
||||
|
||||
// 删除对应作品整体文件夹
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self getDownloadProductionFolderPathWithProduction_id:production_id productionType:productionType] error:nil];
|
||||
}
|
||||
|
||||
// 删除章节文件夹
|
||||
- (BOOL)removeDownloadChapterFolderWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids productionType:(TFProductionType)productionType
|
||||
{
|
||||
for (NSString *chapter_id in chapter_ids) {
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[self getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:[chapter_id integerValue] productionType:productionType]]) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[self getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:[chapter_id integerValue] productionType:productionType] error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// 删除章节plist文件记录
|
||||
return [self removeRecordDownloadChapterWithProduction_id:production_id chapter_ids:chapter_ids productionType:productionType];
|
||||
}
|
||||
|
||||
// 作品章节是否下载
|
||||
- (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
if ([[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:chapter_id productionType:productionType] error:nil].count > 0) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 获取下载章节数
|
||||
- (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
// 存在已下载章节内容文件夹数量
|
||||
CGFloat chapterDownloadNumber = 0;
|
||||
|
||||
// 获取作品下全部章节文件夹名称
|
||||
NSArray <NSString *>*chapterFolderNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self getDownloadProductionFolderPathWithProduction_id:production_id productionType:productionType] error:nil];
|
||||
|
||||
for (NSString *folderName in chapterFolderNames) {
|
||||
NSString *chapterFloderPath = [[self getDownloadProductionFolderPathWithProduction_id:production_id productionType:productionType] stringByAppendingPathComponent:folderName];
|
||||
|
||||
BOOL isDirectory = NO;
|
||||
[[NSFileManager defaultManager] fileExistsAtPath:chapterFloderPath isDirectory:&isDirectory];
|
||||
if (isDirectory && [[NSFileManager defaultManager] contentsOfDirectoryAtPath:chapterFloderPath error:nil].count > 0) {
|
||||
chapterDownloadNumber ++;
|
||||
}
|
||||
}
|
||||
|
||||
return chapterDownloadNumber;
|
||||
}
|
||||
|
||||
// 获取类别下的所有下载作品
|
||||
- (NSArray <TFProductionModel *> *)getDownloadProductionArrayWithProductionType:(TFProductionType)productionType
|
||||
{
|
||||
return [self productionRecordDownloadArrayWithProductionType:productionType];
|
||||
}
|
||||
|
||||
- (TFProductionModel *)getDownloadProductionModelWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
for (TFProductionModel *t_model in [self productionRecordDownloadArrayWithProductionType:productionType]) {
|
||||
if (t_model.production_id == production_id) {
|
||||
return t_model;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 主文件夹路径
|
||||
- (NSString *)getDownloadRootFolderPathWithProductionType:(TFProductionType)productionType
|
||||
{
|
||||
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectOrNilAtIndex:0];
|
||||
|
||||
// 主目录文件夹
|
||||
NSString *rootFolderPath = [documentPath stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:@"WXYZ_DownloadFileFolder"]];
|
||||
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:rootFolderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:rootFolderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// 作品类别文件夹
|
||||
NSString *typeFolderPath = @"";
|
||||
switch (productionType) {
|
||||
case TFProductionTypeNovel: // 存储全部下载书籍
|
||||
typeFolderPath = [rootFolderPath stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:@"BookDownloadFileFolder"]];
|
||||
break;
|
||||
case TFProductionTypeComic: // 存储全部下载漫画
|
||||
typeFolderPath = [rootFolderPath stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:@"ComicDownloadFileFolder"]];
|
||||
break;
|
||||
case TFProductionTypeAudio: // 存储全部下载听书
|
||||
typeFolderPath = [rootFolderPath stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:@"AudioDownloadFileFolder"]];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:typeFolderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:typeFolderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
return typeFolderPath;
|
||||
}
|
||||
|
||||
// 下载作品文件夹
|
||||
- (NSString *)getDownloadProductionFolderPathWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
NSString *productionFolderPath = [[self getDownloadRootFolderPathWithProductionType:productionType] stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:productionType], [TFUtilsHelper formatStringWithInteger:production_id]]]];
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:productionFolderPath] && production_id != 0) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:productionFolderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
return productionFolderPath;
|
||||
}
|
||||
|
||||
// 下载章节文件夹
|
||||
- (NSString *)getDownloadChapterFolderPathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
NSString *chapterFolderPath = [[self getDownloadProductionFolderPathWithProduction_id:production_id productionType:productionType] stringByAppendingPathComponent:[TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@", [TFUtilsHelper formatStringWithInteger:productionType], [TFUtilsHelper formatStringWithInteger:production_id], [TFUtilsHelper formatStringWithInteger:chapter_id]]]];
|
||||
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:chapterFolderPath] && production_id != 0) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:chapterFolderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
return chapterFolderPath;
|
||||
}
|
||||
|
||||
// 下载作品记录文件路径
|
||||
- (NSString *)getDownloadProductionRecordPlistFilePathWithProductionType:(TFProductionType)productionType
|
||||
{
|
||||
NSString *productionRecordFilePath = [[self getDownloadRootFolderPathWithProductionType:productionType] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [TFUtilsHelper stringToMD5:@"WXYZ_DownloadProductionRecordFile"]]];
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:productionRecordFilePath]) {
|
||||
[@[] writeToFile:productionRecordFilePath atomically:NO];
|
||||
}
|
||||
|
||||
return productionRecordFilePath;
|
||||
}
|
||||
|
||||
// 下载章节记录文件路径
|
||||
- (NSString *)getDownloadChapterRecordPlistFilePathWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType
|
||||
{
|
||||
// 记录下载状态plist文件地址
|
||||
NSString *chapterRecordFilePath = [[self getDownloadProductionFolderPathWithProduction_id:production_id productionType:productionType] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [TFUtilsHelper stringToMD5:@"WXYZ_DownloadChapterRecordFile"]]];
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:chapterRecordFilePath]) {
|
||||
[@{} writeToFile:chapterRecordFilePath atomically:NO];
|
||||
}
|
||||
|
||||
return chapterRecordFilePath;
|
||||
}
|
||||
|
||||
// 作品记录缓存变量
|
||||
static NSMutableDictionary *_productionsRecordDictionary;
|
||||
- (NSMutableArray *)productionRecordDownloadArrayWithProductionType:(TFProductionType)productionType;
|
||||
{
|
||||
if (!_productionsRecordDictionary) {
|
||||
// 此全局临时变量的作用是记录不同作品的下载记录,减少了文件的读写,增加访问效率
|
||||
_productionsRecordDictionary = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSMutableArray *t_arr = [_productionsRecordDictionary objectForKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:productionType]]];
|
||||
if (!t_arr) {
|
||||
|
||||
NSMutableArray *localArr = [NSMutableArray arrayWithContentsOfFile:[self getDownloadProductionRecordPlistFilePathWithProductionType:productionType]];
|
||||
|
||||
t_arr = [NSMutableArray array];
|
||||
|
||||
for (NSString *json in localArr) {
|
||||
[t_arr addObject:[TFProductionModel modelWithJSON:json]];
|
||||
}
|
||||
|
||||
[_productionsRecordDictionary setObject:t_arr forKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:productionType]]];
|
||||
}
|
||||
|
||||
return t_arr;
|
||||
}
|
||||
|
||||
// 写入作品记录文件
|
||||
- (BOOL)writeToPlistFileWithProductionType:(TFProductionType)productionType;
|
||||
{
|
||||
NSMutableArray *t_downloadArr = [self productionRecordDownloadArrayWithProductionType:productionType];
|
||||
|
||||
NSMutableArray *saveArr = [NSMutableArray array];
|
||||
|
||||
for (TFProductionModel *t_model in t_downloadArr) {
|
||||
[saveArr addObject:[t_model modelToJSONString]];
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (![saveArr writeToFile:[self getDownloadProductionRecordPlistFilePathWithProductionType:productionType] atomically:NO]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 章节记录缓存变量
|
||||
static NSMutableDictionary *_chaptersRecordDictionary;
|
||||
- (NSMutableDictionary *)chaptersRecordDownloadDictionaryWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType modelClass:(Class)modelClass
|
||||
{
|
||||
if (!_chaptersRecordDictionary) {
|
||||
// 此全局临时变量的作用是记录不同作品的下载记录,减少了文件的读写,增加访问效率
|
||||
_chaptersRecordDictionary = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSMutableDictionary *t_dic = [_chaptersRecordDictionary objectForKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:production_id]]];
|
||||
if (!t_dic || t_dic.count == 0) {
|
||||
|
||||
NSMutableDictionary *localDic = [NSMutableDictionary dictionaryWithContentsOfFile:[self getDownloadChapterRecordPlistFilePathWithProduction_id:production_id productionType:productionType]];
|
||||
|
||||
t_dic = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in localDic.allKeys) {
|
||||
id value = [localDic objectForKey:key];
|
||||
if ([value isKindOfClass:[NSDictionary class]]) {
|
||||
[t_dic setObject:value forKey:key];
|
||||
} else if ([value isEqualToString:identify_fail]) {
|
||||
[t_dic setObject:value forKey:key];
|
||||
} else if (![value isEqual:identify_downloading] && value) {
|
||||
[t_dic setObject:[modelClass modelWithJSON:value] forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
[_chaptersRecordDictionary setObject:t_dic forKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:production_id]]];
|
||||
}
|
||||
|
||||
return t_dic;
|
||||
}
|
||||
|
||||
// 写入章节记录文件
|
||||
- (BOOL)writeToChapterPlistFileWithProduction_id:(NSInteger)production_id productionType:(TFProductionType)productionType modelClass:(Class)modelClass
|
||||
{
|
||||
NSMutableDictionary *t_downloadDic = [self chaptersRecordDownloadDictionaryWithProduction_id:production_id productionType:productionType modelClass:modelClass];
|
||||
if (t_downloadDic.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *save_dic = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in t_downloadDic.allKeys) {
|
||||
|
||||
id value = [t_downloadDic objectForKey:key];
|
||||
|
||||
if ([value isKindOfClass:[modelClass class]]) {
|
||||
value = [value modelToJSONString];
|
||||
}
|
||||
[save_dic setObject:value forKey:key];
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (![save_dic writeToFile:[self getDownloadChapterRecordPlistFilePathWithProduction_id:production_id productionType:productionType] atomically:NO]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// WXYZ_DownloadManagerEnumProtocol.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/13.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_DownloadChapterState) {
|
||||
WXYZ_DownloadStateChapterDownloadNormal, // 章节下载未开始
|
||||
WXYZ_DownloadStateChapterDownloadStart, // 章节下载开始
|
||||
WXYZ_DownloadStateChapterDownloadFinished, // 章节下载完成
|
||||
WXYZ_DownloadStateChapterDownloadFail // 章节下载失败
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_DownloadMissionState) {
|
||||
WXYZ_DownloadStateMissionShouldPay, // 任务失败需要支付
|
||||
WXYZ_DownloadStateMissionNormal, // 任务未开始
|
||||
WXYZ_DownloadStateMissionStart, // 任务开始
|
||||
WXYZ_DownloadStateMissionFinished, // 任务完成
|
||||
WXYZ_DownloadStateMissionFail // 任务失败
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_ProductionDownloadState) {
|
||||
WXYZ_ProductionDownloadStateNormal = 0, // 普通状态
|
||||
WXYZ_ProductionDownloadStateDownloading = 1, // 下载中
|
||||
WXYZ_ProductionDownloadStateDownloaded = 2, // 已下载
|
||||
WXYZ_ProductionDownloadStateFail = 3, // 失败
|
||||
WXYZ_ProductionDownloadStateSelected = 4 // 选中
|
||||
};
|
||||
|
||||
#define identify_downloading @"id_downloading"
|
||||
|
||||
#define identify_fail @"id_fail"
|
||||
|
||||
@protocol WXYZ_DownloadManagerEnumProtocol <NSObject>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// WXYZ_DownloadHelperProtocol.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/4/1.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WXYZ_DownloadManagerEnumProtocol.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol WXYZ_DownloadManagerProtocol <NSObject>
|
||||
|
||||
@optional
|
||||
/*
|
||||
增
|
||||
**/
|
||||
|
||||
// 下载章节
|
||||
- (void)downloadChapterWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 下载多个章节
|
||||
- (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *>*)chapter_ids;
|
||||
|
||||
/*
|
||||
删
|
||||
**/
|
||||
|
||||
// 删除本地已下载作品
|
||||
- (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 删除本地已下载章节
|
||||
- (void)removeDownloadChaptersWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray <NSString *> *)chapter_ids;
|
||||
|
||||
/*
|
||||
查
|
||||
**/
|
||||
|
||||
// 获取下载章节数
|
||||
- (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 获取全部已下载章节model
|
||||
- (NSArray *__nullable)getDownloadChapterModelArrayWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 获取已下载章节model
|
||||
- (id)getDownloadChapterModelWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 获取章节下载状态
|
||||
- (WXYZ_ProductionDownloadState)getChapterDownloadStateWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 章节是否下载
|
||||
- (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// NSDate+TFExtension.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSDate (TFExtension)
|
||||
|
||||
+ (NSString *)chindDateFormate:(NSDate *)date;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// NSDate+TFExtension.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSDate+TFExtension.h"
|
||||
|
||||
@implementation NSDate (TFExtension)
|
||||
|
||||
+ (NSString *)chindDateFormate:(NSDate *)update
|
||||
{
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
|
||||
NSString *destDateString = [dateFormatter stringFromDate:update];
|
||||
|
||||
return destDateString;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// TFIAPManager.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, TFIAPFiledCode) {
|
||||
TFIAP_FILEDCOED_APPLECODE = 0, // 苹果返回错误信息
|
||||
TFIAP_FILEDCOED_NORIGHT = 1, // 用户禁止应用内付费购买
|
||||
TFIAP_FILEDCOED_EMPTYGOODS = 2, // 商品为空
|
||||
TFIAP_FILEDCOED_CANNOTGETINFORMATION = 3, // 无法获取产品信息,请重试
|
||||
TFIAP_FILEDCOED_BUYFILED = 4, // 购买失败,请重试
|
||||
TFIAP_FILEDCOED_USERCANCEL = 5, // 用户取消交易
|
||||
TFIAP_FILEDCOED_BUYING = 6, // 商品正在请求
|
||||
TFIAP_FILEDCOED_NOTLOGGEDIN = 7 //
|
||||
};
|
||||
|
||||
|
||||
@protocol TFIAPManagerResultsDelegate <NSObject>
|
||||
// 请求成功
|
||||
- (void)requestSuccess;
|
||||
// 请求失败
|
||||
- (void)filedWithErrorCode:(NSInteger)errorCode andError:(NSString *)error;
|
||||
@end
|
||||
|
||||
@interface TFIAPManager : NSObject
|
||||
|
||||
interface_singleton
|
||||
|
||||
// 作品id - 用于充值来源统计
|
||||
@property (nonatomic ,assign) NSInteger production_id;
|
||||
|
||||
// 作品类型 - 用于充值来源统计
|
||||
@property (nonatomic ,assign) TFProductionType productionType;
|
||||
|
||||
@property (nonatomic ,weak) id<TFIAPManagerResultsDelegate>delegate;
|
||||
|
||||
/**
|
||||
启动工具
|
||||
*/
|
||||
- (void)startManager;
|
||||
|
||||
/**
|
||||
结束工具
|
||||
*/
|
||||
- (void)stopManager;
|
||||
|
||||
/**
|
||||
请求商品列表
|
||||
*/
|
||||
- (void)requestProductWithId:(NSString *)productId;
|
||||
|
||||
- (void)checkIAPFiles;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,339 @@
|
||||
//
|
||||
// TFIAPManager.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFIAPManager.h"
|
||||
#import "TFSandBoxHelper.h"
|
||||
#import "NSDate+TFExtension.h"
|
||||
|
||||
static NSString * const receiptKey = @"receipt_key";
|
||||
static NSString * const dateKey = @"date_key";
|
||||
static NSString * const userIdKey = @"userId_key";
|
||||
|
||||
dispatch_queue_t iap_queue() {
|
||||
static dispatch_queue_t as_iap_queue;
|
||||
static dispatch_once_t onceToken_iap_queue;
|
||||
dispatch_once(&onceToken_iap_queue, ^{
|
||||
as_iap_queue = dispatch_queue_create([[NSString stringWithFormat:@"com.%@.queue", app_key] cStringUsingEncoding:NSUTF8StringEncoding], DISPATCH_QUEUE_CONCURRENT);
|
||||
});
|
||||
return as_iap_queue;
|
||||
}
|
||||
|
||||
@interface TFIAPManager ()<SKPaymentTransactionObserver, SKProductsRequestDelegate>
|
||||
|
||||
@property (nonatomic ,assign) BOOL isRequestFinished; // 判断请求是否完成
|
||||
@property (nonatomic ,copy) NSString *receipt; // 交易成功后拿到的一个64编码字符串
|
||||
@property (nonatomic ,copy) NSString *date; // 交易时间
|
||||
@property (nonatomic ,copy) NSString *userId; // 交易人
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFIAPManager
|
||||
|
||||
implementation_singleton(TFIAPManager)
|
||||
|
||||
/***
|
||||
内购支付两个阶段:
|
||||
1.app直接向苹果服务器请求商品,支付阶段;
|
||||
2.苹果服务器返回凭证,app向公司服务器发送验证,公司再向苹果服务器验证阶段;
|
||||
*/
|
||||
|
||||
/**
|
||||
阶段一正在进中,app退出。
|
||||
在程序启动时,设置监听,监听是否有未完成订单,有的话恢复订单。
|
||||
*/
|
||||
|
||||
// 开启监听
|
||||
- (void)startManager
|
||||
{
|
||||
dispatch_async(iap_queue(), ^{
|
||||
|
||||
self.isRequestFinished = YES;
|
||||
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
||||
|
||||
[self checkIAPFiles];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopManager
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - 开始请求苹果商品
|
||||
- (void)requestProductWithId:(NSString *)productId
|
||||
{
|
||||
if (!TFUserInfoManager.isLogin) {
|
||||
[TFLoginOptionsViewController presentLoginView:nil];
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_NOTLOGGEDIN error:nil];
|
||||
self.isRequestFinished = YES; // 商品信息错误
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isRequestFinished) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"正在连接App Store")];
|
||||
|
||||
if ([SKPaymentQueue canMakePayments]) { // 用户允许app内购
|
||||
if (productId.length) {
|
||||
|
||||
self.isRequestFinished = NO;
|
||||
|
||||
NSArray *product = [[NSArray alloc] initWithObjects:productId, nil];
|
||||
NSSet *set = [NSSet setWithArray:product];
|
||||
SKProductsRequest *productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
|
||||
productRequest.delegate = self;
|
||||
[productRequest start];
|
||||
|
||||
} else {
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_EMPTYGOODS error:nil];
|
||||
self.isRequestFinished = YES; // 商品信息错误
|
||||
}
|
||||
} else { // 用户未允许app内购
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_NORIGHT error:nil];
|
||||
self.isRequestFinished = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
|
||||
{
|
||||
NSArray *product = response.products;
|
||||
|
||||
if (product.count == 0) {
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_CANNOTGETINFORMATION error:nil];
|
||||
self.isRequestFinished = YES; // 失败,请求完成
|
||||
} else {
|
||||
// 发起购买请求
|
||||
SKPayment *payment = [SKPayment paymentWithProduct:product[0]];
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)completeTransaction:(SKPaymentTransaction *)transaction
|
||||
{
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
self.isRequestFinished = YES; // 成功,请求完成
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
|
||||
{
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_APPLECODE error:[error localizedDescription]];
|
||||
self.isRequestFinished = YES; // 失败,请求完成
|
||||
}
|
||||
|
||||
#pragma mark - 购买操作后的回调
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions
|
||||
{
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchasing: // 正在交易
|
||||
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"商品正在请求中")];
|
||||
break;
|
||||
|
||||
case SKPaymentTransactionStatePurchased: { // 交易完成
|
||||
[self getReceipt]; // 获取交易成功后的购买凭证
|
||||
[self saveReceipt]; // 存储交易凭证
|
||||
[self checkIAPFiles]; // 发送凭证服务器验证是否有效
|
||||
[self completeTransaction:transaction];
|
||||
}
|
||||
break;
|
||||
|
||||
case SKPaymentTransactionStateFailed: // 交易失败
|
||||
[self failedTransaction:transaction];
|
||||
break;
|
||||
|
||||
case SKPaymentTransactionStateRestored: // 已经购买过该商品
|
||||
[self restoreTransaction:transaction];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取交易成功后的购买凭证
|
||||
- (void)getReceipt
|
||||
{
|
||||
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
|
||||
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
|
||||
self.receipt = [receiptData base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
- (void)failedTransaction:(SKPaymentTransaction *)transaction
|
||||
{
|
||||
if(transaction.error.code != SKErrorPaymentCancelled) { // 交易失败
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_BUYFILED error:nil];
|
||||
} else { // 取消交易
|
||||
[self filedWithErrorCode:TFIAP_FILEDCOED_USERCANCEL error:nil];
|
||||
}
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
|
||||
self.isRequestFinished = YES; // 失败,请求完成
|
||||
}
|
||||
|
||||
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
|
||||
{
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
self.isRequestFinished = YES; // 恢复购买,请求完成
|
||||
}
|
||||
|
||||
#pragma mark 将购买凭证存储到本地,验证凭证失败时,App再次启动后会重新验证购买凭证
|
||||
- (void)saveReceipt
|
||||
{
|
||||
self.date = [NSDate chindDateFormate:[NSDate date]];
|
||||
NSString *fileName = [TFUtilsHelper getUDID];
|
||||
self.userId = @"UserID";
|
||||
NSString *savedPath = [NSString stringWithFormat:@"%@/%@.plist", [TFSandBoxHelper iapReceiptPath], fileName];
|
||||
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
self.receipt, receiptKey,
|
||||
self.date, dateKey,
|
||||
self.userId, userIdKey,
|
||||
nil];
|
||||
[dic writeToFile:savedPath atomically:YES];
|
||||
}
|
||||
|
||||
- (void)checkIAPFiles
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
// 搜索该目录下的所有文件和目录
|
||||
NSArray *cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:[TFSandBoxHelper iapReceiptPath] error:&error];
|
||||
if (error == nil) {
|
||||
for (NSString *name in cacheFileNameArray) {
|
||||
if ([name hasSuffix:@".plist"]){
|
||||
NSString *filePath = [NSString stringWithFormat:@"%@/%@", [TFSandBoxHelper iapReceiptPath], name];
|
||||
[self sendAppStoreRequestBuyPlist:filePath];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 获取本地存储的购买凭证失败
|
||||
[self.delegate filedWithErrorCode:TFIAP_FILEDCOED_APPLECODE andError:error.description];
|
||||
}
|
||||
}
|
||||
|
||||
// 验证支付凭证
|
||||
- (void)sendAppStoreRequestBuyPlist:(NSString *)plistPath
|
||||
{
|
||||
NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||||
// 这里的参数请根据自己公司后台服务器接口定制,但是必须发送的是持久化保存购买凭证
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:[dic objectForKey:receiptKey], receiptKey, [dic objectForKey:dateKey], dateKey, [dic objectForKey:userIdKey], userIdKey, nil];
|
||||
|
||||
// 发送购买凭证至服务器
|
||||
if ([params objectForKey:@"receipt_key"]) {
|
||||
[self applePayBackRequest:[params objectForKey:@"receipt_key"]];
|
||||
} else {
|
||||
[self.delegate filedWithErrorCode:TFIAP_FILEDCOED_APPLECODE andError:TFLocalizedString(@"支付凭证验证失败!")];
|
||||
}
|
||||
}
|
||||
|
||||
// 验证成功,移除本地记录的购买凭证
|
||||
- (void)removeReceipt
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if ([fileManager fileExistsAtPath:[TFSandBoxHelper iapReceiptPath]]) {
|
||||
[fileManager removeItemAtPath:[TFSandBoxHelper iapReceiptPath] error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 苹果凭证验证
|
||||
- (void)applePayBackRequest:(NSString *)receipt
|
||||
{
|
||||
WS(weakSelf)
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"获取相关支付凭证")];
|
||||
|
||||
// 统计充值来源数据
|
||||
NSMutableDictionary *parameter = [NSMutableDictionary dictionary];
|
||||
[parameter setObject:receipt forKey:@"receipt"];
|
||||
if (self.production_id > 0) {
|
||||
[parameter setObject:[TFUtilsHelper formatStringWithInteger:self.production_id] forKey:@"book_id"];
|
||||
switch (self.productionType) {
|
||||
case TFProductionTypeNovel:
|
||||
case TFProductionTypeAi:
|
||||
[parameter setObject:@"1" forKey:@"content_type"];
|
||||
break;
|
||||
case TFProductionTypeComic:
|
||||
[parameter setObject:@"2" forKey:@"content_type"];
|
||||
break;
|
||||
case TFProductionTypeAudio:
|
||||
[parameter setObject:@"3" forKey:@"content_type"];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[TFNetworkTools POST:Apple_Pay_Back parameters:[parameter copy] model:nil success:^(BOOL isSuccess, id _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
[weakSelf requestSuccess];
|
||||
} else {
|
||||
[weakSelf filedWithErrorCode:TFIAP_FILEDCOED_BUYFILED error:nil];
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[weakSelf filedWithErrorCode:TFIAP_FILEDCOED_BUYFILED error:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark 错误信息反馈
|
||||
- (void)requestSuccess
|
||||
{
|
||||
[self removeReceipt];
|
||||
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"支付成功")];
|
||||
|
||||
// 发送全局购买成功通知
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Recharge_Success object:nil];
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(requestSuccess)]) {
|
||||
[self.delegate requestSuccess];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)filedWithErrorCode:(NSInteger)code error:(NSString *)error
|
||||
{
|
||||
switch (code) {
|
||||
case TFIAP_FILEDCOED_APPLECODE:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:error];
|
||||
break;
|
||||
|
||||
case TFIAP_FILEDCOED_NORIGHT:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"请您开启内购支付")];
|
||||
break;
|
||||
|
||||
case TFIAP_FILEDCOED_EMPTYGOODS:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"商品获取出错")];
|
||||
break;
|
||||
|
||||
case TFIAP_FILEDCOED_CANNOTGETINFORMATION:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"商品获取出错")];
|
||||
break;
|
||||
|
||||
case TFIAP_FILEDCOED_USERCANCEL:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"您已取消交易")];
|
||||
break;
|
||||
|
||||
case TFIAP_FILEDCOED_BUYING:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"交易正在进行")];
|
||||
break;
|
||||
case TFIAP_FILEDCOED_NOTLOGGEDIN:
|
||||
break;
|
||||
|
||||
default:
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"支付失败,请稍后重试")];
|
||||
break;
|
||||
}
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(filedWithErrorCode:andError:)]) {
|
||||
[self.delegate filedWithErrorCode:code andError:error];
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// TFSandBoxHelper.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFSandBoxHelper : NSObject
|
||||
|
||||
+ (NSString *)homePath; // 程序主目录,可见子目录(3个):Documents、Library、tmp
|
||||
|
||||
+ (NSString *)appPath; // 程序目录,不能存任何东西
|
||||
|
||||
+ (NSString *)docPath; // 文档目录,需要ITUNES同步备份的数据存这里,可存放用户数据
|
||||
|
||||
+ (NSString *)libPrefPath; // 配置目录,配置文件存这里
|
||||
|
||||
+ (NSString *)libCachePath; // 缓存目录,系统永远不会删除这里的文件,ITUNES会删除
|
||||
|
||||
+ (NSString *)tmpPath; // 临时缓存目录,APP退出后,系统可能会删除这里的内容
|
||||
|
||||
+ (NSString *)iapReceiptPath; // 用于存储iap内购返回的购买凭证
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// TFSandBoxHelper.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/10.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFSandBoxHelper.h"
|
||||
|
||||
@implementation TFSandBoxHelper
|
||||
|
||||
+ (NSString *)homePath
|
||||
{
|
||||
return NSHomeDirectory();
|
||||
}
|
||||
|
||||
+ (NSString *)appPath
|
||||
{
|
||||
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
|
||||
return [paths objectOrNilAtIndex:0];
|
||||
}
|
||||
|
||||
+ (NSString *)docPath
|
||||
{
|
||||
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
return [paths objectOrNilAtIndex:0];
|
||||
}
|
||||
|
||||
+ (NSString *)libPrefPath
|
||||
{
|
||||
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
||||
return [[paths objectOrNilAtIndex:0] stringByAppendingFormat:@"/Preferences"];
|
||||
}
|
||||
|
||||
+ (NSString *)libCachePath
|
||||
{
|
||||
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
||||
return [[paths objectOrNilAtIndex:0] stringByAppendingFormat:@"/Caches"];
|
||||
}
|
||||
|
||||
+ (NSString *)tmpPath
|
||||
{
|
||||
return [NSHomeDirectory() stringByAppendingFormat:@"/tmp"];
|
||||
}
|
||||
|
||||
+ (NSString *)iapReceiptPath
|
||||
{
|
||||
NSString *path = [[self libPrefPath] stringByAppendingFormat:@"/EACEF35FE363A75A"];
|
||||
[self hasLive:path];
|
||||
return path;
|
||||
}
|
||||
|
||||
+ (BOOL)hasLive:(NSString *)path
|
||||
{
|
||||
if (NO == [[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
return [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:NULL];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// TFKeyboardManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFKeyboardManager : NSObject
|
||||
|
||||
// 键盘高度改变
|
||||
@property (nonatomic ,copy) void (^keyboardHeightChanged)(CGFloat keyboardHeight, CGFloat shouldMoveDistance, CGRect shouldMoveFrame);
|
||||
// 输入框与键盘间距 default 10
|
||||
@property (nonatomic ,assign) CGFloat spacingFromKeyboard;
|
||||
// 是否添加键盘工具条 default YES
|
||||
@property (nonatomic ,assign) BOOL showToolBar;
|
||||
// 主移动视图
|
||||
@property (nonatomic ,strong) UIView *adaptiveMovementView;
|
||||
|
||||
- (instancetype)initObserverWithAdaptiveMovementView:(UIView *)adaptiveMovementView;
|
||||
|
||||
- (void)startKeyboardObserver;
|
||||
|
||||
- (void)stopKeyboardObserver;
|
||||
|
||||
+ (void)hideKeyboard;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// TFKeyboardManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFKeyboardManager.h"
|
||||
|
||||
@interface TFKeyboardManager ()
|
||||
|
||||
@property (nonatomic ,strong) UIToolbar *toolBar;
|
||||
@property (nonatomic ,strong) UIView *baseView;
|
||||
@property (nonatomic ,assign) CGFloat moveDistance; // 被遮挡时,控件需移动距离
|
||||
@property (nonatomic ,assign) NSValue *superViewFrame;
|
||||
@property (nonatomic ,assign) BOOL isShowkeyboard; // 键盘是否显示
|
||||
@property (nonatomic ,assign) BOOL isObserver; // 监听是否已添加
|
||||
@property (nonatomic ,strong) UIBarButtonItem *doneItem;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFKeyboardManager
|
||||
|
||||
- (instancetype)initObserverWithAdaptiveMovementView:(UIView *)adaptiveMovementView
|
||||
{
|
||||
if (self = [super init]) {
|
||||
|
||||
self.baseView = adaptiveMovementView;
|
||||
|
||||
_spacingFromKeyboard = 10;
|
||||
_showToolBar = YES;
|
||||
|
||||
self.isShowkeyboard = NO;
|
||||
self.isObserver = NO;
|
||||
|
||||
[self toolBar];
|
||||
[self startKeyboardObserver];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startKeyboardObserver
|
||||
{
|
||||
if (self.isObserver) {
|
||||
return;
|
||||
}
|
||||
self.isObserver = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(firstResponderDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(firstResponderDidBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowWithNotification:) name:UIKeyboardWillShowNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideWithNotification:) name:UIKeyboardWillHideNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(switchLanguage) name:Notification_Switch_Language object:nil];
|
||||
}
|
||||
|
||||
- (void)switchLanguage
|
||||
{
|
||||
self.doneItem.title = TFLocalizedString(@"完成");
|
||||
}
|
||||
|
||||
- (void)stopKeyboardObserver
|
||||
{
|
||||
if (!self.isObserver) {
|
||||
return;
|
||||
}
|
||||
self.isObserver = NO;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)firstResponderDidBeginEditing:(NSNotification *)notification
|
||||
{
|
||||
[self setKeyboardToolBarForView:notification.object];
|
||||
}
|
||||
|
||||
- (void)keyboardWillShowWithNotification:(NSNotification *)notification
|
||||
{
|
||||
CGRect startFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
|
||||
if (startFrame.size.height > 0 && !self.isShowkeyboard) {
|
||||
|
||||
self.isShowkeyboard = YES;
|
||||
|
||||
UITextView *firstResponder = (UITextView *)[self findFirstResponderFromView:self.baseView];
|
||||
// 屏蔽网页
|
||||
if ([firstResponder isKindOfClass:NSClassFromString(@"WKContentView")] || !self.baseView || !firstResponder) {
|
||||
return ;
|
||||
}
|
||||
|
||||
[self setKeyboardToolBarForView:firstResponder];
|
||||
|
||||
CGFloat keyboardHeight = [self getKeyboardSizeWithKeyboardInfo:[notification userInfo]].height;
|
||||
|
||||
CGRect firstResponderFrame = [self.baseView convertRect:firstResponder.frame toView:kMainWindow];
|
||||
|
||||
self.moveDistance = CGRectGetMaxY(firstResponderFrame) - (kMainWindow.height - keyboardHeight) + _spacingFromKeyboard;
|
||||
|
||||
// 没有遮挡输入框
|
||||
if (self.moveDistance < 0) {
|
||||
self.moveDistance = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
self.superViewFrame = [NSValue valueWithCGRect:self.baseView.frame];
|
||||
|
||||
// 移动后的frame
|
||||
CGRect newSuperViewFrame = CGRectMake(self.baseView.origin.x, CGRectGetMaxY(self.baseView.frame) - self.moveDistance - self.baseView.size.height, self.baseView.size.width, self.baseView.size.height);
|
||||
|
||||
if (self.keyboardHeightChanged) {
|
||||
NSLog(@"%lf", self.moveDistance);
|
||||
self.keyboardHeightChanged(keyboardHeight, - self.moveDistance, newSuperViewFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyboardWillHideWithNotification:(NSNotification *)notification
|
||||
{
|
||||
if (self.moveDistance > 0 && self.isShowkeyboard) {
|
||||
if (self.keyboardHeightChanged) {
|
||||
self.keyboardHeightChanged(0, self.moveDistance, self.superViewFrame.CGRectValue);
|
||||
}
|
||||
}
|
||||
|
||||
self.isShowkeyboard = NO;
|
||||
self.moveDistance = 0;
|
||||
self.superViewFrame = nil;
|
||||
}
|
||||
|
||||
#pragma mark - tool
|
||||
- (UIView *)findFirstResponderFromView:(UIView *)view
|
||||
{
|
||||
if (view.isFirstResponder) {
|
||||
return view;
|
||||
}
|
||||
for (UIView *subView in view.subviews) {
|
||||
UIView * firstResponder = [self findFirstResponderFromView:subView];
|
||||
if (firstResponder != nil) {
|
||||
return firstResponder;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGSize)getKeyboardSizeWithKeyboardInfo:(NSDictionary *)keyboardInfo
|
||||
{
|
||||
NSValue *value = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
|
||||
return [value CGRectValue].size;
|
||||
}
|
||||
|
||||
- (void)setKeyboardToolBarForView:(UITextView *)view
|
||||
{
|
||||
if (self.showToolBar) {
|
||||
if (!view.inputAccessoryView || view.inputAccessoryView == nil) {
|
||||
view.inputAccessoryView = self.toolBar;
|
||||
}
|
||||
} else {
|
||||
view.inputAccessoryView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAdaptiveMovementView:(UIView *)adaptiveMovementView
|
||||
{
|
||||
_adaptiveMovementView = adaptiveMovementView;
|
||||
self.baseView = adaptiveMovementView;
|
||||
}
|
||||
|
||||
- (UIToolbar *)toolBar
|
||||
{
|
||||
if (!_toolBar) {
|
||||
_toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 40)];
|
||||
_toolBar.barStyle = UIBarStyleDefault;
|
||||
|
||||
UIBarButtonItem *flexibleSpaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||||
|
||||
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithTitle:TFLocalizedString(@"完成") style:UIBarButtonItemStyleDone target:self action:@selector(hideKeyboard)];
|
||||
self.doneItem = doneItem;
|
||||
|
||||
[_toolBar setItems:@[flexibleSpaceItem, doneItem]];
|
||||
}
|
||||
return _toolBar;
|
||||
}
|
||||
|
||||
- (void)hideKeyboard
|
||||
{
|
||||
[TFKeyboardManager hideKeyboard];
|
||||
}
|
||||
|
||||
+ (void)hideKeyboard
|
||||
{
|
||||
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self stopKeyboardObserver];
|
||||
}
|
||||
@end
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// TFNetworkManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreTelephony/CTCellularData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
/*** 详细的网络类型 ***/
|
||||
typedef NS_ENUM(NSUInteger, TFNetworkType) {
|
||||
/** 默认类型 WWAN */
|
||||
TFNetworkTypeDefault = 0,
|
||||
/** 无网络 */
|
||||
TFNetworkTypeNone = 1,
|
||||
/** 未知网络 */
|
||||
TFNetworkTypeUnknown = 2,
|
||||
/** LTE */
|
||||
TFNetworkTypeLTE = 3,
|
||||
/** WiFi */
|
||||
TFNetworkTypeWiFi = 4,
|
||||
/** 2G */
|
||||
TFNetworkType2G = 5,
|
||||
/** 3G */
|
||||
TFNetworkType3G = 6,
|
||||
/** 4G */
|
||||
TFNetworkType4G = 7,
|
||||
|
||||
};
|
||||
|
||||
@interface TFNetworkManager : NSObject
|
||||
|
||||
/// 实时获取APP网络访问状态
|
||||
/// @param complete 一个回调,返回APP的网络访问状态
|
||||
+ (void)cellularDataRestrictionDidUpdateNotifier:(void(^)(CTCellularDataRestrictedState status))complete;
|
||||
|
||||
/// 获取APP网络访问状态
|
||||
+ (CTCellularDataRestrictedState)currentNetworkStatus;
|
||||
|
||||
/// 获取网络类型
|
||||
+ (TFNetworkType)currentNetworkType;
|
||||
|
||||
/// 获取APP的联网状态,YES:有网络,NO:无网络
|
||||
+ (BOOL)networkingStatus;
|
||||
|
||||
/// 实时获取APP的联网状态
|
||||
/// @param complete 一个回调,返回APP的联网状态,YES:有网络,NO:无网络
|
||||
+ (void)networkingStatus:(void(^)(BOOL status))complete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// TFNetworkManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFNetworkManager.h"
|
||||
|
||||
@interface TFNetworkManager ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFNetworkManager
|
||||
|
||||
+ (void)cellularDataRestrictionDidUpdateNotifier:(void(^)(CTCellularDataRestrictedState status))complete
|
||||
{
|
||||
CTCellularData *cellularData = [[CTCellularData alloc] init];
|
||||
cellularData.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState status) {
|
||||
if (complete) {
|
||||
complete(status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+ (CTCellularDataRestrictedState)currentNetworkStatus
|
||||
{
|
||||
if (TARGET_IPHONE_SIMULATOR) return kCTCellularDataNotRestricted;
|
||||
|
||||
CTCellularDataRestrictedState __block state;
|
||||
CTCellularData *cellularData = [[CTCellularData alloc] init];
|
||||
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
|
||||
cellularData.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState status) {
|
||||
state = status;
|
||||
dispatch_semaphore_signal(signal);
|
||||
};
|
||||
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
|
||||
cellularData.cellularDataRestrictionDidUpdateNotifier = nil;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
+ (TFNetworkType)currentNetworkType
|
||||
{
|
||||
if (@available(iOS 13.0, *)) {
|
||||
|
||||
UIStatusBarManager *statusBarManager = kRCodeSync([UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager);
|
||||
id statusBar = nil;
|
||||
|
||||
// 获取当前的状态栏
|
||||
if (![statusBarManager respondsToSelector:NSSelectorFromString(@"createLocalStatusBar")]) return TFNetworkTypeUnknown;
|
||||
|
||||
SEL selector = NSSelectorFromString(@"createLocalStatusBar");
|
||||
IMP imp = [statusBarManager methodForSelector:selector];
|
||||
UIView * (*func) (id, SEL) = (void *)imp;
|
||||
UIView *localStatusBar = kRCodeSync(func(statusBarManager, selector));
|
||||
|
||||
if ([localStatusBar respondsToSelector:NSSelectorFromString(@"statusBar")]) {
|
||||
SEL selector = NSSelectorFromString(@"statusBar");
|
||||
IMP imp = [localStatusBar methodForSelector:selector];
|
||||
id (*func) (id, SEL) = (void *)imp;
|
||||
statusBar = func(localStatusBar, selector);
|
||||
}
|
||||
|
||||
if (!statusBar) return TFNetworkTypeUnknown;
|
||||
|
||||
id currentData = [[statusBar valueForKeyPath:@"_statusBar"] valueForKeyPath:@"currentData"];
|
||||
id wifiEntry = [currentData valueForKeyPath:@"wifiEntry"];
|
||||
id cellularEntry = [currentData valueForKeyPath:@"cellularEntry"];
|
||||
if (wifiEntry && [[wifiEntry valueForKeyPath:@"isEnabled"] boolValue]) {
|
||||
return TFNetworkTypeWiFi;
|
||||
}
|
||||
|
||||
if (cellularEntry && [[cellularEntry valueForKeyPath:@"isEnabled"] boolValue]) {
|
||||
NSNumber *type = [cellularEntry valueForKeyPath:@"type"];
|
||||
if (![type boolValue]) return TFNetworkTypeUnknown;
|
||||
|
||||
switch (type.integerValue) {
|
||||
case 0:
|
||||
return TFNetworkTypeNone;
|
||||
case 1:
|
||||
return TFNetworkType2G;
|
||||
case 4:
|
||||
return TFNetworkType3G;
|
||||
case 5:
|
||||
return TFNetworkType4G;
|
||||
|
||||
default:
|
||||
return TFNetworkTypeDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iOS13.0以下的处理方法
|
||||
YYReachability* reach = [YYReachability reachabilityWithHostname:@"www.baidu.com"];
|
||||
|
||||
if (reach.status == YYReachabilityStatusNone) {
|
||||
return TFNetworkTypeNone;
|
||||
} else if (reach.status == YYReachabilityStatusWWAN) {
|
||||
return TFNetworkType4G;
|
||||
} else {
|
||||
return TFNetworkTypeWiFi;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)networkingStatus
|
||||
{
|
||||
BOOL originStatus = [AFNetworkReachabilityManager sharedManager].reachable;
|
||||
if (originStatus == NO) {
|
||||
TFNetworkType currentStatus = [self currentNetworkType];
|
||||
if (currentStatus == TFNetworkTypeNone || currentStatus == TFNetworkTypeUnknown) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (void)networkingStatus:(void(^)(BOOL status))complete
|
||||
{
|
||||
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
|
||||
|
||||
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
|
||||
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi) {
|
||||
if (complete) {
|
||||
complete(YES);
|
||||
}
|
||||
} else {
|
||||
if (complete) {
|
||||
complete(NO);
|
||||
}
|
||||
}
|
||||
}];
|
||||
[manager startMonitoring];
|
||||
}
|
||||
|
||||
@end
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>-1</key>
|
||||
<string>无效的URL地址</string>
|
||||
<key>-999</key>
|
||||
<string>无效的URL地址</string>
|
||||
<key>-1000</key>
|
||||
<string>无效的URL地址</string>
|
||||
<key>-1001</key>
|
||||
<string>网络连接超时</string>
|
||||
<key>-1002</key>
|
||||
<string>不支持的URL地址</string>
|
||||
<key>-1003</key>
|
||||
<string>找不到服务器</string>
|
||||
<key>-1004</key>
|
||||
<string>连接不上服务器</string>
|
||||
<key>-1005</key>
|
||||
<string>网络连接异常</string>
|
||||
<key>-1006</key>
|
||||
<string>DNS查询失败</string>
|
||||
<key>-1007</key>
|
||||
<string>HTTP请求重定向</string>
|
||||
<key>-1008</key>
|
||||
<string>资源不可用</string>
|
||||
<key>-1009</key>
|
||||
<string>当前无网络连接</string>
|
||||
<key>-1010</key>
|
||||
<string>重定向到不存在的位置</string>
|
||||
<key>-1011</key>
|
||||
<string>服务器响应异常</string>
|
||||
<key>-1012</key>
|
||||
<string>用户取消授权</string>
|
||||
<key>-1013</key>
|
||||
<string>需要用户授权</string>
|
||||
<key>-1014</key>
|
||||
<string>0字节资源</string>
|
||||
<key>-1015</key>
|
||||
<string>无法解码原始数据</string>
|
||||
<key>-1016</key>
|
||||
<string>无法解码内容数据</string>
|
||||
<key>-1017</key>
|
||||
<string>无法解析响应</string>
|
||||
<key>-1018</key>
|
||||
<string>国际漫游关闭</string>
|
||||
<key>-1019</key>
|
||||
<string>被叫激活</string>
|
||||
<key>-1020</key>
|
||||
<string>数据不被允许</string>
|
||||
<key>-1021</key>
|
||||
<string>请求体</string>
|
||||
<key>-1100</key>
|
||||
<string>文件不存在</string>
|
||||
<key>-1101</key>
|
||||
<string>文件是个目录</string>
|
||||
<key>-1102</key>
|
||||
<string>无读取文件权限</string>
|
||||
<key>-1103</key>
|
||||
<string>请求数据长度超出最大限度</string>
|
||||
<key>-1200</key>
|
||||
<string>安全连接失败</string>
|
||||
<key>-1201</key>
|
||||
<string>服务器证书失效</string>
|
||||
<key>-1202</key>
|
||||
<string>不被信任的服务器证书</string>
|
||||
<key>-1203</key>
|
||||
<string>未知Root的服务器证书</string>
|
||||
<key>-1204</key>
|
||||
<string>服务器证书未生效</string>
|
||||
<key>-1205</key>
|
||||
<string>客户端证书被拒</string>
|
||||
<key>-1206</key>
|
||||
<string>需要客户端证书</string>
|
||||
<key>-2000</key>
|
||||
<string>无法从网络获取</string>
|
||||
<key>-3000</key>
|
||||
<string>无法创建文件</string>
|
||||
<key>-3001</key>
|
||||
<string>无法打开文件</string>
|
||||
<key>-3002</key>
|
||||
<string>无法关闭文件</string>
|
||||
<key>-3003</key>
|
||||
<string>无法写入文件</string>
|
||||
<key>-3004</key>
|
||||
<string>无法删除文件</string>
|
||||
<key>-3005</key>
|
||||
<string>无法移动文件</string>
|
||||
<key>-3006</key>
|
||||
<string>下载解码数据失败</string>
|
||||
<key>-3007</key>
|
||||
<string>下载解码数据失败</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// TFNetworkTools.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFNetworking.h"
|
||||
|
||||
|
||||
@class TFNetworkRequestModel;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^ _Nullable requestSuccessBlock) (BOOL isSuccess, id _Nullable t_model, TFNetworkRequestModel *requestModel);
|
||||
typedef void(^ _Nullable quickRequestSuccessBlock) (BOOL isSuccess, id _Nullable t_model, BOOL isCache, TFNetworkRequestModel *requestModel);
|
||||
typedef void(^ _Nullable requestFailedBlock) (NSURLSessionDataTask * _Nullable task, NSError *error);
|
||||
|
||||
@interface TFNetworkTools : UIViewController
|
||||
|
||||
/// POST request
|
||||
/// @param url 请求地址
|
||||
/// @param parameters 请求参数
|
||||
/// @param model 数据类型,设置为nil时返回的t_model将是原始字典
|
||||
/// @param success 成功回调
|
||||
/// @param failure 失败回调
|
||||
+ (void)POST:(NSString *)url parameters:(NSDictionary * _Nullable)parameters model:(Class _Nullable)model success:(requestSuccessBlock)success failure:(requestFailedBlock)failure;
|
||||
|
||||
|
||||
/// POST request,带缓存
|
||||
/// @param url 请求地址
|
||||
/// @param parameters 请求参数
|
||||
/// @param model 数据类型,设置为nil时返回的t_model将是原始字典
|
||||
/// @param success 成功回调
|
||||
/// @param failure 失败回调
|
||||
+ (void)POSTQuick:(NSString *)url parameters:(NSDictionary * _Nullable)parameters model:(Class _Nullable)model success:(quickRequestSuccessBlock)success failure:(requestFailedBlock)failure;
|
||||
|
||||
|
||||
/// POST request,可以设置回调队列
|
||||
/// @param url 请求地址
|
||||
/// @param parameters 请求参数
|
||||
/// @param model 数据类型,设置为nil时返回的t_model将是原始字典
|
||||
/// @param completionQueue 回调队列
|
||||
/// @param success 成功回调
|
||||
/// @param failure 失败回调
|
||||
+ (void)POST:(NSString *)url parameters:(NSDictionary * _Nullable)parameters model:(Class _Nullable)model completionQueue:(dispatch_queue_t)completionQueue success:(requestSuccessBlock)success failure:(requestFailedBlock)failure;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TFNetworkRequestModel : NSObject
|
||||
|
||||
@property (nonatomic ,strong ,nullable) NSURLSessionDataTask *task;
|
||||
|
||||
@property (nonatomic ,strong ,nullable) NSDictionary *data;
|
||||
|
||||
@property (nonatomic ,strong ,nullable) NSString *msg;
|
||||
|
||||
@property (nonatomic ,assign) NSInteger code;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// TFNetworkTools.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFNetworkTools.h"
|
||||
#import <XHNetworkCache.h>
|
||||
#import "CommonCrypto/CommonDigest.h"
|
||||
#import "NSString+TFExtension.h"
|
||||
|
||||
// 请求成功
|
||||
#define Request_Success_From(x) [[NSString stringWithFormat:@"%@",[x objectForKey:@"code"]] isEqualToString:@"0"]
|
||||
|
||||
static NSString * const kCode = @"code";
|
||||
static NSString * const kMsg = @"msg";
|
||||
static NSString * const kData = @"data";
|
||||
|
||||
@implementation TFNetworkTools
|
||||
|
||||
+ (void)POST:(NSString *)url parameters:(NSDictionary * _Nullable)parameters model:(Class _Nullable)model success:(requestSuccessBlock)success failure:(requestFailedBlock)failure
|
||||
{
|
||||
[self POST:url parameters:parameters model:model completionQueue:dispatch_get_main_queue() success:success failure:failure];
|
||||
}
|
||||
|
||||
+ (void)POSTQuick:(NSString *)url parameters:(NSDictionary * _Nullable)parameters model:(Class _Nullable)model success:(quickRequestSuccessBlock)success failure:(requestFailedBlock)failure
|
||||
{
|
||||
// 读取缓存数据,若存在则返回缓存数据
|
||||
NSString *prefix = [url stringByAppendingString:TFLanguageManager.serverLocalized];
|
||||
if ([XHNetworkCache checkCacheWithURL:prefix params:parameters]) {
|
||||
NSDictionary *dic = [NSDictionary dictionaryWithDictionary:[XHNetworkCache cacheJsonWithURL:prefix params:parameters]];
|
||||
TFNetworkRequestModel *requestModel = [[TFNetworkRequestModel alloc] init];
|
||||
requestModel.data = dic;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
!success ?: success(YES, model ? [model modelWithDictionary:dic] : dic, YES, requestModel);
|
||||
});
|
||||
}
|
||||
|
||||
[self POST:url parameters:parameters model:model completionQueue:dispatch_get_main_queue() success:^(BOOL isSuccess, id _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
[XHNetworkCache save_asyncJsonResponseToCacheFile:model ? [t_model modelToJSONObject] : requestModel.data andURL:prefix params:parameters completed:nil];
|
||||
}
|
||||
!success ?: success(isSuccess, t_model, NO, requestModel);
|
||||
} failure:failure];
|
||||
}
|
||||
|
||||
+ (void)POST:(NSString *)url parameters:(NSDictionary *)parameters model:(Class _Nullable)model completionQueue:(dispatch_queue_t)completionQueue success:(requestSuccessBlock)success failure:(requestFailedBlock)failure
|
||||
{
|
||||
// 没有网络则直接返回
|
||||
if (![TFNetworkManager networkingStatus]) return;
|
||||
|
||||
TFLog(@"--->请求地址URL:%@",url);
|
||||
url = [APIURL stringByAppendingString:url];
|
||||
url = url.networkUrl;
|
||||
parameters = [self integrationRequestWithParameters:parameters ?: @{}];
|
||||
|
||||
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
|
||||
securityPolicy.allowInvalidCertificates = YES;
|
||||
securityPolicy.validatesDomainName = NO;
|
||||
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
manager.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
manager.requestSerializer = [AFJSONRequestSerializer serializer];
|
||||
|
||||
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
[manager.requestSerializer setTimeoutInterval:kOvertime];
|
||||
manager.completionQueue = completionQueue;
|
||||
|
||||
TFLog(@"--->请求参数%@",manager);
|
||||
|
||||
[manager POST:url parameters:parameters headers:@{@"Content-Type":@"application/json", @"Accept":@"application/json"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
||||
|
||||
TFLog(@"--->请求成功返回%@",responseObject);
|
||||
|
||||
NSDictionary *dic = [NSDictionary dictionaryWithDictionary:responseObject];
|
||||
TFNetworkRequestModel *requestModel = [[TFNetworkRequestModel alloc] init];
|
||||
requestModel.task = task;
|
||||
requestModel.data = dic[kData];
|
||||
requestModel.msg = [NSString stringWithFormat:@"%@", dic[kMsg]];
|
||||
requestModel.code = [dic[kCode] integerValue];
|
||||
|
||||
if (requestModel.code != 704) { // 704 请求重复排除
|
||||
!success ?: success(Request_Success_From(dic), model ? [model modelWithDictionary:dic[kData]] : dic, requestModel);
|
||||
}
|
||||
|
||||
[manager.session finishTasksAndInvalidate];
|
||||
if (requestModel.code == 301 || requestModel.code == 302) {
|
||||
[kNotification postNotificationName:Notification_Logout object:nil];
|
||||
}
|
||||
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
TFLog(@"--->请求失败返回:%@",error);
|
||||
!failure ?: failure(task, error);
|
||||
[manager.session finishTasksAndInvalidate];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSString *)authString:(NSDictionary *)params
|
||||
{
|
||||
NSArray *allKeys = params.allKeys;
|
||||
allKeys = [allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
|
||||
return [obj1 compare:obj2 options:NSNumericSearch];
|
||||
}];
|
||||
|
||||
NSMutableArray *signArray = [NSMutableArray array];
|
||||
for (NSString *key in allKeys) {
|
||||
NSString *keyValue = [NSString stringWithFormat:@"%@=%@", key, params[key]];
|
||||
[signArray addObject:keyValue];
|
||||
}
|
||||
|
||||
NSString *signString = [signArray componentsJoinedByString:@"&"];
|
||||
signString = [NSString stringWithFormat:@"%@%@%@", app_key, signString, secret_key];
|
||||
|
||||
return [self stringToMD5:signString];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)integrationRequestWithParameters:(NSDictionary *)parameters
|
||||
{
|
||||
NSMutableDictionary *integrationParament = [NSMutableDictionary dictionaryWithDictionary:parameters];
|
||||
[integrationParament setObject:App_Ver forKey:@"ver"];
|
||||
[integrationParament setObject:@"1" forKey:@"osType"];
|
||||
[integrationParament setObject:@"1" forKey:@"appId"];
|
||||
[integrationParament setObject:@"1" forKey:@"product"];
|
||||
[integrationParament setObject:@"AppStore" forKey:@"marketChannel"];
|
||||
[integrationParament setObject:[[NSBundle mainBundle] bundleIdentifier] forKey:@"packageName"];
|
||||
[integrationParament setObject:[TFUtilsHelper getTimeStamp] ? : @"" forKey:@"time"];
|
||||
[integrationParament setObject:[TFUtilsHelper getUDID] ? : @"" forKey:@"udid"];
|
||||
[integrationParament setObject:[TFUtilsHelper getSystemVersion] ? : @"" forKey:@"sysVer"];
|
||||
[integrationParament setObject:[TFUtilsHelper getCurrentDeviceModel] ? : @"" forKey:@"phoneModel"];
|
||||
[integrationParament setObject:[TFUserInfoManager shareInstance].token forKey:@"token"];
|
||||
[integrationParament setObject:@"zh" forKey:@"language"];
|
||||
// [integrationParament setObject:[TFLanguageManager serverLocalized] forKey:@"language"];
|
||||
[integrationParament setObject:[self authString:integrationParament] ? : @"" forKey:@"sign"];
|
||||
|
||||
return integrationParament;
|
||||
}
|
||||
|
||||
+ (NSString *)stringToMD5:(NSString *)inputStr
|
||||
{
|
||||
const char *cStr = [inputStr UTF8String];
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(cStr,(CC_LONG)strlen(cStr), result);
|
||||
NSString *resultStr = [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
return [resultStr lowercaseString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation TFNetworkRequestModel
|
||||
|
||||
@end
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// TFURLProtocol.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFURLProtocol : NSURLProtocol
|
||||
|
||||
@property (nonatomic ,assign ,readonly) BOOL isSwizzle;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/// 开始监听
|
||||
+ (void)startMonitor;
|
||||
|
||||
/// 停止监听
|
||||
+ (void)stopMonitor;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@interface TFSessionConfig : NSObject
|
||||
|
||||
+ (void)swizzleSelector;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// TFURLProtocol.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFURLProtocol.h"
|
||||
#import "AppDelegate.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// 拦截标识符,避免无限循环
|
||||
static NSString * const kProtocolIdentifier = @"kProtocolIdentifier";
|
||||
// 默认微信Token请求接口前缀
|
||||
static NSString * const kDefaultTokenURL = @"https://api.weixin.qq.com/sns/oauth2/access_token?";
|
||||
|
||||
@interface TFURLProtocol ()<NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSURLSessionDelegate>
|
||||
|
||||
@property (nonatomic ,strong) NSURLSessionDataTask *dataTask;
|
||||
@property (nonatomic ,strong) NSOperationQueue *sessionDelegateQueue;
|
||||
@property (nonatomic ,strong) NSURLResponse *response;
|
||||
@property (nonatomic ,weak) AppDelegate *delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFURLProtocol
|
||||
|
||||
+ (void)startMonitor
|
||||
{
|
||||
[NSURLProtocol registerClass:self];
|
||||
[self sharedInstance];
|
||||
[_instance load];
|
||||
}
|
||||
|
||||
+ (void)stopMonitor
|
||||
{
|
||||
[NSURLProtocol unregisterClass:self];
|
||||
[self sharedInstance];
|
||||
[_instance unload];
|
||||
}
|
||||
|
||||
static TFURLProtocol *_instance;
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_instance = [super allocWithZone:zone];
|
||||
});
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// 拦截网络请求
|
||||
/// @param request 需要拦截的请求
|
||||
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
|
||||
{
|
||||
if ([NSURLProtocol propertyForKey:kProtocolIdentifier inRequest:request]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 拦截微信获取Token的网络请求。
|
||||
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
|
||||
AppDelegate * __block delegate;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
_instance.delegate = delegate;
|
||||
dispatch_semaphore_signal(signal);
|
||||
});
|
||||
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
|
||||
NSString *prefix = delegate.checkSettingModel.wechatTokenURL ?: kDefaultTokenURL;
|
||||
if ([request.URL.absoluteString hasPrefix:prefix]) {
|
||||
NSURLComponents *components = [[NSURLComponents alloc] initWithString:request.URL.absoluteString];
|
||||
[components.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSString *name = obj.name;
|
||||
NSString *value = obj.value;
|
||||
if ([name isEqualToString:@"code"]) {
|
||||
delegate.wechatCode = value;
|
||||
}
|
||||
}];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
/// 返回自定义的网络请求
|
||||
/// @param request 网络请求
|
||||
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [[NSURLRequest alloc] init];
|
||||
}
|
||||
|
||||
// 重写父类的开始加载方法
|
||||
- (void)startLoading
|
||||
{
|
||||
NSURLSessionConfiguration *configuration =
|
||||
[NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
|
||||
self.sessionDelegateQueue = [[NSOperationQueue alloc] init];
|
||||
self.sessionDelegateQueue.maxConcurrentOperationCount = 1;
|
||||
self.sessionDelegateQueue.name = @"com.beiwo.queue";
|
||||
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:self.sessionDelegateQueue];
|
||||
|
||||
self.dataTask = [session dataTaskWithRequest:self.request];
|
||||
[self.dataTask resume];
|
||||
}
|
||||
|
||||
// 结束加载
|
||||
- (void)stopLoading
|
||||
{
|
||||
[self.dataTask cancel];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
||||
{
|
||||
if (!error) {
|
||||
[self.client URLProtocolDidFinishLoading:self];
|
||||
} else if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
|
||||
} else {
|
||||
[self.client URLProtocol:self didFailWithError:error];
|
||||
}
|
||||
self.dataTask = nil;
|
||||
}
|
||||
|
||||
// 当服务端返回信息时,这个回调函数会被ULS调用,在这里实现http返回信息的截取
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data
|
||||
{
|
||||
[self.client URLProtocol:self didLoadData:data];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
|
||||
{
|
||||
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
|
||||
self.response = response;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
|
||||
{
|
||||
if (response != nil) {
|
||||
self.response = response;
|
||||
|
||||
[[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)load
|
||||
{
|
||||
if (_isSwizzle == YES) return;
|
||||
|
||||
_isSwizzle = YES;
|
||||
|
||||
[TFSessionConfig swizzleSelector];
|
||||
}
|
||||
|
||||
- (void)unload
|
||||
{
|
||||
if (_isSwizzle == NO) return;
|
||||
|
||||
_isSwizzle = NO;
|
||||
|
||||
[TFSessionConfig swizzleSelector];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation TFSessionConfig
|
||||
|
||||
+ (void)swizzleSelector
|
||||
{
|
||||
Class originCls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
|
||||
Method fromMethod = class_getInstanceMethod(originCls, @selector(protocolClasses));
|
||||
Method toMethed = class_getInstanceMethod(self.class, @selector(protocolClasses));
|
||||
|
||||
if (!fromMethod || !toMethed) {
|
||||
[NSException raise:NSInternalInconsistencyException format:@"无法加载NEURLSessionConfiguration。"];
|
||||
}
|
||||
method_exchangeImplementations(fromMethod, toMethed);
|
||||
}
|
||||
|
||||
- (NSArray *)protocolClasses
|
||||
{
|
||||
return @[[TFURLProtocol class]];
|
||||
}
|
||||
|
||||
@end
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// TFReadRecordManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TFBookMarkModel;
|
||||
@interface TFReadRecordManager : NSObject
|
||||
|
||||
+ (instancetype)shareManagerWithProductionType:(TFProductionType)productionType;
|
||||
|
||||
// 添加阅读记录
|
||||
- (BOOL)addReadingRecordWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id chapterTitle:(NSString *)chapterTitle;
|
||||
|
||||
// 获取某个作品当前阅读章节id
|
||||
- (NSInteger)getReadingRecordChapter_idWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 获取某个作品当前阅读章节名称
|
||||
- (NSString *)getReadingRecordChapterTitleWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 作品章节是否已读
|
||||
- (BOOL)chapterHasReadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 作品是否已读
|
||||
- (BOOL)productionHasReadedWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 设置漫画阅读记录(仅限漫画使用)
|
||||
- (void)setComicReadingRecord:(NSInteger)comic_id chapter_id:(NSInteger)chapter_id offsetY:(CGFloat)offsetY;
|
||||
|
||||
/// 获取漫画阅读记录(章节ID :offsetY)(仅限漫画使用)
|
||||
- (NSDictionary *)getComicReadingRecord:(NSInteger)comic_id;
|
||||
|
||||
// 设置播放器进度(仅限有声功能使用)
|
||||
- (void)addPlayingProgress:(CGFloat)progress production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 播放作品进度(仅限有声功能使用)
|
||||
- (CGFloat)getPlayingProgressWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
// 是否是当前正在播放的作品(仅限有声功能使用)
|
||||
- (BOOL)isCurrentPlayingProductionWithProduction_id:(NSInteger)production_id;
|
||||
|
||||
// 是否是当前正在播放的章节(仅限有声功能使用)
|
||||
- (BOOL)isCurrentPlayingChapterWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id;
|
||||
|
||||
/* 添加书签
|
||||
@param bookID 书籍ID
|
||||
@param chapterID 章节ID
|
||||
@param chapterSort 章节索引
|
||||
@param chapterTitle 章节标题
|
||||
@param chapterContent 章节内容
|
||||
@param pageContent 当前页内容
|
||||
**/
|
||||
+ (TFBookMarkModel *)addBookMark:(NSString *)bookID chapterID:(NSString *)chapterID chapterSort:(NSInteger)chapterSort chapterTitle:(NSString *)chapterTitle chapterContent:(NSString *)chapterContent pageContent:(NSString *)pageContent;
|
||||
|
||||
/* 获取书籍所有书签
|
||||
@param bookID 书籍ID
|
||||
**/
|
||||
+ (NSArray<TFBookMarkModel *> *)bookMark:(NSString *)bookID;
|
||||
|
||||
/* 获取章节所有书签
|
||||
@param bookID 书籍ID
|
||||
@param chapterID 章节ID
|
||||
**/
|
||||
+ (NSArray<TFBookMarkModel *> *)bookMark:(NSString *)bookID chapterID:(NSString *)chapterID;
|
||||
|
||||
/* 删除指定书签
|
||||
@param markModel 书签Model
|
||||
**/
|
||||
+ (void)removeBookMark:(TFBookMarkModel *)markModel;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
//
|
||||
// TFReadRecordManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFReadRecordManager.h"
|
||||
#import "TFBookMarkModel.h"
|
||||
|
||||
// 当前正在阅读作品id key
|
||||
#define current_read_production_id_cache_key(type) ([NSString stringWithFormat:@"%@_%@", @"UkhU98p5", [TFUtilsHelper formatStringWithInteger:type]])
|
||||
|
||||
// 某个作品正在阅读的章节标题 key
|
||||
#define current_read_production_title_cache_key(type, production_id) ([NSString stringWithFormat:@"%@%@%@", @"SOdYb5CO", [TFUtilsHelper formatStringWithInteger:type], [TFUtilsHelper formatStringWithInteger:production_id]])
|
||||
|
||||
// 某个作品正在阅读的章节id
|
||||
#define current_read_chapter_id_cache_key(type, production_id) ([NSString stringWithFormat:@"%@_%@_%@", @"KBOwoFvR", [TFUtilsHelper formatStringWithInteger:type], [TFUtilsHelper formatStringWithInteger:production_id]])
|
||||
|
||||
// 某个章节的播放进度 key
|
||||
#define current_playing_chapter_progress(type, production_id) ([NSString stringWithFormat:@"%@_%@_%@", @"TLIo2gaz", [TFUtilsHelper formatStringWithInteger:type], [TFUtilsHelper formatStringWithInteger:production_id]])
|
||||
|
||||
// 某个章节是否已读 key
|
||||
#define has_readed_cache_key(type, production_id ) ([NSString stringWithFormat:@"%@%@%@", @"KNU5HliF", [TFUtilsHelper formatStringWithInteger:type], [TFUtilsHelper formatStringWithInteger:production_id]])
|
||||
|
||||
@interface TFReadRecordManager ()
|
||||
|
||||
@property (nonatomic ,strong) NSMutableDictionary *readRecordDictionary;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TFReadRecordManager
|
||||
|
||||
static TFProductionType _productionType;
|
||||
static TFReadRecordManager *_instance_record;
|
||||
|
||||
+ (instancetype)shareManagerWithProductionType:(TFProductionType)productionType
|
||||
{
|
||||
static dispatch_once_t once_token_record;
|
||||
dispatch_once(&once_token_record, ^{
|
||||
_instance_record = [[self alloc] init];
|
||||
});
|
||||
|
||||
_productionType = productionType;
|
||||
|
||||
return _instance_record;
|
||||
}
|
||||
|
||||
// 添加阅读记录
|
||||
- (BOOL)addReadingRecordWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id chapterTitle:(NSString *)chapterTitle
|
||||
{
|
||||
// 记录当前正在阅读作品id
|
||||
[self.readRecordDictionary setObject:[TFUtilsHelper formatStringWithInteger:production_id] forKey:current_read_production_id_cache_key(_productionType)];
|
||||
|
||||
// 记录当前正在阅读章节id
|
||||
[self.readRecordDictionary setObject:[TFUtilsHelper formatStringWithInteger:chapter_id] forKey:current_read_chapter_id_cache_key(_productionType, production_id)];
|
||||
|
||||
// 记录当前阅读章节名称
|
||||
[self.readRecordDictionary setObject:chapterTitle?:@"" forKey:current_read_production_title_cache_key(_productionType, production_id)];
|
||||
|
||||
// 设置已读作品
|
||||
if (![self chapterHasReadedWithProduction_id:production_id chapter_id:chapter_id]) {
|
||||
NSMutableDictionary *t_dic = [[self.readRecordDictionary objectForKey:has_readed_cache_key(_productionType, production_id)] mutableCopy];
|
||||
if (!t_dic) {
|
||||
t_dic = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[t_dic setObject:@"1" forKey:[TFUtilsHelper formatStringWithInteger:chapter_id]];
|
||||
|
||||
[self.readRecordDictionary setObject:[t_dic copy] forKey:has_readed_cache_key(_productionType, production_id)];
|
||||
}
|
||||
|
||||
return [self writeToPlistFile];
|
||||
}
|
||||
|
||||
// 获取某个作品当前阅读章节id
|
||||
- (NSInteger)getReadingRecordChapter_idWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSString *chapter_id_string = [self.readRecordDictionary objectForKey:current_read_chapter_id_cache_key(_productionType, production_id)];
|
||||
return [chapter_id_string integerValue];
|
||||
}
|
||||
|
||||
// 获取某个作品当前阅读章节名称
|
||||
- (NSString *)getReadingRecordChapterTitleWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSString *chapter_id_string = [self.readRecordDictionary objectForKey:current_read_production_title_cache_key(_productionType, production_id)];
|
||||
return chapter_id_string;
|
||||
}
|
||||
|
||||
// 作品章节是否已读
|
||||
- (BOOL)chapterHasReadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [[self.readRecordDictionary objectForKey:has_readed_cache_key(_productionType, production_id)] mutableCopy];
|
||||
if (t_dic.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [t_dic objectForKey:[TFUtilsHelper formatStringWithInteger:chapter_id]]?YES:NO;
|
||||
}
|
||||
|
||||
// 作品是否已读
|
||||
- (BOOL)productionHasReadedWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [[self.readRecordDictionary objectForKey:has_readed_cache_key(_productionType, production_id)] mutableCopy];
|
||||
if (t_dic.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 设置播放器进度(仅限有声功能使用)
|
||||
- (void)addPlayingProgress:(CGFloat)progress production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSMutableDictionary *t_dic = [[self.readRecordDictionary objectForKey:current_playing_chapter_progress(_productionType, production_id)] mutableCopy];
|
||||
if (!t_dic) {
|
||||
t_dic = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[t_dic setObject:[TFUtilsHelper formatStringWithFloat:progress] forKey:[TFUtilsHelper formatStringWithInteger:chapter_id]];
|
||||
|
||||
[self.readRecordDictionary setObject:[t_dic copy] forKey:current_playing_chapter_progress(_productionType, production_id)];
|
||||
}
|
||||
|
||||
// 播放作品进度(仅限有声功能使用)
|
||||
- (CGFloat)getPlayingProgressWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
NSDictionary *t_dic = [[self.readRecordDictionary objectForKey:current_playing_chapter_progress(_productionType, production_id)] copy];
|
||||
|
||||
NSString *progress = [t_dic objectForKey:[TFUtilsHelper formatStringWithInteger:chapter_id]];
|
||||
if (progress && progress.length > 0) {
|
||||
|
||||
if ([progress floatValue] > 0.95) {
|
||||
return 0;
|
||||
}
|
||||
return [progress floatValue];
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
// 是否是当前正在播放的作品(仅限有声功能使用)
|
||||
- (BOOL)isCurrentPlayingProductionWithProduction_id:(NSInteger)production_id
|
||||
{
|
||||
if ([[self.readRecordDictionary objectForKey:current_read_production_id_cache_key(_productionType)] isEqualToString:[TFUtilsHelper formatStringWithInteger:production_id]]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 是否是当前正在播放的章节(仅限有声功能使用)
|
||||
- (BOOL)isCurrentPlayingChapterWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id
|
||||
{
|
||||
if ([self getReadingRecordChapter_idWithProduction_id:production_id] == chapter_id) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)readRecordDictionary
|
||||
{
|
||||
if (!_readRecordDictionary) {
|
||||
_readRecordDictionary = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSMutableDictionary *t_dic = [_readRecordDictionary objectForKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:_productionType]]];
|
||||
if (!t_dic) {
|
||||
t_dic = [NSMutableDictionary dictionaryWithContentsOfFile:[self cacheFilePath]];
|
||||
[_readRecordDictionary setObject:t_dic forKey:[NSString stringWithFormat:@"%@", [TFUtilsHelper formatStringWithInteger:_productionType]]];
|
||||
}
|
||||
|
||||
return t_dic;
|
||||
}
|
||||
|
||||
- (NSString *)cacheFilePath
|
||||
{
|
||||
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectOrNilAtIndex:0];
|
||||
NSString *rootFloderPath = [documentPath stringByAppendingPathComponent:[@"WXYZ_ReadingRecordFileFloder" md5String]];
|
||||
// 创建章节文件夹
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:rootFloderPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:rootFloderPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
NSString *cacheFilePath = @"";
|
||||
switch (_productionType) {
|
||||
case TFProductionTypeNovel:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"BookReadingRecordFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeComic:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"ComicReadingRecordFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeAudio:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"AudioReadingRecordFile" md5String]]];
|
||||
break;
|
||||
case TFProductionTypeAi:
|
||||
cacheFilePath = [rootFloderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", [@"AiReadingRecordFile" md5String]]];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath]) {
|
||||
[@{} writeToFile:cacheFilePath atomically:NO];
|
||||
}
|
||||
|
||||
return cacheFilePath;
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
- (BOOL)writeToPlistFile
|
||||
{
|
||||
NSDictionary *t_dic = [self.readRecordDictionary copy];
|
||||
if (t_dic.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if (![t_dic writeToFile:[self cacheFilePath] atomically:NO]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (TFBookMarkModel *)addBookMark:(NSString *)bookID chapterID:(NSString *)chapterID chapterSort:(NSInteger)chapterSort chapterTitle:(NSString *)chapterTitle chapterContent:(NSString *)chapterContent pageContent:(NSString *)pageContent
|
||||
{
|
||||
NSString *fullPath = [[self bookMarkRootPath] stringByAppendingFormat:@"/bookMark_%@.plist", bookID];
|
||||
NSMutableDictionary *t_dict = [NSMutableDictionary dictionaryWithContentsOfFile:fullPath];
|
||||
if (!t_dict) {
|
||||
t_dict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSMutableArray<NSString *> *t_arr = t_dict[chapterID];
|
||||
if (!t_arr) {
|
||||
t_arr = [NSMutableArray array];
|
||||
}
|
||||
|
||||
TFBookMarkModel *t_model = [[TFBookMarkModel alloc] init];
|
||||
t_model.chapterID = chapterID;
|
||||
t_model.chapterSort = chapterSort;
|
||||
t_model.chapterTitle = chapterTitle;
|
||||
t_model.timestamp = [TFUtilsHelper getTimeStamp];
|
||||
t_model.pageContent = pageContent;
|
||||
t_model.bookID = bookID;
|
||||
t_model.specificIndex = [chapterContent rangeOfString:pageContent].location;
|
||||
// 将Model转换成String
|
||||
NSString *bookMark = [t_model modelToJSONString];
|
||||
|
||||
[t_arr addObject:bookMark];
|
||||
[t_dict setValue:t_arr forKey:chapterID];
|
||||
|
||||
[t_dict writeToFile:fullPath atomically:YES];
|
||||
|
||||
return t_model;
|
||||
}
|
||||
|
||||
+ (NSArray<TFBookMarkModel *> *)bookMark:(NSString *)bookID
|
||||
{
|
||||
NSString *fullPath = [[self bookMarkRootPath] stringByAppendingFormat:@"/bookMark_%@.plist", bookID];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) return @[];
|
||||
|
||||
NSMutableDictionary *t_dict = [NSMutableDictionary dictionaryWithContentsOfFile:fullPath];
|
||||
NSMutableArray<TFBookMarkModel *> *markArr = [NSMutableArray array];
|
||||
for (NSString *key in t_dict) {
|
||||
NSArray<NSString *> *t_arr = t_dict[key];
|
||||
for (NSString *obj in t_arr) {
|
||||
NSDictionary *dict = [TFUtilsHelper dictionaryWithJsonString:obj];
|
||||
TFBookMarkModel *t_model = [TFBookMarkModel modelWithDictionary:dict];
|
||||
[markArr addObject:t_model];
|
||||
}
|
||||
}
|
||||
|
||||
return markArr;
|
||||
}
|
||||
|
||||
+ (NSArray<TFBookMarkModel *> *)bookMark:(NSString *)bookID chapterID:(NSString *)chapterID
|
||||
{
|
||||
NSString *fullPath = [[self bookMarkRootPath] stringByAppendingFormat:@"/bookMark_%@.plist", bookID];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) return @[];
|
||||
|
||||
NSMutableDictionary *t_dict = [NSMutableDictionary dictionaryWithContentsOfFile:fullPath];
|
||||
NSArray<NSString *> *t_arr = t_dict[chapterID];
|
||||
if (!t_arr) return @[];
|
||||
|
||||
NSMutableArray<TFBookMarkModel *> *markArr = [NSMutableArray array];
|
||||
for (NSString *obj in t_arr) {
|
||||
NSDictionary *dict = [TFUtilsHelper dictionaryWithJsonString:obj];
|
||||
TFBookMarkModel *t_model = [TFBookMarkModel modelWithDictionary:dict];
|
||||
[markArr addObject:t_model];
|
||||
}
|
||||
|
||||
return markArr;
|
||||
}
|
||||
|
||||
+ (void)removeBookMark:(TFBookMarkModel *)markModel
|
||||
{
|
||||
NSString *fullPath = [[self bookMarkRootPath] stringByAppendingFormat:@"/bookMark_%@.plist", markModel.bookID];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) return;
|
||||
|
||||
NSMutableDictionary *t_dict = [NSMutableDictionary dictionaryWithContentsOfFile:fullPath];
|
||||
NSMutableArray<NSString *> *t_arr = t_dict[markModel.chapterID];
|
||||
if (!t_arr) return;
|
||||
|
||||
NSString *bookMark = [markModel modelToJSONString];
|
||||
[t_arr removeObject:bookMark];
|
||||
[t_dict setValue:t_arr forKey:markModel.chapterID];
|
||||
[t_dict writeToFile:fullPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)bookMarkRootPath
|
||||
{
|
||||
NSString *rootPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
|
||||
rootPath = [rootPath stringByAppendingPathComponent:@"bookMark"];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:rootPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:@{} error:nil];
|
||||
}
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
- (void)setComicReadingRecord:(NSInteger)comic_id chapter_id:(NSInteger)chapter_id offsetY:(CGFloat)offsetY
|
||||
{
|
||||
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
||||
path = [path stringByAppendingPathComponent:@"comic_record.plist"];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
[[NSFileManager defaultManager] createFileAtPath:path contents:[NSData data] attributes:@{}];
|
||||
}
|
||||
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
|
||||
if (!dict) {
|
||||
dict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
NSDictionary *params = @{
|
||||
[NSString stringWithFormat:@"%zd", chapter_id] : [NSString stringWithFormat:@"%f", offsetY]
|
||||
};
|
||||
[dict setValue:params forKey:[NSString stringWithFormat:@"%zd", comic_id]];
|
||||
[dict writeToFile:path atomically:NO];
|
||||
}
|
||||
|
||||
- (NSDictionary *)getComicReadingRecord:(NSInteger)comic_id
|
||||
{
|
||||
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
|
||||
path = [path stringByAppendingPathComponent:@"comic_record.plist"];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) return @{};
|
||||
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||
if ([dict valueForKey:[NSString stringWithFormat:@"%zd", comic_id]]) {
|
||||
return [dict valueForKey:[NSString stringWithFormat:@"%zd", comic_id]];
|
||||
} else {
|
||||
return @{};
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// TFShareBodyModel.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFShareBodyModel : NSObject
|
||||
|
||||
@property (nonatomic ,copy) NSString *title;
|
||||
@property (nonatomic ,copy) NSString *desc;
|
||||
@property (nonatomic ,copy) NSString *imageUrl;
|
||||
@property (nonatomic ,copy) NSString *shareUrl;
|
||||
@property (nonatomic ,strong) UIImage *shareImage;
|
||||
@property (nonatomic ,copy) NSString *error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// TFShareBodyModel.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFShareBodyModel.h"
|
||||
|
||||
@implementation TFShareBodyModel
|
||||
|
||||
- (void)setShareUrl:(NSString *)shareUrl
|
||||
{
|
||||
_shareUrl = shareUrl;
|
||||
|
||||
if (!kObjectIsEmpty(shareUrl)) {
|
||||
[[YYWebImageManager sharedManager] requestImageWithURL:[NSURL URLWithString:shareUrl] options:kNilOptions progress:nil transform:nil completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
|
||||
self.shareImage = image;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)shareImage
|
||||
{
|
||||
if (!_shareImage) {
|
||||
_shareImage = [UIImage imageNamed:[[[[NSBundle mainBundle] infoDictionary] valueForKeyPath:@"CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles"] lastObject]];
|
||||
}
|
||||
return _shareImage;
|
||||
}
|
||||
|
||||
- (NSString *)title
|
||||
{
|
||||
if (!_title) {
|
||||
_title = App_Name;
|
||||
}
|
||||
return _title;
|
||||
}
|
||||
|
||||
- (NSString *)desc
|
||||
{
|
||||
if (!_desc) {
|
||||
_desc = [NSString stringWithFormat:@"%@ - %@%@", App_Name, TFLocalizedString(@"分享得"), TFSystemInfoManager.masterUnit];
|
||||
}
|
||||
return _desc;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// TFShareManager.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TFShareView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFShareManager : NSObject
|
||||
|
||||
/// 分享作品
|
||||
/// @param production_id 作品ID
|
||||
/// @param chapter_id 章节ID
|
||||
/// @param type 分享类型
|
||||
+ (void)shareWithProduction_id:(NSString *)production_id chapter_id:(NSString * _Nullable)chapter_id type:(TFShareType)type;
|
||||
|
||||
/// 分享APP
|
||||
+ (void)shareApp;
|
||||
|
||||
/// 自定义分享
|
||||
/// @param title 分享标题
|
||||
/// @param desc 分享描述
|
||||
/// @param imageUrl 分享图片链接
|
||||
/// @param shareUrl 分享链接
|
||||
+ (void)shareWithTitle:(NSString *)title desc:(NSString *)desc imageUrl:(NSString *)imageUrl shareUrl:(NSString *)shareUrl;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// TFShareManager.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFShareManager.h"
|
||||
#import "TFShareView.h"
|
||||
#import "TFShareModel.h"
|
||||
#import "TFShareBodyModel.h"
|
||||
#if __has_include(<UMShare/UMShare.h>)
|
||||
#import <UMShare/UMShare.h>
|
||||
#endif
|
||||
#import <ShareSDK/ShareSDK.h>
|
||||
|
||||
@implementation TFShareManager
|
||||
|
||||
+ (void)shareWithProduction_id:(NSString *)production_id chapter_id:(NSString * _Nullable)chapter_id type:(TFShareType)type
|
||||
{
|
||||
TFShareBodyModel *t_model = [[TFShareBodyModel alloc] init];
|
||||
|
||||
[self getDetailsWithShareModel:t_model isShareApp:NO type:type production_id:production_id chapter_id:chapter_id];
|
||||
|
||||
[self showShareView:^(TFSharingPlatform platform) {
|
||||
[self share:platform shareModel:t_model];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)shareApp
|
||||
{
|
||||
TFShareBodyModel *t_model = [[TFShareBodyModel alloc] init];
|
||||
|
||||
[self getDetailsWithShareModel:t_model isShareApp:YES type:kNilOptions production_id:nil chapter_id:nil];
|
||||
|
||||
[self showShareView:^(TFSharingPlatform platform) {
|
||||
[self share:platform shareModel:t_model];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)shareWithTitle:(NSString *)title desc:(NSString *)desc imageUrl:(NSString *)imageUrl shareUrl:(NSString *)shareUrl
|
||||
{
|
||||
TFShareBodyModel *t_model = [[TFShareBodyModel alloc] init];
|
||||
t_model.title = title;
|
||||
t_model.desc = desc;
|
||||
t_model.imageUrl = imageUrl;
|
||||
t_model.shareUrl = shareUrl;
|
||||
[self showShareView:^(TFSharingPlatform platform) {
|
||||
[self share:platform shareModel:t_model];
|
||||
}];
|
||||
}
|
||||
|
||||
/// 从服务器获取分享的详细数据
|
||||
+ (void)getDetailsWithShareModel:(TFShareBodyModel *)shareModel isShareApp:(BOOL)isShareApp type:(TFShareType)type production_id:(NSString * _Nullable)production_id chapter_id:(NSString * _Nullable)chapter_id
|
||||
{
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
if (!isShareApp) {
|
||||
[params setObject:@(type) forKey:@"type"];
|
||||
[params setObject:production_id ?: @"" forKey:@"novel_id"];
|
||||
if (!kObjectIsEmpty(chapter_id)) {
|
||||
[params setObject:chapter_id forKey:@"chapter_id"];
|
||||
}
|
||||
}
|
||||
|
||||
[TFNetworkTools POST:isShareApp ? App_Share : App_Chapter_Share parameters:params model:TFShareModel.class success:^(BOOL isSuccess, TFShareModel *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
if (isSuccess) {
|
||||
NSString *shareUrl = @"";
|
||||
if (TFUserInfoManager.isLogin) {
|
||||
shareUrl = [NSString stringWithFormat:@"%@?uid=%zd&osType=1&product=1", t_model.link ?: [NSString stringWithFormat:@"%@%@", APIURL, Site_Share], TFUserInfoManager.shareInstance.uid];
|
||||
} else {
|
||||
shareUrl = [NSString stringWithFormat:@"%@?osType=1&product=1", t_model.link ?: [NSString stringWithFormat:@"%@%@", APIURL, Site_Share]];
|
||||
}
|
||||
|
||||
shareModel.title = @"";
|
||||
shareModel.title = t_model.title;
|
||||
shareModel.desc = t_model.desc;
|
||||
shareModel.imageUrl = t_model.imgUrl;
|
||||
shareModel.shareUrl = shareUrl;
|
||||
} else {
|
||||
shareModel.error = requestModel.msg ?: TFLocalizedString(@"分享错误");
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
shareModel.error = TFLocalizedString(@"分享错误");
|
||||
}];
|
||||
}
|
||||
|
||||
/// 弹出分享框
|
||||
+ (void)showShareView:(void(^)(TFSharingPlatform platform))clickBlock
|
||||
{
|
||||
TFShareView *shareView = [[TFShareView alloc] init];
|
||||
shareView.clickHandler = ^(TFSharingPlatform platform) {
|
||||
!clickBlock ?: clickBlock(platform);
|
||||
};
|
||||
[shareView show];
|
||||
}
|
||||
|
||||
/// 分享至指定平台
|
||||
+ (void)share:(TFSharingPlatform)platform shareModel:(TFShareBodyModel *)shareModel
|
||||
{
|
||||
if (kObjectIsEmpty(shareModel.shareUrl) && kObjectIsEmpty(shareModel.imageUrl)) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享地址出错,请稍后重试")];
|
||||
return;
|
||||
}
|
||||
// 获取分享平台的数据
|
||||
NSMutableDictionary *params = ({
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
[params SSDKSetupShareParamsByText:shareModel.desc images:shareModel.shareImage url:[NSURL URLWithString:shareModel.shareUrl] title:shareModel.title type:SSDKContentTypeAuto];
|
||||
params;
|
||||
});
|
||||
|
||||
// 分享至指定平台
|
||||
switch (platform) {
|
||||
case TFSharingPlatformQQ: {
|
||||
|
||||
[ShareSDK share:SSDKPlatformSubTypeQQFriend parameters:params onStateChanged:^(SSDKResponseState state, NSDictionary *userData, SSDKContentEntity *contentEntity, NSError *error) {
|
||||
if (error.code == 200104) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"请先安装客户端")];
|
||||
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case SSDKResponseStateSuccess: {
|
||||
[self shareCallback];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateFail: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateCancel:
|
||||
break;
|
||||
default: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
break;
|
||||
|
||||
case TFSharingPlatformQQSpace: {
|
||||
[ShareSDK share:SSDKPlatformSubTypeQZone parameters:params onStateChanged:^(SSDKResponseState state, NSDictionary *userData, SSDKContentEntity *contentEntity, NSError *error) {
|
||||
if (error.code == 200104) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"请先安装客户端")];
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case SSDKResponseStateSuccess: {
|
||||
[self shareCallback];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateFail: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateCancel:
|
||||
break;
|
||||
default: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
break;
|
||||
|
||||
case TFSharingPlatformWeChat: {
|
||||
|
||||
[ShareSDK share:SSDKPlatformSubTypeWechatSession parameters:params onStateChanged:^(SSDKResponseState state, NSDictionary *userData, SSDKContentEntity *contentEntity, NSError *error) {
|
||||
if (error.code == 200104) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"请先安装客户端")];
|
||||
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case SSDKResponseStateSuccess: {
|
||||
[self shareCallback];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateFail: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateCancel:
|
||||
break;
|
||||
default: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
break;
|
||||
|
||||
case TFSharingPlatformFriendCircle: {
|
||||
[ShareSDK share:SSDKPlatformSubTypeWechatTimeline parameters:params onStateChanged:^(SSDKResponseState state, NSDictionary *userData, SSDKContentEntity *contentEntity, NSError *error) {
|
||||
if (error.code == 200104) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"请先安装客户端")];
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case SSDKResponseStateSuccess: {
|
||||
[self shareCallback];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateFail: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
break;
|
||||
case SSDKResponseStateCancel:
|
||||
break;
|
||||
default: {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"分享失败")];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// 分享回调
|
||||
+ (void)shareCallback
|
||||
{
|
||||
[TFNetworkTools POST:Share_Back parameters:nil model:nil success:^(BOOL isSuccess, NSDictionary *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
if (isSuccess) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Share_Success object:nil];
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"分享成功")];
|
||||
}
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TFShareModel.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFShareModel : NSObject
|
||||
|
||||
@property (nonatomic ,copy) NSString *link;
|
||||
@property (nonatomic ,copy) NSString *title;
|
||||
@property (nonatomic ,copy) NSString *desc;
|
||||
@property (nonatomic ,copy) NSString *imgUrl;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// TFShareModel.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFShareModel.h"
|
||||
|
||||
@implementation TFShareModel
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// TFShareView.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/*** 分享平台 ***/
|
||||
typedef NS_ENUM(NSUInteger, TFSharingPlatform) {
|
||||
|
||||
TFSharingPlatformQQ = 0, /** 分享到 QQ */
|
||||
TFSharingPlatformWeChat = 1, /** 分享到 微信 */
|
||||
TFSharingPlatformQQSpace = 2, /** 分享到 QQ空间 */
|
||||
TFSharingPlatformFriendCircle = 3, /** 分享到 朋友圈 */
|
||||
};
|
||||
|
||||
/*** 分享类型 ***/
|
||||
typedef NS_ENUM(NSUInteger, TFShareType) {
|
||||
TFShareTypeBook = 1, /** 分享小说 */
|
||||
TFShareTypeComic = 2, /** 分享漫画 */
|
||||
TFShareTypeAudio = 3, /** 分享听书 */
|
||||
};
|
||||
|
||||
typedef void(^ClickHandler)(TFSharingPlatform platform);
|
||||
|
||||
@interface TFShareView : UIView
|
||||
|
||||
@property (nonatomic ,copy) ClickHandler clickHandler;
|
||||
/**
|
||||
* 显示\隐藏
|
||||
*/
|
||||
- (void)show;
|
||||
|
||||
- (void)hidden;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// TFShareView.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFShareView.h"
|
||||
#import "TFShareViewCell.h"
|
||||
#import "UIView+BorderLine.h"
|
||||
#import "UIView+LayoutCallback.h"
|
||||
|
||||
#define ZY_ItemCellHeight 100.f // 每个item的高度
|
||||
|
||||
@interface TFShareView () <UIGestureRecognizerDelegate, UICollectionViewDataSource, UICollectionViewDelegate>
|
||||
{
|
||||
UITapGestureRecognizer *tap;
|
||||
}
|
||||
|
||||
@property (nonatomic ,strong) UIView *backView;
|
||||
@property (nonatomic ,strong) UILabel *titleLabel;
|
||||
@property (nonatomic ,strong) UICollectionView *mainCollectionView;
|
||||
@property (nonatomic ,strong) UICollectionViewFlowLayout *mainCollectionViewFlowLayout;
|
||||
@property (nonatomic ,strong) UIButton *cancelButton;
|
||||
|
||||
@property (nonatomic ,strong) NSMutableArray *dataSources;
|
||||
@end
|
||||
|
||||
@implementation TFShareView
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
|
||||
[self initialize];
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialize
|
||||
{
|
||||
self.dataSources = [NSMutableArray array];
|
||||
|
||||
if (TF_WeChat_Share_Mode) {
|
||||
[self.dataSources addObject:@[@"微信好友", @"login_wechat", [NSNumber numberWithInteger:TFSharingPlatformWeChat]]];
|
||||
[self.dataSources addObject:@[@"朋友圈", @"share_wechat_timeline", [NSNumber numberWithInteger:TFSharingPlatformFriendCircle]]];
|
||||
}
|
||||
|
||||
if (TF_QQ_Share_Mode) {
|
||||
[self.dataSources addObject:@[@"QQ好友", @"login_qq", [NSNumber numberWithInteger:TFSharingPlatformQQ]]];
|
||||
[self.dataSources addObject:@[@"QQ空间", @"share_qzone", [NSNumber numberWithInteger:TFSharingPlatformQQSpace]]];
|
||||
}
|
||||
|
||||
self.userInteractionEnabled = YES;
|
||||
self.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
self.backgroundColor = kBlackTransparentAlphaColor(0);
|
||||
[kMainWindow addSubview:self];
|
||||
|
||||
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hidden)];
|
||||
tap.numberOfTapsRequired = 1;
|
||||
tap.delegate = self;
|
||||
[self addGestureRecognizer:tap];
|
||||
|
||||
self.mainCollectionView.scrollEnabled = YES;
|
||||
if (self.dataSources.count <= 4) {
|
||||
self.mainCollectionView.scrollEnabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[self addSubview:self.backView];
|
||||
|
||||
WS(weakSelf)
|
||||
self.titleLabel.frameBlock = ^(UIView * _Nonnull view) {
|
||||
// 设置文字分割线
|
||||
CALayer *splitLine = [CALayer layer];
|
||||
splitLine.backgroundColor = kGrayLineColor.CGColor;
|
||||
splitLine.anchorPoint = CGPointMake(0, 0);
|
||||
splitLine.bounds = CGRectMake(0, 0, 160, 1);
|
||||
splitLine.center = CGPointMake(weakSelf.titleLabel.centerX, weakSelf.titleLabel.centerY);
|
||||
[weakSelf.backView.layer addSublayer:splitLine];
|
||||
[weakSelf.backView bringSubviewToFront:view];
|
||||
};
|
||||
|
||||
[self.backView addSubview:self.titleLabel];
|
||||
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.mas_equalTo(kHalfMargin);
|
||||
make.centerX.mas_equalTo(self.backView.mas_centerX);
|
||||
make.width.mas_equalTo(80);
|
||||
make.height.mas_equalTo(40);
|
||||
}];
|
||||
|
||||
[self.backView addSubview:self.mainCollectionView];
|
||||
[self.mainCollectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.titleLabel.mas_bottom).with.offset(kHalfMargin);
|
||||
make.width.mas_equalTo(self.backView.mas_width);
|
||||
make.height.mas_equalTo(ZY_ItemCellHeight);
|
||||
}];
|
||||
|
||||
[self.backView addSubview:self.cancelButton];
|
||||
[self.backView bringSubviewToFront:self.cancelButton];
|
||||
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.mainCollectionView.mas_bottom);
|
||||
make.width.mas_equalTo(self.backView.mas_width);
|
||||
make.height.mas_equalTo(PUB_TABBAR_HEIGHT - kHalfMargin);
|
||||
}];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self.cancelButton addBorderLineWithBorderWidth:kCellLineHeight borderColor:kGrayLineColor cornerRadius:0 borderType:UIBorderSideTypeTop];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return self.dataSources.count;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TFShareViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TFShareViewCell" forIndexPath:indexPath];
|
||||
cell.sourceArray = [self.dataSources objectOrNilAtIndex:indexPath.row];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSArray *t_arr = [self.dataSources objectOrNilAtIndex:indexPath.row];
|
||||
TFSharingPlatform platform = (TFSharingPlatform)[[t_arr objectOrNilAtIndex:2] integerValue];
|
||||
!self.clickHandler ?: self.clickHandler(platform);
|
||||
[self hidden];
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.dataSources.count <= 4) {
|
||||
return CGSizeMake((SCREEN_WIDTH - 2 * kHalfMargin) / self.dataSources.count, ZY_ItemCellHeight);
|
||||
}
|
||||
return CGSizeMake(SCREEN_WIDTH / 4.7, ZY_ItemCellHeight);
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
|
||||
{
|
||||
if (self.dataSources.count <= 4) {
|
||||
return UIEdgeInsetsMake(0, kHalfMargin, 0, kHalfMargin);
|
||||
}
|
||||
return UIEdgeInsetsMake(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
- (void)show
|
||||
{
|
||||
if (self.dataSources.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
WS(weakSelf)
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
weakSelf.backgroundColor = kBlackTransparentAlphaColor(0.5);
|
||||
weakSelf.backView.frame = CGRectMake(0, SCREEN_HEIGHT - (kHalfMargin + 40 + kHalfMargin + ZY_ItemCellHeight + PUB_TABBAR_HEIGHT - kHalfMargin), SCREEN_WIDTH, kHalfMargin + 40 + kHalfMargin + ZY_ItemCellHeight + PUB_TABBAR_HEIGHT - kHalfMargin);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)hidden
|
||||
{
|
||||
WS(weakSelf)
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
weakSelf.backgroundColor = kBlackTransparentAlphaColor(0);
|
||||
weakSelf.backView.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, 40 + ZY_ItemCellHeight + PUB_TABBAR_HEIGHT);
|
||||
} completion:^(BOOL finished) {
|
||||
[weakSelf removeFromKeyWindow];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
if ([touch.view isEqual:self]) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeFromKeyWindow
|
||||
{
|
||||
if (self.superview) {
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)backView
|
||||
{
|
||||
if (!_backView) {
|
||||
_backView = [[UIView alloc] init];
|
||||
_backView.userInteractionEnabled = YES;
|
||||
_backView.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, kHalfMargin + 40 + kHalfMargin + ZY_ItemCellHeight + PUB_TABBAR_HEIGHT - kHalfMargin);
|
||||
_backView.backgroundColor = [UIColor whiteColor];
|
||||
[_backView addRoundingCornersWithRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight];
|
||||
_backView.clipsToBounds = YES;
|
||||
}
|
||||
return _backView;
|
||||
}
|
||||
|
||||
- (UILabel *)titleLabel
|
||||
{
|
||||
if (!_titleLabel) {
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
_titleLabel.backgroundColor = [UIColor whiteColor];
|
||||
_titleLabel.textColor = kGrayTextColor;
|
||||
_titleLabel.text = TFLocalizedString(@"分享至");
|
||||
_titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_titleLabel.font = kMainFont;
|
||||
}
|
||||
return _titleLabel;
|
||||
}
|
||||
|
||||
- (UIButton *)cancelButton
|
||||
{
|
||||
if (!_cancelButton) {
|
||||
_cancelButton = [[UIButton alloc] init];
|
||||
_cancelButton.backgroundColor = [UIColor whiteColor];
|
||||
[_cancelButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, PUB_TABBAR_OFFSET / 2, 0)];
|
||||
[_cancelButton setTitle:TFLocalizedString(@"取消") forState:UIControlStateNormal];
|
||||
[_cancelButton setTitleColor:kGrayTextColor forState:UIControlStateNormal];
|
||||
[_cancelButton.titleLabel setFont:kMainFont];
|
||||
[_cancelButton addTarget:self action:@selector(hidden) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _cancelButton;
|
||||
}
|
||||
|
||||
- (UICollectionView *)mainCollectionView
|
||||
{
|
||||
if (!_mainCollectionView) {
|
||||
_mainCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:self.mainCollectionViewFlowLayout];
|
||||
_mainCollectionView.userInteractionEnabled = YES;
|
||||
_mainCollectionView.backgroundColor = [UIColor clearColor];
|
||||
_mainCollectionView.showsVerticalScrollIndicator = NO;
|
||||
_mainCollectionView.showsHorizontalScrollIndicator = NO;
|
||||
_mainCollectionView.alwaysBounceHorizontal = YES;
|
||||
_mainCollectionView.delegate = self;
|
||||
_mainCollectionView.dataSource = self;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
_mainCollectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[_mainCollectionView registerClass:[TFShareViewCell class] forCellWithReuseIdentifier:@"TFShareViewCell"];
|
||||
}
|
||||
return _mainCollectionView;
|
||||
}
|
||||
|
||||
- (UICollectionViewFlowLayout *)mainCollectionViewFlowLayout
|
||||
{
|
||||
if (!_mainCollectionViewFlowLayout) {
|
||||
_mainCollectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
_mainCollectionViewFlowLayout.minimumInteritemSpacing = 0;
|
||||
_mainCollectionViewFlowLayout.minimumLineSpacing = 0;
|
||||
_mainCollectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
}
|
||||
return _mainCollectionViewFlowLayout;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TFShareViewCell.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFShareViewCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic ,strong) NSArray *sourceArray;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// TFShareViewCell.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFShareViewCell.h"
|
||||
|
||||
@interface TFShareViewCell ()
|
||||
|
||||
@property (nonatomic ,strong) UIImageView *iconView;
|
||||
@property (nonatomic ,strong) UILabel *titleView;
|
||||
@end
|
||||
|
||||
@implementation TFShareViewCell
|
||||
|
||||
- (UIImageView *)iconView
|
||||
{
|
||||
if (!_iconView) {
|
||||
_iconView = [[UIImageView alloc] init];
|
||||
_iconView.userInteractionEnabled = YES;
|
||||
}
|
||||
return _iconView;
|
||||
}
|
||||
|
||||
- (UILabel *)titleView
|
||||
{
|
||||
if (!_titleView) {
|
||||
_titleView = [[UILabel alloc] init];
|
||||
_titleView.backgroundColor = [UIColor whiteColor];
|
||||
_titleView.textColor = kGrayTextColor;
|
||||
_titleView.font = kFont12;
|
||||
_titleView.textAlignment = NSTextAlignmentCenter;
|
||||
_titleView.userInteractionEnabled = NO;
|
||||
}
|
||||
return _titleView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.userInteractionEnabled = YES;
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
[self createSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubViews
|
||||
{
|
||||
[self addSubview:self.iconView];
|
||||
[self.iconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.height.mas_equalTo(60);
|
||||
make.centerX.mas_equalTo(self.mas_centerX);
|
||||
make.top.mas_equalTo(5);
|
||||
}];
|
||||
|
||||
[self addSubview:self.titleView];
|
||||
[self.titleView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(self.iconView.mas_bottom);
|
||||
make.width.mas_equalTo(self.mas_width);
|
||||
make.height.mas_equalTo(30);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setSourceArray:(NSArray *)sourceArray
|
||||
{
|
||||
_sourceArray = sourceArray;
|
||||
|
||||
self.iconView.image = [UIImage imageNamed:[sourceArray objectOrNilAtIndex:1]];
|
||||
self.titleView.text = [sourceArray objectOrNilAtIndex:0];
|
||||
}
|
||||
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TFSystemInfoManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFSystemInfoManager : NSObject
|
||||
// 主货币单位
|
||||
@property (nonatomic ,copy ,class) NSString *masterUnit;
|
||||
// 子货币单位
|
||||
@property (nonatomic ,copy ,class) NSString *subUnit;
|
||||
// 首页性别频道:1:男,2:女
|
||||
@property (nonatomic ,assign ,class) NSInteger sexChannel;
|
||||
// 审核状态
|
||||
@property (nonatomic ,copy ,class) NSString *magicStatus;
|
||||
// 隐私协议同意状态
|
||||
@property (nonatomic ,assign ,class) BOOL isAgree;
|
||||
// 阅读任务记录作品id
|
||||
@property (nonatomic ,copy ,class) NSString *taskReadProductionId;
|
||||
// 默认手机区号
|
||||
@property (nonatomic ,copy ,class) NSString *zoneNumber;
|
||||
// 首次性别选择,可能为nil
|
||||
@property (nonatomic ,copy ,class ,nullable) NSString *firstGenderSelecte;
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
+ (instancetype)alloc UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// TFSystemInfoManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFSystemInfoManager.h"
|
||||
#import "TFAreaCodeModel.h"
|
||||
|
||||
@implementation TFSystemInfoManager
|
||||
|
||||
static NSString *_taskReadProductionId = nil;
|
||||
|
||||
+ (void)setTaskReadProductionId:(NSString *)taskReadProductionId
|
||||
{
|
||||
if (!taskReadProductionId) return;
|
||||
|
||||
_taskReadProductionId = taskReadProductionId;
|
||||
[self.systemDict setObject:taskReadProductionId forKey:KEY_PATH(self, taskReadProductionId)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)taskReadProductionId
|
||||
{
|
||||
return _taskReadProductionId ?: [self.systemDict objectForKey:@"taskReadProductionId"]?:@"";
|
||||
}
|
||||
|
||||
static NSString *_masterUnit = nil;
|
||||
|
||||
+ (void)setMasterUnit:(NSString *)masterUnit
|
||||
{
|
||||
if (!masterUnit) return;
|
||||
_masterUnit = masterUnit;
|
||||
[self.systemDict setObject:masterUnit forKey:KEY_PATH(self, masterUnit)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)masterUnit
|
||||
{
|
||||
return _masterUnit ?: [self.systemDict objectForKey:@"masterUnit"] ?: Main_Unit_Name;
|
||||
}
|
||||
|
||||
static NSString *_subUnit = nil;
|
||||
|
||||
+ (void)setSubUnit:(NSString *)subUnit
|
||||
{
|
||||
if (!subUnit) return;
|
||||
_subUnit = subUnit;
|
||||
[self.systemDict setObject:subUnit forKey:KEY_PATH(self, subUnit)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)subUnit
|
||||
{
|
||||
return _subUnit ?: [self.systemDict objectForKey:@"subUnit"] ?: Sub_Unit_Name;
|
||||
}
|
||||
|
||||
static NSInteger _sexChannel = 0;
|
||||
|
||||
+ (void)setSexChannel:(NSInteger)sexChannel
|
||||
{
|
||||
_sexChannel = sexChannel;
|
||||
[self.systemDict setObject:[NSString stringWithFormat:@"%zd", sexChannel] forKey:KEY_PATH(self, sexChannel)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSInteger)sexChannel
|
||||
{
|
||||
return _sexChannel ?: [[self.systemDict objectForKey:@"sexChannel"] integerValue] ?: 1;
|
||||
}
|
||||
|
||||
static NSString *_magicStatus = nil;
|
||||
|
||||
+ (void)setMagicStatus:(NSString *)magicStatus
|
||||
{
|
||||
_magicStatus = magicStatus;
|
||||
[self.systemDict setObject:magicStatus forKey:KEY_PATH(self, magicStatus)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)magicStatus
|
||||
{
|
||||
return _magicStatus ?: [self.systemDict objectForKey:@"magicStatus"];
|
||||
}
|
||||
|
||||
static BOOL _isAgree = NO;
|
||||
|
||||
+ (void)setIsAgree:(BOOL)isAgree
|
||||
{
|
||||
_isAgree = isAgree;
|
||||
|
||||
[self.systemDict setObject:[NSString stringWithFormat:@"%d", isAgree] forKey:KEY_PATH(self, isAgree)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (BOOL)isAgree
|
||||
{
|
||||
return _isAgree ?: [[self.systemDict objectForKey:@"isAgree"] boolValue];
|
||||
}
|
||||
|
||||
static NSString *_firstGenderSelecte = nil;
|
||||
|
||||
+ (void)setFirstGenderSelecte:(NSString *)firstGenderSelecte
|
||||
{
|
||||
_firstGenderSelecte = firstGenderSelecte;
|
||||
|
||||
[self.systemDict setValue:firstGenderSelecte forKey:KEY_PATH(self, firstGenderSelecte)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)firstGenderSelecte
|
||||
{
|
||||
return _firstGenderSelecte ?: [self.systemDict objectForKey:@"firstGenderSelecte"];
|
||||
}
|
||||
|
||||
static NSString *_zoneNumber = nil;
|
||||
|
||||
+ (void)setZoneNumber:(NSString *)zoneNumber
|
||||
{
|
||||
_zoneNumber = zoneNumber;
|
||||
[self.systemDict setValue:zoneNumber forKey:KEY_PATH(self, zoneNumber)];
|
||||
[self.systemDict writeToFile:self.systemPath atomically:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)zoneNumber
|
||||
{
|
||||
if (!_zoneNumber) {/**< 从本地记录获取*/
|
||||
_zoneNumber = [self.systemDict objectForKey:@"zoneNumber"];
|
||||
if (!_zoneNumber) {/**< 从当前语言环境和时区获取*/
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"areaCode.json" ofType:nil];
|
||||
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
|
||||
TFAreaCodeModel *t_model = [TFAreaCodeModel modelWithJSON:data];
|
||||
NSString *zoneCode = [[NSLocale currentLocale] objectForKey:NSLocaleIdentifier];
|
||||
for (TFAreaCodeListModel *list in t_model.list) {
|
||||
for (TFAreaCodeDetailModel *obj in list.list) {
|
||||
if ([zoneCode hasSuffix:obj.code]) {
|
||||
TFSystemInfoManager.zoneNumber = obj.areaCode;
|
||||
return _zoneNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
TFSystemInfoManager.zoneNumber = @"86";
|
||||
}
|
||||
}
|
||||
return _zoneNumber;
|
||||
}
|
||||
|
||||
static NSMutableDictionary *_systemDict = nil;
|
||||
|
||||
+ (NSMutableDictionary *)systemDict
|
||||
{
|
||||
if (!_systemDict) {
|
||||
_systemDict = [NSMutableDictionary dictionaryWithContentsOfFile:self.systemPath];
|
||||
|
||||
if (!_systemDict) {
|
||||
_systemDict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
}
|
||||
return _systemDict;
|
||||
}
|
||||
|
||||
+ (NSString *)systemPath
|
||||
{
|
||||
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:[System_Info_Path stringByAppendingPathExtension:@"plist"]];
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
[[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil];
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TFTencentPermissionsManager.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/4.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TFTencentPermissionsType) {
|
||||
TFTencentPermissionsTypeLogin,
|
||||
TFTencentPermissionsTypeBinding
|
||||
};
|
||||
|
||||
@protocol TFTencentPermissionsDelegate <NSObject>
|
||||
@optional
|
||||
// 登录成功
|
||||
- (void)tencentPermissionsSuccess:(TFUserInfoManager *)userData;
|
||||
|
||||
// 登录失败
|
||||
- (void)tencentPermissionsFail:(NSString *)error;
|
||||
@end
|
||||
|
||||
@interface TFTencentPermissionsManager : NSObject
|
||||
|
||||
@property (nonatomic ,weak) id <TFTencentPermissionsDelegate> delegate;
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (void)tencentPermissionsType:(TFTencentPermissionsType)type;
|
||||
|
||||
+ (BOOL)isInstallTencent;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// TFTencentPermissionsManager.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/4.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFTencentPermissionsManager.h"
|
||||
#import <ShareSDK/ShareSDK.h>
|
||||
|
||||
@interface TFTencentPermissionsManager ()
|
||||
|
||||
@property (nonatomic ,assign) TFTencentPermissionsType *permissionsType;
|
||||
@end
|
||||
|
||||
@implementation TFTencentPermissionsManager
|
||||
|
||||
implementation_singleton(TFTencentPermissionsManager)
|
||||
|
||||
- (void)tencentPermissionsType:(TFTencentPermissionsType)type
|
||||
{
|
||||
WS(weakSelf)
|
||||
[ShareSDK authorize:SSDKPlatformTypeQQ settings:nil onStateChanged:^(SSDKResponseState state, SSDKUser *user, NSError *error) {
|
||||
|
||||
if (state == SSDKResponseStateSuccess) {
|
||||
if (type == TFTencentPermissionsTypeLogin) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"QQ登录中")];
|
||||
}
|
||||
|
||||
if (type == TFTencentPermissionsTypeBinding) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"正在获取QQ信息")];
|
||||
}
|
||||
|
||||
NSDictionary *params = @{
|
||||
@"access_token" : user.credential.token
|
||||
};
|
||||
|
||||
if (type == TFTencentPermissionsTypeLogin) {
|
||||
|
||||
[TFNetworkTools POST:QQ_Login parameters:params model:TFUserInfoManager.class success:^(BOOL isSuccess, TFUserInfoManager *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
if (isSuccess) {
|
||||
[weakSelf tencentRequestSuccess:t_model];
|
||||
return;
|
||||
}
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[weakSelf tencentRequestFail:TFLocalizedString(@"QQ登录失败")];
|
||||
}];
|
||||
return ;
|
||||
}
|
||||
|
||||
if (type == TFTencentPermissionsTypeBinding) {
|
||||
[TFNetworkTools POST:QQ_Binding parameters:params model:TFUserInfoManager.class success:^(BOOL isSuccess, TFUserInfoManager *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
if (isSuccess) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"QQ绑定成功")];
|
||||
[weakSelf tencentRequestSuccess:t_model];
|
||||
return ;
|
||||
}
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[weakSelf tencentRequestFail:TFLocalizedString(@"QQ绑定失败")];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
} else if (state == SSDKResponseStateCancel) {
|
||||
[weakSelf tencentRequestFail:TFLocalizedString(@"授权取消")];
|
||||
|
||||
} else if (state == SSDKResponseStateFail) {
|
||||
if (type == TFTencentPermissionsTypeLogin) {
|
||||
[weakSelf tencentRequestFail:TFLocalizedString(@"QQ登录失败")];
|
||||
}
|
||||
|
||||
if (type == TFTencentPermissionsTypeBinding) {
|
||||
[weakSelf tencentRequestFail:TFLocalizedString(@"QQ绑定失败")];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (BOOL)isInstallTencent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tencentRequestSuccess:(TFUserInfoManager *)userData
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(tencentPermissionsSuccess:)]) {
|
||||
[self.delegate tencentPermissionsSuccess:userData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tencentRequestFail:(NSString *)error
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(tencentPermissionsFail:)]) {
|
||||
[self.delegate tencentPermissionsFail:error ?: @""];
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// TFUserInfoManager.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TFUserInfoManager : NSObject<NSCopying, NSMutableCopying>
|
||||
|
||||
@property (nonatomic ,copy) NSString *token;
|
||||
|
||||
@property (nonatomic ,assign) NSInteger uid;
|
||||
|
||||
@property (nonatomic ,copy) NSString *nickname;
|
||||
|
||||
@property (nonatomic ,copy) NSString *mobile;
|
||||
|
||||
@property (nonatomic ,copy) NSString *avatar;
|
||||
|
||||
/// 0:未知,1:女,2:男
|
||||
@property (nonatomic ,assign) NSInteger gender;
|
||||
|
||||
@property (nonatomic ,assign ,getter = isVip) BOOL vip;
|
||||
/// 主货币余额
|
||||
@property (nonatomic ,assign) NSInteger masterRemain;
|
||||
/// 子货币余额
|
||||
@property (nonatomic ,assign) NSInteger subRemain;
|
||||
/// 月票余额
|
||||
@property (nonatomic ,assign) NSInteger ticketRemain;
|
||||
/// 总余额
|
||||
@property (nonatomic ,assign) NSInteger totalRemain;
|
||||
/// 自动订阅开启状态
|
||||
@property (nonatomic ,assign) BOOL auto_sub;
|
||||
/// 登录状态
|
||||
@property (nonatomic ,assign ,class ,readonly ,getter = isLogin) BOOL login;
|
||||
|
||||
/// 更新Model数据,会自动同步本地数据。
|
||||
+ (instancetype)updateWithDict:(NSDictionary *)dict;
|
||||
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
+ (instancetype)logout;
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
+ (instancetype)alloc UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// TFUserInfoManager.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/3.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFUserInfoManager.h"
|
||||
#import "NSObject+Utils.h"
|
||||
#import <ShareSDK/ShareSDK.h>
|
||||
|
||||
@implementation TFUserInfoManager
|
||||
|
||||
static TFUserInfoManager *_userInfoInstance;
|
||||
|
||||
+ (instancetype)shareInstance
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_userInfoInstance = [super allocWithZone:zone];
|
||||
_userInfoInstance = [self dynamicInit];
|
||||
});
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:TFUserInfoManager.userInfoPath] && kObjectIsEmpty(_userInfoInstance.token)) {
|
||||
_userInfoInstance = [NSKeyedUnarchiver unarchiveObjectWithFile:TFUserInfoManager.userInfoPath];
|
||||
}
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
- (id)mutableCopyWithZone:(NSZone *)zone
|
||||
{
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
+ (BOOL)isLogin
|
||||
{
|
||||
return !kObjectIsEmpty([[self shareInstance] token]);
|
||||
}
|
||||
|
||||
static NSDictionary<NSString *, NSString *> *_property;
|
||||
|
||||
+ (instancetype)logout
|
||||
{
|
||||
[self dynamicInit];
|
||||
|
||||
// 删除本地用户信息
|
||||
[[NSFileManager defaultManager] removeItemAtPath:self.userInfoPath error:nil];
|
||||
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, id> *)modelCustomPropertyMapper
|
||||
{
|
||||
return @{
|
||||
@"token" : @"user_token",
|
||||
@"vip" : @"is_vip",
|
||||
@"masterRemain" : @"goldRemain",
|
||||
@"subRemain" : @"silverRemain",
|
||||
@"totalRemain" : @"remain"
|
||||
};
|
||||
}
|
||||
|
||||
/// 动态初始化Model所有值,避免Null、nil
|
||||
+ (instancetype)dynamicInit
|
||||
{
|
||||
if (_userInfoInstance == nil) _userInfoInstance = [self shareInstance];
|
||||
|
||||
// 动态获取属性列表,遍历属性并初始化.
|
||||
_property = _property ? : [self propertyDict];
|
||||
|
||||
[_property enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull value, BOOL * _Nonnull stop) {
|
||||
SEL sel = [TFUtilsHelper createSetterWithPropertyName:key];
|
||||
if ([_userInfoInstance respondsToSelector:sel]) {
|
||||
Class t_class = NSClassFromString(value);
|
||||
[self updateWithSel:sel obj:[t_class new]];
|
||||
}
|
||||
}];
|
||||
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
+ (instancetype)updateWithDict:(NSDictionary *)dict
|
||||
{
|
||||
if (_userInfoInstance == nil) _userInfoInstance = [self shareInstance];
|
||||
|
||||
// 动态获取属性列表,遍历并更新属性
|
||||
_property = _property ? : [self propertyDict];
|
||||
|
||||
[_property enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull value, BOOL * _Nonnull stop) {
|
||||
|
||||
SEL sel = [TFUtilsHelper createSetterWithPropertyName:key];
|
||||
|
||||
if ([_userInfoInstance respondsToSelector:sel]) {
|
||||
NSDictionary *t_dict = [self modelCustomPropertyMapper];
|
||||
id t_name = t_dict[key];
|
||||
if ([t_name isKindOfClass:NSArray.class]) {
|
||||
for (NSString *obj in t_name) {
|
||||
[self updateWithSel:sel obj:dict[obj]];
|
||||
}
|
||||
} else if ([t_name isKindOfClass:NSString.class]) {
|
||||
[self updateWithSel:sel obj:dict[t_name]];
|
||||
} else {
|
||||
[self updateWithSel:sel obj:dict[key]];
|
||||
}
|
||||
}
|
||||
}];
|
||||
// 删除本地用户信息
|
||||
[[NSFileManager defaultManager] removeItemAtPath:self.userInfoPath error:nil];
|
||||
// 保存用户信息到本地
|
||||
[NSKeyedArchiver archiveRootObject:_userInfoInstance toFile:self.userInfoPath];
|
||||
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
+ (void)updateWithSel:(SEL)sel obj:(id)obj
|
||||
{
|
||||
if (!obj) return;
|
||||
|
||||
if ([obj isKindOfClass:NSNumber.class]) {
|
||||
NSNumber *number = obj;
|
||||
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)_userInfoInstance, sel, (uint64_t)number.longLongValue);
|
||||
return;
|
||||
}
|
||||
|
||||
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)_userInfoInstance, sel, obj);
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self.class selector:@selector(logout) name:Notification_Logout object:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary
|
||||
{
|
||||
_userInfoInstance = [super modelWithDictionary:dictionary];
|
||||
|
||||
[NSKeyedArchiver archiveRootObject:_userInfoInstance toFile:self.userInfoPath];
|
||||
|
||||
return _userInfoInstance;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
return [self modelInitWithCoder:coder];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
[self modelEncodeWithCoder:coder];
|
||||
}
|
||||
|
||||
+ (NSString *)userInfoPath
|
||||
{
|
||||
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"userInfo"];
|
||||
}
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TFWeChatPermissionsManager.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TFWeChatPermissionsType) {
|
||||
TFWeChatPermissionsTypeLogin,
|
||||
TFWeChatPermissionsTypeBinding
|
||||
};
|
||||
|
||||
@protocol TFWeChatPermissionsDelegate <NSObject>
|
||||
@optional
|
||||
// 登录成功
|
||||
- (void)wechatResponseSuccess:(TFUserInfoManager *)userData;
|
||||
|
||||
// 登录失败
|
||||
- (void)wechatResponseFail:(NSString *)error;
|
||||
@end
|
||||
|
||||
@interface TFWeChatPermissionsManager : NSObject
|
||||
|
||||
@property (nonatomic, weak) id <TFWeChatPermissionsDelegate> delegate;
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (void)weChatPermissionsType:(TFWeChatPermissionsType)type;
|
||||
|
||||
+ (BOOL)isInstallWechat;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// TFWeChatPermissionsManager.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/12/5.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFWeChatPermissionsManager.h"
|
||||
#import <ShareSDK/ShareSDK.h>
|
||||
#import "AppDelegate.h"
|
||||
#import "TFURLProtocol.h"
|
||||
|
||||
@interface TFWeChatPermissionsManager ()
|
||||
|
||||
@property (nonatomic ,assign) TFWeChatPermissionsType permissionsType;
|
||||
@end
|
||||
|
||||
@implementation TFWeChatPermissionsManager
|
||||
|
||||
implementation_singleton(TFWeChatPermissionsManager)
|
||||
|
||||
- (void)weChatPermissionsType:(TFWeChatPermissionsType)type
|
||||
{
|
||||
// 注册网络请求监听。
|
||||
[TFURLProtocol startMonitor];
|
||||
|
||||
WS(weakSelf)
|
||||
[ShareSDK authorize:SSDKPlatformTypeWechat settings:nil onStateChanged:^(SSDKResponseState state, SSDKUser *user, NSError *error) {
|
||||
|
||||
if (state == SSDKResponseStateSuccess) {
|
||||
|
||||
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
NSString *code = delegate.wechatCode ?: @"";
|
||||
|
||||
if (code.length > 0) {
|
||||
if (type == TFWeChatPermissionsTypeLogin) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"微信登录中")];
|
||||
}
|
||||
|
||||
if (type == TFWeChatPermissionsTypeBinding) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusLoading promptTitle:TFLocalizedString(@"正在获取微信信息")];
|
||||
}
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"微信登录失败")];
|
||||
return;
|
||||
}
|
||||
|
||||
if (code && code.length > 0) {
|
||||
|
||||
NSDictionary *params = @{
|
||||
@"code" : code
|
||||
};
|
||||
|
||||
if (type == TFWeChatPermissionsTypeLogin) {
|
||||
|
||||
[TFNetworkTools POST:WeChat_Login parameters:params model:TFUserInfoManager.class success:^(BOOL isSuccess, TFUserInfoManager *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
[TFURLProtocol stopMonitor];
|
||||
if (isSuccess) {
|
||||
[weakSelf wechatRequestSuccess:t_model];
|
||||
return;
|
||||
}
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[TFURLProtocol stopMonitor];
|
||||
[weakSelf wechatRequestFail:TFLocalizedString(@"微信登录失败")];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == TFWeChatPermissionsTypeBinding) {
|
||||
[TFNetworkTools POST:WeChat_Binding parameters:params model:TFUserInfoManager.class success:^(BOOL isSuccess, TFUserInfoManager *_Nullable t_model, TFNetworkRequestModel *requestModel) {
|
||||
[TFURLProtocol stopMonitor];
|
||||
if (isSuccess) {
|
||||
[weakSelf wechatRequestSuccess:t_model];
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg];
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[TFURLProtocol stopMonitor];
|
||||
[weakSelf wechatRequestFail:TFLocalizedString(@"微信绑定失败")];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
} else if (state == SSDKResponseStateCancel) {
|
||||
TFLog(@"授权取消");
|
||||
} else if (state == SSDKResponseStateFail) {
|
||||
TFLog(@"=== 授权错误%@",error);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[ShareSDK getUserInfo:SSDKPlatformTypeWechat onStateChanged:^(SSDKResponseState state, SSDKUser *user, NSError *error) {
|
||||
if (state == SSDKResponseStateSuccess) {
|
||||
|
||||
TFLog(@"uid=%@",user.uid);
|
||||
TFLog(@"%@",user.credential);
|
||||
TFLog(@"token=%@",user.credential.token);
|
||||
TFLog(@"nickname=%@",user.nickname);
|
||||
} else {
|
||||
TFLog(@"%@",error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (BOOL)isInstallWechat
|
||||
{
|
||||
return YES;//[WXApi isWXAppInstalled];
|
||||
}
|
||||
|
||||
- (void)wechatRequestSuccess:(TFUserInfoManager *)userData
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(wechatResponseSuccess:)]) {
|
||||
[self.delegate wechatResponseSuccess:userData];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)wechatRequestFail:(NSString *)error
|
||||
{
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(wechatResponseFail:)]) {
|
||||
[self.delegate wechatResponseFail:error ? : @""];
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/11/24.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import "TFTabbarViewHelper.h"
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>
|
||||
|
||||
@property (nonatomic ,strong) TFCheckSettingModel *checkSettingModel;
|
||||
|
||||
@property (nonatomic ,strong) UNUserNotificationCenter *notificationCenter;
|
||||
|
||||
@property (nonatomic ,strong) TFTabbarViewHelper *tabBarControllerConfig;
|
||||
|
||||
@property (nonatomic ,strong) UIWindow *window;
|
||||
/** 微信登录Code */
|
||||
@property (nonatomic ,copy) NSString *wechatCode;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// TFReader
|
||||
//
|
||||
// Created by 谢腾飞 on 2020/11/24.
|
||||
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "AppDelegate+TFCheckSetting.h"
|
||||
#import "AppDelegate+TFShortcutTouch.h"
|
||||
#import "AppDelegate+TFEvaluation.h"
|
||||
#import "AppDelegate+TFAppSign.h"
|
||||
#import "AppDelegate+TFAdvertising.h"
|
||||
#import "AppDelegate+TFStartTimes.h"
|
||||
#import "AppDelegate+TFPopup.h"
|
||||
#import "AppDelegate+TFDeviceID.h"
|
||||
#import "AppDelegate+TFMobService.h"
|
||||
#if TF_Enable_Ai
|
||||
#import "AppDelegate+TFAISpeech.h"
|
||||
#endif
|
||||
|
||||
#import "CYLTabBarController.h"
|
||||
#import "TFIAPManager.h"
|
||||
#import "TFBookMarkModel.h"
|
||||
#import "TFReadRecordManager.h"
|
||||
#import "TFAgreementAlertView.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
@property (nonatomic ,copy) NSString *enterBackgroundTime; // 进入后台时间戳
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[TFViewHelper setStateBarLightStyle];
|
||||
|
||||
if (!TFSystemInfoManager.isAgree) {
|
||||
TFAgreementAlertView *alert = [[TFAgreementAlertView alloc] initInController];
|
||||
alert.confirmButtonClickBlock = ^{
|
||||
[self firstApplication:application didFinishLaunchingWithOptions:launchOptions];
|
||||
};
|
||||
[alert showAlertView];
|
||||
} else {
|
||||
[self firstApplication:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
// 注册设备信息
|
||||
[self initDeviceInfo];
|
||||
// 检查系统设置
|
||||
[self initCheckSetting];
|
||||
// MOB 推送
|
||||
[self initMobShare];
|
||||
// 3D Touch
|
||||
[self initShortcutTouch];
|
||||
// 应用内好评
|
||||
[self initAppStoreScore];
|
||||
|
||||
#if TF_Enable_Ai
|
||||
[self initAISpeech];
|
||||
#endif
|
||||
|
||||
#if TF_Super_Member_Mode || TF_Recharge_Mode
|
||||
// 启动IAP
|
||||
[[TFIAPManager sharedManager] startManager];
|
||||
#endif
|
||||
// 签到
|
||||
[self initUserSign];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)firstApplication:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
self.tabBarControllerConfig = [[TFTabbarViewHelper alloc] init];
|
||||
|
||||
CYLTabBarController *tabBarController = self.tabBarControllerConfig.tabBarController;
|
||||
[self.window setRootViewController:tabBarController];
|
||||
|
||||
// 开启Mob推送
|
||||
[self initMobPushWithApplication:application launchOptions:launchOptions];
|
||||
|
||||
// 开启广告
|
||||
[self initADManager];
|
||||
|
||||
// 启动页
|
||||
[self initLaunchADView];
|
||||
[self initInsterestView];
|
||||
[self updateInsterestData];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
[self initStartTimes];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application
|
||||
{
|
||||
self.enterBackgroundTime = [TFUtilsHelper currentDateString];
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application
|
||||
{
|
||||
#if TF_Super_Member_Mode || TF_Recharge_Mode
|
||||
// 检查未完成的内购
|
||||
[[TFIAPManager sharedManager] checkIAPFiles];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application
|
||||
{
|
||||
application.applicationIconBadgeNumber = 0;
|
||||
|
||||
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:TF_START_PAGE];
|
||||
|
||||
StartPage *start_page = [StartPage modelWithJSON:data];
|
||||
|
||||
NSInteger enterInterval = [TFUtilsHelper getCurrentMinutesIntervalWithTimeStamp:self.enterBackgroundTime];
|
||||
|
||||
if (start_page.skip_type == 5 && enterInterval >= TF_Launch_Interval) { // 穿山甲开屏广告
|
||||
[self initLaunchADView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application
|
||||
{
|
||||
/**结束IAP工具类*/
|
||||
[[TFIAPManager sharedManager] stopManager];
|
||||
|
||||
[application endReceivingRemoteControlEvents];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user