小说绘上架版本
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// WXYZ_ADPangoinVideo.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by LL on 2020/7/29.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFAdvertisementBasicView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ADPangolinVideo : TFAdvertisementBasicView
|
||||
|
||||
//- (void)show;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// WXYZ_ADPangoinVideo.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by LL on 2020/7/29.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ADPangolinVideo.h"
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface WXYZ_ADPangolinVideo ()
|
||||
// 观看视频前的音量大小
|
||||
@property (nonatomic ,assign) CGFloat initialVolume;
|
||||
@property (nonatomic ,weak) MPVolumeView *volumeView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ADPangolinVideo
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (void)hide
|
||||
{
|
||||
[TFPromptManager hideLoading];
|
||||
[self removeFromSuperview];
|
||||
// 穿山甲广告视频播放结束后会设置成AVAudioSessionCategorySoloAmbient模式导致有声或听书被停止播放
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
|
||||
[self restoreVolume];
|
||||
});
|
||||
}
|
||||
|
||||
// 恢复音量
|
||||
- (void)restoreVolume {
|
||||
UISlider *volumeSlider = nil;
|
||||
for (UIView *obj in self.volumeView.subviews) {
|
||||
if ([obj.class.description isEqualToString:@"MPVolumeSlider"]) {
|
||||
volumeSlider = (UISlider *)obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.volumeView.showsVolumeSlider = YES;
|
||||
[volumeSlider setValue:self.initialVolume animated:NO];
|
||||
[volumeSlider sendActionsForControlEvents:UIControlEventTouchUpInside];
|
||||
[self.volumeView sizeToFit];
|
||||
}
|
||||
|
||||
- (MPVolumeView *)volumeView {
|
||||
if (!_volumeView) {
|
||||
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(-100, -100, 40, 40)];
|
||||
_volumeView = volumeView;
|
||||
[self.window addSubview:volumeView];
|
||||
}
|
||||
return _volumeView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// WXYZ_ADPangolinView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/7/25.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFAdvertisementBasicView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ADPangolinView : TFAdvertisementBasicView
|
||||
// 广告关闭时的回调
|
||||
@property (nonatomic ,copy) void(^closeBlock)(void);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// WXYZ_ADPangolinView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/7/25.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ADPangolinView.h"
|
||||
#import "WXYZ_ADPangolinVideo.h"
|
||||
#import "TFReaderSettingHelper.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "UIView+LayoutCallback.h"
|
||||
|
||||
@interface WXYZ_ADPangolinView ()
|
||||
|
||||
@property (nonatomic, weak) UILabel *titleLabel;
|
||||
@property (nonatomic, weak) UILabel *subtitle;
|
||||
/// 高度比例
|
||||
@property (nonatomic, assign) CGFloat heightScale;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ADPangolinView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame advertisementType:(TFAdvertisementType)type advertisementPosition:(TFAdvertisementPosition)position
|
||||
{
|
||||
if (self = [super initWithFrame:frame advertisementType:type advertisementPosition:position]) {
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
if (self.position == TFAdvertisementPositionEnd && self.type == TFAdvertisementTypeNovel) {
|
||||
UILabel *subtitle = [[UILabel alloc] init];
|
||||
self.subtitle = subtitle;
|
||||
subtitle.backgroundColor = [UIColor clearColor];
|
||||
UIColor *textColor = [TFReaderSettingHelper sharedManager].getReaderTextColor;
|
||||
subtitle.textColor = textColor;
|
||||
subtitle.font = kFont14;
|
||||
AppDelegate *delegate = (AppDelegate *)kRCodeSync([UIApplication sharedApplication].delegate);
|
||||
subtitle.text = [NSString stringWithFormat:@"%@%@", delegate.checkSettingModel.ad_status_setting.video_ad_text ?: @"", @">"];
|
||||
[self addSubview:subtitle];
|
||||
subtitle.frameBlock = ^(UIView * _Nonnull view) {
|
||||
[view addBorderLineWithBorderWidth:1.0 borderColor:textColor cornerRadius:0.0 borderType:UIBorderSideTypeBottom];
|
||||
};
|
||||
subtitle.userInteractionEnabled = YES;
|
||||
[subtitle addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(watchVideo)]];
|
||||
|
||||
UILabel *titleLabel = [[UILabel alloc] init];
|
||||
self.titleLabel = titleLabel;
|
||||
titleLabel.backgroundColor = [UIColor clearColor];
|
||||
titleLabel.text = TFLocalizedString(@"点击/滑动可继续阅读");
|
||||
textColor = [textColor colorWithAlphaComponent:0.4];
|
||||
titleLabel.textColor = textColor;
|
||||
titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
titleLabel.font = kFont17;
|
||||
[self addSubview:titleLabel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAdvertModel:(TFAdvertModel *)advertModel
|
||||
{
|
||||
[super setAdvertModel:advertModel];
|
||||
|
||||
self.heightScale = 1.0 / 1.2;
|
||||
if (advertModel.ad_width > 0 && advertModel.ad_height > 0) {
|
||||
self.heightScale = (CGFloat)advertModel.ad_height / advertModel.ad_width;
|
||||
}
|
||||
if (self.position == TFAdvertisementPositionEnd) {
|
||||
self.heightScale = 1.0 / 1.2;
|
||||
}
|
||||
}
|
||||
/// 观看激励视频
|
||||
- (void)watchVideo
|
||||
{
|
||||
WXYZ_ADPangolinVideo *video = [[WXYZ_ADPangolinVideo alloc] initWithFrame:CGRectZero advertisementType:self.type advertisementPosition:self.position];
|
||||
|
||||
// [video show];
|
||||
}
|
||||
|
||||
@end
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// WXYZ_AnnouncementView.h
|
||||
// GKADRollingView
|
||||
//
|
||||
// Created by Gao on 2017/2/16.
|
||||
// Copyright © 2017年 gao. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "WXYZ_AnnouncementViewCollectionViewCell.h"
|
||||
|
||||
@interface WXYZ_AnnouncementView : UIView
|
||||
|
||||
typedef void(^WXYZ_AnnouncementViewTapBlock) (NSString *path, NSUInteger index);
|
||||
|
||||
@property (nonatomic, copy) WXYZ_AnnouncementViewTapBlock clickAdBlock;
|
||||
|
||||
@property (nonatomic, strong) NSArray<TFAnnouncementModel *> *modelArr;
|
||||
|
||||
@property (nonatomic, strong) UIColor *titleColor; // default is orange
|
||||
|
||||
@property (nonatomic, strong) UIFont *textFont; // default is 12
|
||||
|
||||
@property (nonatomic, strong) UIColor *textColor; // default is black
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval duration; // default is 3s
|
||||
|
||||
/// 是否居中,默认靠左
|
||||
@property (nonatomic, assign) BOOL isCenter;
|
||||
|
||||
@end
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// WXYZ_AnnouncementView.m
|
||||
// GKADRollingView
|
||||
//
|
||||
// Created by Gao on 2017/2/16.
|
||||
// Copyright © 2017年 gao. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_AnnouncementView.h"
|
||||
|
||||
@interface WXYZ_AnnouncementView () <UICollectionViewDelegate,UICollectionViewDataSource>
|
||||
{
|
||||
NSTimer *_timer;
|
||||
UICollectionView *_collectionView;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) NSInteger visibleItems;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_AnnouncementView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.minimumLineSpacing = 0;
|
||||
layout.minimumInteritemSpacing = 0;
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
||||
layout.itemSize = CGSizeMake(SCREEN_WIDTH - 2 * kMargin, kLabelHeight);
|
||||
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) collectionViewLayout:layout];
|
||||
_collectionView.showsVerticalScrollIndicator = NO;
|
||||
_collectionView.pagingEnabled = YES;
|
||||
_collectionView.scrollEnabled = NO;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.backgroundColor = [UIColor clearColor];
|
||||
[self addSubview:_collectionView];
|
||||
[_collectionView registerClass:[WXYZ_AnnouncementViewCollectionViewCell class] forCellWithReuseIdentifier:@"WXYZ_AnnouncementViewCollectionViewCell"];
|
||||
|
||||
[_collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.mas_equalTo(self);
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)setModelArr:(NSArray<TFAnnouncementModel *> *)modelArr
|
||||
{
|
||||
if (_modelArr != modelArr) {
|
||||
_modelArr = modelArr;
|
||||
[_collectionView reloadData];
|
||||
|
||||
if (_timer == nil) {
|
||||
_timer = [NSTimer timerWithTimeInterval:_duration?:5 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
|
||||
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
|
||||
[runloop addTimer:_timer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)starTimer
|
||||
{
|
||||
//开启定时器
|
||||
[_timer setFireDate:[NSDate distantPast]];
|
||||
}
|
||||
|
||||
- (void)stopTimer
|
||||
{
|
||||
//暂停定时器
|
||||
[_timer setFireDate:[NSDate distantFuture]];
|
||||
}
|
||||
|
||||
- (void)nextPage
|
||||
{
|
||||
@try {
|
||||
if (self.visibleItems == _modelArr.count) {
|
||||
self.visibleItems = 0;
|
||||
[self->_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.visibleItems inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
|
||||
}
|
||||
self.visibleItems++;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.visibleItems < _modelArr.count) {
|
||||
[self->_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.visibleItems inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
|
||||
}
|
||||
});
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
} @finally {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return _modelArr.count + 1;
|
||||
}
|
||||
|
||||
- (WXYZ_AnnouncementViewCollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
WXYZ_AnnouncementViewCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"WXYZ_AnnouncementViewCollectionViewCell" forIndexPath:indexPath];
|
||||
cell.textColor = self.textColor;
|
||||
cell.isCenter = self.isCenter;
|
||||
if (indexPath.row == 0) {
|
||||
cell.announcementModel = _modelArr.lastObject;
|
||||
} else {
|
||||
if (indexPath.row - 1 >= 0 && indexPath.row - 1 < _modelArr.count) {
|
||||
cell.announcementModel = _modelArr[indexPath.row - 1];
|
||||
} else {
|
||||
cell.announcementModel = _modelArr.firstObject;
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.row == 0) {
|
||||
self.clickAdBlock(_modelArr.lastObject.content, indexPath.row);
|
||||
} else {
|
||||
self.clickAdBlock(_modelArr[indexPath.row - 1].content, indexPath.row - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// WXYZ_AnnouncementViewCollectionViewCell.h
|
||||
// GKADRollingView
|
||||
//
|
||||
// Created by Gao on 2017/2/16.
|
||||
// Copyright © 2017年 gao. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TFBookRackModel.h"
|
||||
|
||||
@interface WXYZ_AnnouncementViewCollectionViewCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic, strong) TFAnnouncementModel *announcementModel;
|
||||
|
||||
@property (nonatomic, strong) UIColor *titleColor; // default is orange
|
||||
|
||||
@property (nonatomic, strong) UIFont *textFont; // default is 12
|
||||
|
||||
@property (nonatomic, strong) UIColor *textColor; // default is black
|
||||
|
||||
/// 文字是否居中
|
||||
@property (nonatomic, assign) BOOL isCenter;
|
||||
|
||||
@end
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// WXYZ_AnnouncementViewCollectionViewCell.m
|
||||
// GKADRollingView
|
||||
//
|
||||
// Created by Gao on 2017/2/16.
|
||||
// Copyright © 2017年 gao. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_AnnouncementViewCollectionViewCell.h"
|
||||
|
||||
@interface WXYZ_AnnouncementViewCollectionViewCell ()
|
||||
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
|
||||
@property (nonatomic, strong) UIImageView *headerImg;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_AnnouncementViewCollectionViewCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
_titleLabel.font = _textFont?:kFont12;
|
||||
_titleLabel.textColor = _textColor?:[UIColor blackColor];
|
||||
_titleLabel.numberOfLines = 1;
|
||||
[self addSubview:_titleLabel];
|
||||
|
||||
_headerImg = [[UIImageView alloc] init];
|
||||
[_headerImg setImage:[[UIImage imageNamed:@"rack_notice"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
|
||||
_headerImg.tintColor = kMainColor;
|
||||
[self addSubview:_headerImg];
|
||||
|
||||
[_headerImg mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(0);
|
||||
make.bottom.mas_equalTo(self.mas_bottom);
|
||||
make.width.mas_equalTo(self.mas_height);
|
||||
}];
|
||||
|
||||
[_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(_headerImg.mas_right).with.offset(kHalfMargin);
|
||||
make.top.mas_equalTo(0);
|
||||
make.bottom.mas_equalTo(self.mas_bottom);
|
||||
#if TF_Sign_Mode
|
||||
make.right.mas_equalTo(self.mas_right).with.offset(- kHalfMargin - 100);
|
||||
#else
|
||||
make.right.mas_equalTo(self.mas_right).with.offset(- kHalfMargin);
|
||||
#endif
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void)setAnnouncementModel:(TFAnnouncementModel *)announcementModel
|
||||
{
|
||||
_announcementModel = announcementModel;
|
||||
|
||||
_titleLabel.text = announcementModel.title;
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)textColor {
|
||||
_titleLabel.textColor = textColor;
|
||||
}
|
||||
|
||||
- (void)setIsCenter:(BOOL)isCenter {
|
||||
if (isCenter) {
|
||||
[_titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.center.equalTo(self);
|
||||
}];
|
||||
_headerImg.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// UIView+CKViewCategory.h
|
||||
// CKAudioProgress
|
||||
//
|
||||
// Created by guo on 2019/5/14.
|
||||
// Copyright © 2019 guo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CALayer (CKLayerCategory)
|
||||
|
||||
@property (nonatomic) CGFloat ckOriginY;
|
||||
@property (nonatomic) CGFloat ckOriginX;
|
||||
@property (nonatomic) CGFloat ckWidth;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (CKViewCategory)
|
||||
|
||||
@property (nonatomic) CGFloat ckOriginY;
|
||||
@property (nonatomic) CGFloat ckCenterX;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// UIView+CKViewCategory.m
|
||||
// CKAudioProgress
|
||||
//
|
||||
// Created by guo on 2019/5/14.
|
||||
// Copyright © 2019 guo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CALayer+CKViewCategory.h"
|
||||
|
||||
@implementation CALayer (CKLayerCategory)
|
||||
|
||||
- (CGFloat)ckOriginX {
|
||||
return self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (void)setCkOriginX:(CGFloat)ckOriginX {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = ckOriginX;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)ckOriginY {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setCkOriginY:(CGFloat)ckOriginY {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = ckOriginY;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)ckWidth {
|
||||
return self.frame.size.width;
|
||||
}
|
||||
|
||||
- (void)setCkWidth:(CGFloat)ckWidth {
|
||||
CGRect frame = self.frame;
|
||||
frame.size.width = ckWidth;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIView (CKViewCategory)
|
||||
|
||||
- (CGFloat)ckOriginY {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setCkOriginY:(CGFloat)ckOriginY {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = ckOriginY;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)ckCenterX {
|
||||
return self.center.x;
|
||||
}
|
||||
- (void)setCkCenterX:(CGFloat)ckCenterX {
|
||||
CGPoint center = self.center;
|
||||
center.x = ckCenterX;
|
||||
self.center = center;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// CKAudioProgressView.h
|
||||
// CKAudioProgress
|
||||
//
|
||||
// Created by guo on 2019/5/14.
|
||||
// Copyright © 2019 guo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol CKAudioProgressViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
- (void)audioProgressTouchBegin;
|
||||
- (void)audioProgresstouchEndhCurrentTime:(NSInteger)currentTime totalTime:(NSInteger)totalTime;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, CKAudioProgressType) {
|
||||
CKAudioProgressTypeTimeline ///表示拖拽view是个时间进度
|
||||
};
|
||||
@interface CKAudioProgressView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *cachedBgColor; ///缓冲进度条背景颜色
|
||||
@property (nonatomic, strong) UIColor *progressBgColor; ///进度条默认填充背景色
|
||||
|
||||
/**
|
||||
@desc 已经播放的进度条渐变色, 存储 CGColorRef 对象的数组
|
||||
@note 该属性和 playedBgColor 二选一
|
||||
*/
|
||||
@property (nonatomic, copy ) NSArray *colors;
|
||||
/**
|
||||
@desc 已经播放的进度条背景颜色
|
||||
@note 该属性和colors 二选一
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *playedBgColor;
|
||||
|
||||
@property (nonatomic, assign) CGFloat cornerRadius;
|
||||
|
||||
@property (nonatomic, assign) CGRect slideViewBounds; ///拖拽view(圆点或时间进度)的大小
|
||||
|
||||
@property (nonatomic, assign) NSInteger totalTimeLength;
|
||||
|
||||
@property (nonatomic, weak ) id<CKAudioProgressViewDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame type:(CKAudioProgressType)progressType;
|
||||
/**
|
||||
@note 修改当前进度,可以根据后面参数计算出当前播放时长并显示在时间lable上
|
||||
@param progress 进度百分比
|
||||
@param audioLength 总时间
|
||||
*/
|
||||
- (void)updateProgress:(CGFloat)progress audioLength:(NSInteger)audioLength;
|
||||
|
||||
- (void)updateCacheProgress:(CGFloat)progress;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// CKAudioProgressView.m
|
||||
// CKAudioProgress
|
||||
//
|
||||
// Created by guo on 2019/5/14.
|
||||
// Copyright © 2019 guo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CKAudioProgressView.h"
|
||||
#import "CALayer+CKViewCategory.h"
|
||||
|
||||
@interface CKAudioProgressView()
|
||||
|
||||
@property (nonatomic, assign) BOOL isSliding;
|
||||
@property (nonatomic, assign) NSInteger audioLength;
|
||||
|
||||
@property (nonatomic, strong) UIView *slideView;
|
||||
@property (nonatomic, strong) UILabel *lb_time;
|
||||
// 进度指示器
|
||||
@property (nonatomic, strong) UILabel *lb_indicator;
|
||||
@property (nonatomic, strong) CALayer *bgLayer;
|
||||
@property (nonatomic, strong) CALayer *cachedLayer;
|
||||
@property (nonatomic, strong) UIView *progressLineView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CKAudioProgressView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame type:(CKAudioProgressType)progressType {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initViews {
|
||||
|
||||
[self.layer addSublayer:self.bgLayer];
|
||||
[self.layer addSublayer:self.cachedLayer];
|
||||
[self addSubview:self.progressLineView];
|
||||
|
||||
[self addSubview:self.lb_indicator];
|
||||
|
||||
[self addSubview:self.lb_time];
|
||||
|
||||
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)]];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
CGFloat originY = (self.bounds.size.height-2)/2;
|
||||
self.bgLayer.ckOriginY = originY;
|
||||
self.bgLayer.ckWidth = self.frame.size.width;
|
||||
self.cachedLayer.ckOriginY = originY;
|
||||
self.progressLineView.ckOriginY = originY;
|
||||
|
||||
_lb_time.ckOriginY = (self.bounds.size.height-_lb_time.bounds.size.height)/2;
|
||||
}
|
||||
|
||||
#pragma mark - Action
|
||||
static CGFloat percent = 0.0;
|
||||
- (void)timeLabelPanGesture:(UIPanGestureRecognizer *)gesture
|
||||
{
|
||||
UIGestureRecognizerState state = gesture.state;
|
||||
if (UIGestureRecognizerStateBegan == state) {
|
||||
_isSliding = YES;
|
||||
percent = 0.0;
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(audioProgressTouchBegin)]) {
|
||||
[_delegate audioProgressTouchBegin];
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
self.lb_indicator.alpha = 1;
|
||||
}];
|
||||
|
||||
} else if (UIGestureRecognizerStateChanged == state) {
|
||||
CGPoint translation = [gesture translationInView:self];
|
||||
CGPoint slideViewCenter = CGPointMake(gesture.view.center.x+ translation.x, gesture.view.center.y);
|
||||
slideViewCenter.x = MAX(gesture.view.bounds.size.width/2, slideViewCenter.x);
|
||||
slideViewCenter.x = MIN(self.bounds.size.width-gesture.view.bounds.size.width/2, slideViewCenter.x);
|
||||
gesture.view.center = slideViewCenter;
|
||||
[gesture setTranslation:CGPointZero inView:self];
|
||||
|
||||
_progressLineView.width = gesture.view.frame.origin.x;
|
||||
|
||||
CGFloat totalWith = self.bounds.size.width - _lb_time.bounds.size.width;
|
||||
NSInteger audioProgress = gesture.view.frame.origin.x / totalWith * self.audioLength;
|
||||
|
||||
percent = gesture.view.frame.origin.x / totalWith;
|
||||
|
||||
[self setProgress:audioProgress total:self.audioLength];
|
||||
|
||||
self.lb_indicator.ckOriginY = _lb_time.origin.y - 45;
|
||||
self.lb_indicator.ckCenterX = _lb_time.center.x;
|
||||
|
||||
} else if (UIGestureRecognizerStateEnded == state || UIGestureRecognizerStateCancelled == state) {
|
||||
_isSliding = NO;
|
||||
|
||||
NSInteger audioProgress = gesture.view.frame.origin.x / (self.bounds.size.width - _lb_time.bounds.size.width) * self.audioLength;
|
||||
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
self.lb_indicator.alpha = 0;
|
||||
}];
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(audioProgresstouchEndhCurrentTime:totalTime:)]) {
|
||||
[_delegate audioProgresstouchEndhCurrentTime:audioProgress totalTime:self.audioLength];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tapGesture:(UITapGestureRecognizer *)gesture
|
||||
{
|
||||
CGPoint translation = [gesture locationInView:self];
|
||||
|
||||
NSInteger audioProgress = translation.x / self.bounds.size.width * self.audioLength;
|
||||
|
||||
[self setProgress:audioProgress total:self.audioLength];
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(audioProgresstouchEndhCurrentTime:totalTime:)]) {
|
||||
[_delegate audioProgresstouchEndhCurrentTime:audioProgress totalTime:self.audioLength];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgress:(NSInteger)progress total:(NSInteger)total
|
||||
{
|
||||
NSString *title = @"";
|
||||
if (total >= 3600) {
|
||||
title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper getHourTimeTransformationWithTotalTimeLenght:progress], [TFUtilsHelper getHourTimeTransformationWithTotalTimeLenght:total]];
|
||||
} else {
|
||||
title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper getMinuteTimeTransformationWithTotalTimeLenght:progress], [TFUtilsHelper getMinuteTimeTransformationWithTotalTimeLenght:total]];
|
||||
}
|
||||
_lb_time.text = title;
|
||||
|
||||
if (self.lb_indicator.alpha > 0) {
|
||||
self.lb_indicator.text = title;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateProgress:(CGFloat)progress audioLength:(NSInteger)audioLength
|
||||
{
|
||||
if (isnan(percent) || isinf(percent)) {
|
||||
percent = 0;
|
||||
}
|
||||
if (audioLength <= 0) {
|
||||
_lb_time.text = @"00:00/00:00";
|
||||
self.lb_indicator.text = @"00:00/00:00";
|
||||
_lb_time.xtfei_x = 0;
|
||||
_progressLineView.xtfei_width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress > 1) {
|
||||
percent = 1;
|
||||
} else if (progress < 0) {
|
||||
percent = 0;
|
||||
} else {
|
||||
percent = progress;
|
||||
}
|
||||
|
||||
if (!_isSliding) {
|
||||
|
||||
self.audioLength = audioLength;
|
||||
|
||||
[self setProgress:(NSInteger)(percent*audioLength) total:audioLength];
|
||||
|
||||
CGFloat totalWith = self.bounds.size.width-_lb_time.bounds.size.width;
|
||||
CGFloat playedWidth = totalWith*percent;
|
||||
_progressLineView.width = playedWidth;
|
||||
_lb_time.ckCenterX = playedWidth+_lb_time.bounds.size.width/2;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateCacheProgress:(CGFloat)progress
|
||||
{
|
||||
if (!isnan(progress)) {
|
||||
self.cachedLayer.ckWidth = self.frame.size.width * progress;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Getter & Setter
|
||||
|
||||
- (void)setCachedBgColor:(UIColor *)cachedBgColor {
|
||||
if (cachedBgColor) {
|
||||
_cachedBgColor = cachedBgColor;
|
||||
self.cachedLayer.backgroundColor = cachedBgColor.CGColor;
|
||||
}
|
||||
}
|
||||
- (void)setProgressBgColor:(UIColor *)progressBgColor {
|
||||
if (progressBgColor) {
|
||||
_progressBgColor = progressBgColor;
|
||||
self.bgLayer.backgroundColor = progressBgColor.CGColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCornerRadius:(CGFloat)cornerRadius {
|
||||
_cornerRadius = cornerRadius;
|
||||
self.cornerRadius = cornerRadius;
|
||||
self.bgLayer.cornerRadius = cornerRadius;
|
||||
self.cachedLayer.cornerRadius = cornerRadius;
|
||||
self.progressLineView.layer.cornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
- (void)setColors:(NSArray *)colors {
|
||||
if (!_playedBgColor && colors) {
|
||||
_colors = colors;
|
||||
}
|
||||
}
|
||||
- (void)setPlayedBgColor:(UIColor *)playedBgColor {
|
||||
if (!_colors && playedBgColor) {
|
||||
_playedBgColor = playedBgColor;
|
||||
self.progressLineView.backgroundColor = playedBgColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSlideViewBounds:(CGRect)slideViewBounds {
|
||||
if (slideViewBounds.size.width > 0) {
|
||||
_slideViewBounds = slideViewBounds;
|
||||
|
||||
self.lb_time.bounds = slideViewBounds;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTotalTimeLength:(NSInteger)totalTimeLength
|
||||
{
|
||||
_totalTimeLength = totalTimeLength;
|
||||
if (totalTimeLength >= 0) {
|
||||
|
||||
NSString *title = @"";
|
||||
if (totalTimeLength >= 3600) {
|
||||
title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper getHourTimeTransformationWithTotalTimeLenght:totalTimeLength], [TFUtilsHelper getHourTimeTransformationWithTotalTimeLenght:totalTimeLength]];
|
||||
} else {
|
||||
title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper getMinuteTimeTransformationWithTotalTimeLenght:totalTimeLength], [TFUtilsHelper getMinuteTimeTransformationWithTotalTimeLenght:totalTimeLength]];
|
||||
}
|
||||
_lb_time.xtfei_width = [TFViewHelper getDynamicWidthWithLabelFont:[UIFont systemFontOfSize:10] labelHeight:20 labelText:title] + kHalfMargin;
|
||||
_lb_indicator.xtfei_width = [TFViewHelper getDynamicWidthWithLabelFont:kFont12 labelHeight:30 labelText:title] + kHalfMargin;
|
||||
}
|
||||
}
|
||||
|
||||
- (CALayer *)bgLayer {
|
||||
if (!_bgLayer) {
|
||||
_bgLayer = [CALayer layer];
|
||||
_bgLayer.backgroundColor = _progressBgColor ? _progressBgColor.CGColor : [UIColor colorWithRed:242/255.0 green:242/255.0 blue:242/255.0 alpha:1].CGColor;
|
||||
_bgLayer.frame = CGRectMake(0, (self.bounds.size.height-2)/2, self.bounds.size.width, 2);
|
||||
}
|
||||
return _bgLayer;
|
||||
}
|
||||
|
||||
- (CALayer *)cachedLayer {
|
||||
if (!_cachedLayer) {
|
||||
_cachedLayer = [CALayer layer];
|
||||
_cachedLayer.backgroundColor = [UIColor colorWithRed:230/255.0 green:230/255.0 blue:230/255.0 alpha:1.0].CGColor;
|
||||
_cachedLayer.frame = CGRectMake(0, (self.bounds.size.height-2)/2, 0, 2);
|
||||
}
|
||||
return _cachedLayer;
|
||||
}
|
||||
|
||||
- (UIView *)progressLineView {
|
||||
if (!_progressLineView) {
|
||||
_progressLineView = [[UIView alloc] init];
|
||||
_progressLineView.frame = CGRectMake(0, (self.bounds.size.height - 2 ) / 2, 0, 2);
|
||||
}
|
||||
return _progressLineView;
|
||||
}
|
||||
|
||||
- (UIView *)slideView {
|
||||
if (!_slideView) {
|
||||
_slideView = [UIView new];
|
||||
_slideView.frame = CGRectMake(-(24-24/2-12/2), 0, 24, 24);
|
||||
CAShapeLayer *dotLayer = [CAShapeLayer layer];
|
||||
dotLayer.fillColor = [UIColor whiteColor].CGColor;
|
||||
dotLayer.frame = CGRectMake((24-12)/2, (24-12)/2, 12, 12);
|
||||
dotLayer.cornerRadius = 6;
|
||||
dotLayer.shadowColor = [UIColor colorWithRed:255/255.0 green:120/255.0 blue:2/255.0 alpha:1.0].CGColor;
|
||||
dotLayer.shadowOffset = CGSizeMake(0,0);
|
||||
dotLayer.shadowOpacity = 1;
|
||||
dotLayer.shadowRadius = 10;
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:dotLayer.bounds];
|
||||
dotLayer.path = path.CGPath;
|
||||
[_slideView.layer addSublayer:dotLayer];
|
||||
}
|
||||
return _slideView;
|
||||
}
|
||||
|
||||
- (UILabel *)lb_indicator
|
||||
{
|
||||
if (!_lb_indicator) {
|
||||
_lb_indicator = [[UILabel alloc] init];
|
||||
_lb_indicator.font = kFont12;
|
||||
_lb_indicator.textAlignment = NSTextAlignmentCenter;
|
||||
_lb_indicator.textColor = kWhiteColor;
|
||||
_lb_indicator.frame = CGRectMake(0, 0, 92, 30);
|
||||
_lb_indicator.alpha = 0;
|
||||
_lb_indicator.layer.cornerRadius = 15;
|
||||
_lb_indicator.layer.backgroundColor = kColorRGBA(0, 0, 0, 0.7).CGColor;
|
||||
_lb_indicator.userInteractionEnabled = YES;
|
||||
}
|
||||
return _lb_indicator;
|
||||
}
|
||||
|
||||
- (UILabel *)lb_time {
|
||||
if (!_lb_time) {
|
||||
_lb_time = [UILabel new];
|
||||
_lb_time.font = [UIFont systemFontOfSize:10];
|
||||
_lb_time.textAlignment = NSTextAlignmentCenter;
|
||||
_lb_time.textColor = kGrayTextDeepColor;
|
||||
_lb_time.frame = CGRectMake(0, 0, 72, 20);
|
||||
_lb_time.layer.cornerRadius = 10;
|
||||
_lb_time.layer.shadowColor = [UIColor colorWithRed:87/255.0 green:92/255.0 blue:111/255.0 alpha:0.5].CGColor;
|
||||
_lb_time.layer.shadowOffset = CGSizeMake(0,0);
|
||||
_lb_time.layer.shadowOpacity = 3;
|
||||
_lb_time.layer.shadowRadius = 1;
|
||||
_lb_time.layer.backgroundColor = kWhiteColor.CGColor;
|
||||
_lb_time.userInteractionEnabled = YES;
|
||||
[_lb_time addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(timeLabelPanGesture:)]];
|
||||
}
|
||||
return _lb_time;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// WXYZ_BadgeView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/8/7.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXPORT CGFloat const RKNotificationHubDefaultDiameter;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_BadgeView : NSObject
|
||||
|
||||
//%%% setup
|
||||
- (id)initWithView:(UIView *)view;
|
||||
- (id)initWithBarButtonItem:(UIBarButtonItem *)barButtonItem;
|
||||
|
||||
//%%% adjustment methods
|
||||
- (void)setView:(UIView *)view andCount:(int)startCount;
|
||||
- (void)setCircleAtFrame:(CGRect)frame;
|
||||
- (void)setCircleColor:(UIColor*)circleColor labelColor:(UIColor*)labelColor;
|
||||
- (void)setCircleBorderColor:(UIColor *)color borderWidth:(CGFloat)width;
|
||||
- (void)moveCircleByX:(CGFloat)x Y:(CGFloat)y;
|
||||
- (void)scaleCircleSizeBy:(CGFloat)scale;
|
||||
@property (nonatomic, strong) UIFont *countLabelFont;
|
||||
|
||||
//%%% changing the count
|
||||
- (void)increment;
|
||||
- (void)incrementBy:(int)amount;
|
||||
- (void)decrement;
|
||||
- (void)decrementBy:(int)amount;
|
||||
@property (nonatomic, assign) int count;
|
||||
@property (nonatomic, assign) int maxCount;
|
||||
|
||||
//%%% hiding / showing the count
|
||||
- (void)hideCount;
|
||||
- (void)showCount;
|
||||
|
||||
//%%% animations
|
||||
- (void)pop;
|
||||
- (void)blink;
|
||||
- (void)bump;
|
||||
|
||||
@property (nonatomic)UIView *hubView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,409 @@
|
||||
//
|
||||
// WXYZ_BadgeView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/8/7.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_BadgeView.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
//%%% default diameter
|
||||
CGFloat const RKNotificationHubDefaultDiameter = 30;
|
||||
static CGFloat const kCountMagnitudeAdaptationRatio = 0.3;
|
||||
//%%% pop values
|
||||
static CGFloat const kPopStartRatio = .85;
|
||||
static CGFloat const kPopOutRatio = 1.05;
|
||||
static CGFloat const kPopInRatio = .95;
|
||||
|
||||
//%%% blink values
|
||||
static CGFloat const kBlinkDuration = 0.1;
|
||||
static CGFloat const kBlinkAlpha = 0.1;
|
||||
|
||||
//%%% bump values
|
||||
static CGFloat const kFirstBumpDistance = 8.0;
|
||||
static CGFloat const kBumpTimeSeconds = 0.13;
|
||||
static CGFloat const SECOND_BUMP_DIST = 4.0;
|
||||
static CGFloat const kBumpTimeSeconds2 = 0.1;
|
||||
|
||||
@interface RKView : UIView
|
||||
@property (nonatomic) BOOL isUserChangingBackgroundColor;
|
||||
@end
|
||||
|
||||
@implementation RKView
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
if (self.isUserChangingBackgroundColor) {
|
||||
super.backgroundColor = backgroundColor;
|
||||
self.isUserChangingBackgroundColor = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation WXYZ_BadgeView{
|
||||
int curOrderMagnitude;
|
||||
UILabel *countLabel;
|
||||
RKView *redCircle;
|
||||
CGPoint initialCenter;
|
||||
CGRect baseFrame;
|
||||
CGRect initialFrame;
|
||||
BOOL isIndeterminateMode;
|
||||
}
|
||||
|
||||
@synthesize hubView;
|
||||
|
||||
#pragma mark - SETUP
|
||||
|
||||
- (id)initWithView:(UIView *)view
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return nil;
|
||||
|
||||
_maxCount = 100000;
|
||||
[self setView:view andCount:0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithBarButtonItem:(UIBarButtonItem *)barButtonItem
|
||||
{
|
||||
self = [self initWithView:[barButtonItem valueForKey:@"view"]];
|
||||
[self scaleCircleSizeBy:0.7];
|
||||
[self moveCircleByX:-5.0 Y:0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//%%% give this a view and an initial count (0 hides the notification circle)
|
||||
// and it will make a hub for you
|
||||
- (void)setView:(UIView *)view andCount:(int)startCount
|
||||
{
|
||||
curOrderMagnitude = 0;
|
||||
|
||||
CGRect frame = view.frame;
|
||||
|
||||
isIndeterminateMode = NO;
|
||||
|
||||
redCircle = [[RKView alloc]init];
|
||||
redCircle.userInteractionEnabled = NO;
|
||||
redCircle.isUserChangingBackgroundColor = YES;
|
||||
redCircle.backgroundColor = [UIColor redColor];
|
||||
|
||||
countLabel = [[UILabel alloc]initWithFrame:redCircle.frame];
|
||||
countLabel.userInteractionEnabled = NO;
|
||||
self.count = startCount;
|
||||
[countLabel setTextAlignment:NSTextAlignmentCenter];
|
||||
countLabel.textColor = [UIColor whiteColor];
|
||||
countLabel.backgroundColor = [UIColor clearColor];
|
||||
|
||||
[self setCircleAtFrame:CGRectMake(frame.size.width- (RKNotificationHubDefaultDiameter*2/3), -RKNotificationHubDefaultDiameter/3, RKNotificationHubDefaultDiameter, RKNotificationHubDefaultDiameter)];
|
||||
|
||||
[view addSubview:redCircle];
|
||||
[view addSubview:countLabel];
|
||||
[view bringSubviewToFront:redCircle];
|
||||
[view bringSubviewToFront:countLabel];
|
||||
hubView = view;
|
||||
[self checkZero];
|
||||
}
|
||||
|
||||
//%%% set the frame of the notification circle relative to the button
|
||||
- (void)setCircleAtFrame:(CGRect)frame
|
||||
{
|
||||
[redCircle setFrame:frame];
|
||||
initialCenter = CGPointMake(frame.origin.x+frame.size.width/2, frame.origin.y+frame.size.height/2);
|
||||
baseFrame = frame;
|
||||
initialFrame = frame;
|
||||
countLabel.frame = redCircle.frame;
|
||||
redCircle.layer.cornerRadius = frame.size.height/2;
|
||||
[countLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:frame.size.width/2]];
|
||||
[self expandToFitLargerDigits];
|
||||
}
|
||||
|
||||
//%%% moves the circle by x amount on the x axis and y amount on the y axis
|
||||
- (void)moveCircleByX:(CGFloat)x Y:(CGFloat)y
|
||||
{
|
||||
CGRect frame = redCircle.frame;
|
||||
frame.origin.x += x;
|
||||
frame.origin.y += y;
|
||||
[self setCircleAtFrame:frame];
|
||||
}
|
||||
|
||||
//%%% changes the size of the circle. setting a scale of 1 has no effect
|
||||
- (void)scaleCircleSizeBy:(CGFloat)scale
|
||||
{
|
||||
CGRect fr = initialFrame;
|
||||
CGFloat width = fr.size.width * scale;
|
||||
CGFloat height = fr.size.height * scale;
|
||||
CGFloat wdiff = (fr.size.width - width) / 2;
|
||||
CGFloat hdiff = (fr.size.height - height) / 2;
|
||||
|
||||
CGRect frame = CGRectMake(fr.origin.x + wdiff, fr.origin.y + hdiff, width, height);
|
||||
[self setCircleAtFrame:frame];
|
||||
}
|
||||
|
||||
//%%% change the color of the notification circle
|
||||
- (void)setCircleColor:(UIColor*)circleColor labelColor:(UIColor*)labelColor
|
||||
{
|
||||
redCircle.isUserChangingBackgroundColor = YES;
|
||||
redCircle.backgroundColor = circleColor;
|
||||
[countLabel setTextColor:labelColor];
|
||||
}
|
||||
|
||||
- (void)setCircleBorderColor:(UIColor *)color borderWidth:(CGFloat)width {
|
||||
redCircle.layer.borderColor = color.CGColor;
|
||||
redCircle.layer.borderWidth = width;
|
||||
}
|
||||
|
||||
- (void)hideCount
|
||||
{
|
||||
countLabel.hidden = YES;
|
||||
isIndeterminateMode = YES;
|
||||
}
|
||||
|
||||
- (void)showCount
|
||||
{
|
||||
isIndeterminateMode = NO;
|
||||
[self checkZero];
|
||||
}
|
||||
|
||||
#pragma mark - ATTRIBUTES
|
||||
|
||||
//%%% increases count by 1
|
||||
- (void)increment
|
||||
{
|
||||
[self incrementBy:1];
|
||||
}
|
||||
|
||||
//%%% increases count by amount
|
||||
- (void)incrementBy:(int)amount
|
||||
{
|
||||
self.count += amount;
|
||||
}
|
||||
|
||||
//%%% decreases count
|
||||
- (void)decrement
|
||||
{
|
||||
[self decrementBy:1];
|
||||
}
|
||||
|
||||
//%%% decreases count by amount
|
||||
- (void)decrementBy:(int)amount
|
||||
{
|
||||
if (amount >= self.count) {
|
||||
self.count = 0;
|
||||
return;
|
||||
}
|
||||
self.count -= amount;
|
||||
}
|
||||
|
||||
//%%% set the count yourself
|
||||
- (void)setCount:(int)newCount
|
||||
{
|
||||
_count = newCount;
|
||||
|
||||
NSString *labelText = [NSString stringWithFormat:@"%@", @(self.count)];
|
||||
|
||||
if (_count > self.maxCount){
|
||||
labelText = [NSString stringWithFormat:@"%@+", @(self.maxCount)];
|
||||
}
|
||||
|
||||
countLabel.text = labelText;
|
||||
[self checkZero];
|
||||
[self expandToFitLargerDigits];
|
||||
}
|
||||
|
||||
//%% set the font of the label
|
||||
- (void)setCountLabelFont:(UIFont *)font
|
||||
{
|
||||
[countLabel setFont:font];
|
||||
}
|
||||
|
||||
- (UIFont *)countLabelFont
|
||||
{
|
||||
return countLabel.font;
|
||||
}
|
||||
|
||||
#pragma mark - ANIMATION
|
||||
|
||||
//%%% animation that resembles facebook's pop
|
||||
- (void)pop
|
||||
{
|
||||
const float height = baseFrame.size.height;
|
||||
const float width = baseFrame.size.width;
|
||||
const float pop_start_h = height * kPopStartRatio;
|
||||
const float pop_start_w = width * kPopStartRatio;
|
||||
const float time_start = 0.05;
|
||||
const float pop_out_h = height * kPopOutRatio;
|
||||
const float pop_out_w = width * kPopOutRatio;
|
||||
const float time_out = .2;
|
||||
const float pop_in_h = height * kPopInRatio;
|
||||
const float pop_in_w = width * kPopInRatio;
|
||||
const float time_in = .05;
|
||||
const float pop_end_h = height;
|
||||
const float pop_end_w = width;
|
||||
const float time_end = 0.05;
|
||||
|
||||
CABasicAnimation *startSize = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
|
||||
startSize.duration = time_start;
|
||||
startSize.beginTime = 0;
|
||||
startSize.fromValue = @(pop_end_h / 2);
|
||||
startSize.toValue = @(pop_start_h / 2);
|
||||
startSize.removedOnCompletion = FALSE;
|
||||
|
||||
CABasicAnimation *outSize = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
|
||||
outSize.duration = time_out;
|
||||
outSize.beginTime = time_start;
|
||||
outSize.fromValue = startSize.toValue;
|
||||
outSize.toValue = @(pop_out_h / 2);
|
||||
outSize.removedOnCompletion = FALSE;
|
||||
|
||||
CABasicAnimation *inSize = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
|
||||
inSize.duration = time_in;
|
||||
inSize.beginTime = time_start+time_out;
|
||||
inSize.fromValue = outSize.toValue;
|
||||
inSize.toValue = @(pop_in_h / 2);
|
||||
inSize.removedOnCompletion = FALSE;
|
||||
|
||||
CABasicAnimation *endSize = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
|
||||
endSize.duration = time_end;
|
||||
endSize.beginTime = time_in+time_out+time_start;
|
||||
endSize.fromValue = inSize.toValue;
|
||||
endSize.toValue = @(pop_end_h / 2);
|
||||
endSize.removedOnCompletion = FALSE;
|
||||
|
||||
CAAnimationGroup *group = [CAAnimationGroup animation];
|
||||
[group setDuration: time_start+time_out+time_in+time_end];
|
||||
[group setAnimations:@[startSize, outSize, inSize, endSize]];
|
||||
|
||||
[redCircle.layer addAnimation:group forKey:nil];
|
||||
|
||||
[UIView animateWithDuration:time_start animations:^{
|
||||
CGRect frame = redCircle.frame;
|
||||
CGPoint center = redCircle.center;
|
||||
frame.size.height = pop_start_h;
|
||||
frame.size.width = pop_start_w;
|
||||
redCircle.frame = frame;
|
||||
redCircle.center = center;
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:time_out animations:^{
|
||||
CGRect frame = redCircle.frame;
|
||||
CGPoint center = redCircle.center;
|
||||
frame.size.height = pop_out_h;
|
||||
frame.size.width = pop_out_w;
|
||||
redCircle.frame = frame;
|
||||
redCircle.center = center;
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:time_in animations:^{
|
||||
CGRect frame = redCircle.frame;
|
||||
CGPoint center = redCircle.center;
|
||||
frame.size.height = pop_in_h;
|
||||
frame.size.width = pop_in_w;
|
||||
redCircle.frame = frame;
|
||||
redCircle.center = center;
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:time_end animations:^{
|
||||
CGRect frame = redCircle.frame;
|
||||
CGPoint center = redCircle.center;
|
||||
frame.size.height = pop_end_h;
|
||||
frame.size.width = pop_end_w;
|
||||
redCircle.frame = frame;
|
||||
redCircle.center = center;
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
//%%% animation that flashes on an off
|
||||
- (void)blink
|
||||
{
|
||||
[self setAlpha:kBlinkAlpha];
|
||||
|
||||
[UIView animateWithDuration:kBlinkDuration animations:^{
|
||||
[self setAlpha:1];
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:kBlinkDuration animations:^{
|
||||
[self setAlpha:kBlinkAlpha];
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:kBlinkDuration animations:^{
|
||||
[self setAlpha:1];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
//%%% animation that jumps similar to OSX dock icons
|
||||
- (void)bump
|
||||
{
|
||||
if (!CGPointEqualToPoint(initialCenter,redCircle.center)) {
|
||||
//%%% canel previous animation
|
||||
}
|
||||
|
||||
[self bumpCenterY:0];
|
||||
[UIView animateWithDuration:kBumpTimeSeconds animations:^{
|
||||
[self bumpCenterY:kFirstBumpDistance];
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:kBumpTimeSeconds animations:^{
|
||||
[self bumpCenterY:0];
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:kBumpTimeSeconds2 animations:^{
|
||||
[self bumpCenterY:SECOND_BUMP_DIST];
|
||||
}completion:^(BOOL complete){
|
||||
[UIView animateWithDuration:kBumpTimeSeconds2 animations:^{
|
||||
[self bumpCenterY:0];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - HELPERS
|
||||
|
||||
//%%% changes the Y origin of the notification circle
|
||||
- (void)bumpCenterY:(float)yVal
|
||||
{
|
||||
CGPoint center = redCircle.center;
|
||||
center.y = initialCenter.y-yVal;
|
||||
redCircle.center = center;
|
||||
countLabel.center = center;
|
||||
}
|
||||
|
||||
- (void)setAlpha:(float)alpha
|
||||
{
|
||||
redCircle.alpha = alpha;
|
||||
countLabel.alpha = alpha;
|
||||
}
|
||||
|
||||
//%%% hides the notification if the value is 0
|
||||
- (void)checkZero
|
||||
{
|
||||
if (self.count <= 0) {
|
||||
redCircle.hidden = YES;
|
||||
countLabel.hidden = YES;
|
||||
} else {
|
||||
redCircle.hidden = NO;
|
||||
if (!isIndeterminateMode) {
|
||||
countLabel.hidden = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)expandToFitLargerDigits {
|
||||
int orderOfMagnitude = log10((double)self.count);
|
||||
orderOfMagnitude = (orderOfMagnitude >= 2) ? orderOfMagnitude : 1;
|
||||
CGRect frame = initialFrame;
|
||||
frame.size.width = initialFrame.size.width * (1 + kCountMagnitudeAdaptationRatio * (orderOfMagnitude - 1));
|
||||
frame.origin.x = initialFrame.origin.x - (frame.size.width - initialFrame.size.width) / 2;
|
||||
|
||||
[redCircle setFrame:frame];
|
||||
initialCenter = CGPointMake(frame.origin.x+frame.size.width/2, frame.origin.y+frame.size.height/2);
|
||||
baseFrame = frame;
|
||||
countLabel.frame = redCircle.frame;
|
||||
curOrderMagnitude = orderOfMagnitude;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// DPBatteryView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/6/10.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface WXYZ_BatteryView : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *batteryTintColor;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// DPBatteryView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/6/10.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_BatteryView.h"
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
WXBatteryStateColorNormal,
|
||||
WXBatteryStateColorCharging,
|
||||
WXBatteryStateColorWarning,
|
||||
} WXBatteryStateColor;
|
||||
|
||||
@interface WXYZ_BatteryView ()
|
||||
{
|
||||
UIView *batteryView;
|
||||
UILabel *batteryLabel;
|
||||
CAShapeLayer *batteryLayer;
|
||||
CAShapeLayer *layer2;
|
||||
|
||||
CGFloat w;
|
||||
CGFloat lineW;
|
||||
NSTimer *time;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_BatteryView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self initialize];
|
||||
[self createSubViews];
|
||||
[self batteryLevelChanged];
|
||||
[self updateTime];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialize
|
||||
{
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
device.batteryMonitoringEnabled = YES;
|
||||
|
||||
WS(weakSelf)
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIDeviceBatteryLevelDidChangeNotification
|
||||
object:nil queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
[weakSelf batteryLevelChanged];
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserverForName:UIDeviceBatteryStateDidChangeNotification
|
||||
object:nil queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *notification) {
|
||||
[weakSelf batteryStateChanged];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)createSubViews
|
||||
{
|
||||
//电池的宽度
|
||||
w = 25;
|
||||
//电池的x的坐标
|
||||
CGFloat x = 0;
|
||||
//电池的y的坐标
|
||||
CGFloat y = self.height / 2 - 5;
|
||||
//电池的线宽
|
||||
lineW = 1;
|
||||
//电池的高度
|
||||
CGFloat h = 10;
|
||||
|
||||
//画电池
|
||||
UIBezierPath *path1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, w, h) cornerRadius:2];
|
||||
batteryLayer = [CAShapeLayer layer];
|
||||
batteryLayer.lineWidth = lineW;
|
||||
batteryLayer.strokeColor = [UIColor grayColor].CGColor;
|
||||
batteryLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
batteryLayer.path = [path1 CGPath];
|
||||
[self.layer addSublayer:batteryLayer];
|
||||
|
||||
UIBezierPath *path2 = [UIBezierPath bezierPath];
|
||||
[path2 moveToPoint:CGPointMake(x+w+1, y+h/3)];
|
||||
[path2 addLineToPoint:CGPointMake(x+w+1, y+h*2/3)];
|
||||
layer2 = [CAShapeLayer layer];
|
||||
layer2.lineWidth = 2;
|
||||
layer2.strokeColor = [UIColor grayColor].CGColor;
|
||||
layer2.fillColor = [UIColor clearColor].CGColor;
|
||||
layer2.path = [path2 CGPath];
|
||||
[self.layer addSublayer:layer2];
|
||||
|
||||
//绘制进度
|
||||
batteryView = [[UIView alloc] initWithFrame:CGRectMake(x + 1,y + lineW, 0, h - lineW * 2)];
|
||||
batteryView.layer.cornerRadius = 1;
|
||||
[self addSubview:batteryView];
|
||||
|
||||
batteryLabel = [[UILabel alloc] initWithFrame:CGRectMake(x + w + 5, y, 80, h)];
|
||||
batteryLabel.textColor = [UIColor grayColor];
|
||||
batteryLabel.textAlignment = NSTextAlignmentLeft;
|
||||
batteryLabel.font = kFont10;
|
||||
[self addSubview:batteryLabel];
|
||||
|
||||
time = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateTime) userInfo:nil repeats:YES];
|
||||
}
|
||||
|
||||
- (void)updateTime
|
||||
{
|
||||
//获取当前时间
|
||||
NSDate *now = [NSDate date];
|
||||
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSCalendarUnit unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
||||
NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:now];
|
||||
|
||||
NSInteger hour = [dateComponent hour];
|
||||
NSInteger minute = [dateComponent minute];
|
||||
|
||||
if (hour < 10 && minute < 10) {
|
||||
batteryLabel.text = [NSString stringWithFormat:@"0%d:0%d",(int)hour, (int)minute];
|
||||
} else if (hour < 10) {
|
||||
batteryLabel.text = [NSString stringWithFormat:@"0%d:%d",(int)hour, (int)minute];
|
||||
} else if (minute < 10) {
|
||||
batteryLabel.text = [NSString stringWithFormat:@"%d:0%d",(int)hour, (int)minute];
|
||||
} else {
|
||||
batteryLabel.text = [NSString stringWithFormat:@"%d:%d",(int)hour, (int)minute];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 电池剩余比例
|
||||
- (void)batteryLevelChanged
|
||||
{
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
device.batteryMonitoringEnabled = YES;
|
||||
|
||||
float batteryLevel = [device batteryLevel] * 100;
|
||||
|
||||
if (batteryLevel < 0) {
|
||||
batteryLevel = 0;
|
||||
} else if (batteryLevel > 100){
|
||||
batteryLevel = 100;
|
||||
}
|
||||
|
||||
if (batteryLevel <= 10) {
|
||||
[self changeBatteryState:WXBatteryStateColorWarning];
|
||||
} else {
|
||||
[self changeBatteryState:WXBatteryStateColorNormal];
|
||||
}
|
||||
|
||||
CGRect frame = batteryView.frame;
|
||||
frame.size.width = (batteryLevel * (w - lineW * 2)) / 100;
|
||||
batteryView.frame = frame;
|
||||
}
|
||||
|
||||
- (void)batteryStateChanged
|
||||
{
|
||||
switch ([[UIDevice currentDevice] batteryState]) {
|
||||
case 0: // 未开启监视电池状态
|
||||
[self changeBatteryState:WXBatteryStateColorNormal];
|
||||
break;
|
||||
case 1: // 电池未充电状态
|
||||
[self changeBatteryState:WXBatteryStateColorNormal];
|
||||
break;
|
||||
case 2: // 电池充电状态
|
||||
[self changeBatteryState:WXBatteryStateColorCharging];
|
||||
break;
|
||||
case 3: // 电池充电完成
|
||||
[self changeBatteryState:WXBatteryStateColorCharging];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)changeBatteryState:(WXBatteryStateColor)batteryState
|
||||
{
|
||||
switch (batteryState) {
|
||||
case WXBatteryStateColorNormal:
|
||||
batteryView.backgroundColor = kColorRGBA(131, 131, 131, 1);
|
||||
break;
|
||||
case WXBatteryStateColorCharging:
|
||||
batteryView.backgroundColor = kColorRGBA(75, 216, 102, 1);
|
||||
break;
|
||||
case WXBatteryStateColorWarning:
|
||||
batteryView.backgroundColor = kColorRGBA(252, 62, 46, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBatteryTintColor:(UIColor *)batteryTintColor
|
||||
{
|
||||
batteryLayer.strokeColor = batteryTintColor.CGColor;
|
||||
layer2.strokeColor = batteryTintColor.CGColor;
|
||||
batteryLabel.textColor = batteryTintColor;
|
||||
|
||||
if ([UIDevice currentDevice].batteryState == UIDeviceBatteryStateCharging || [UIDevice currentDevice].batteryState == UIDeviceBatteryStateFull) {
|
||||
[self changeBatteryState:WXBatteryStateColorCharging];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[time invalidate];
|
||||
time = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// DZMAnimatedTransitioning.h
|
||||
// DZMAnimatedTransitioning
|
||||
//
|
||||
// Created by 邓泽淼 on 2017/12/20.
|
||||
// Copyright © 2017年 邓泽淼. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NSObject+DZM.h"
|
||||
|
||||
@interface DZMAnimatedTransitioning : NSObject<UIViewControllerAnimatedTransitioning>
|
||||
|
||||
/// 构造方法
|
||||
- (instancetype _Nullable)initWithOperation:(UINavigationControllerOperation)operation;
|
||||
|
||||
/// 构造方法
|
||||
- (instancetype _Nullable)initWithOperation:(UINavigationControllerOperation)operation duration:(float)duration;
|
||||
|
||||
@end
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// DZMAnimatedTransitioning.m
|
||||
// DZMAnimatedTransitioning
|
||||
//
|
||||
// Created by 邓泽淼 on 2017/12/20.
|
||||
// Copyright © 2017年 邓泽淼. All rights reserved.
|
||||
//
|
||||
|
||||
#define DZM_TAG_COVER 818
|
||||
|
||||
#import "DZMAnimatedTransitioning.h"
|
||||
|
||||
static UIImage *coverImage;
|
||||
|
||||
@interface DZMAnimatedTransitioning()
|
||||
{
|
||||
UIView *contentView;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) UINavigationControllerOperation operation;
|
||||
|
||||
@property (nonatomic, assign, readonly) float duration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DZMAnimatedTransitioning
|
||||
|
||||
- (instancetype)initWithOperation:(UINavigationControllerOperation)operation {
|
||||
|
||||
return [self initWithOperation:operation duration:0.6];
|
||||
}
|
||||
|
||||
- (instancetype)initWithOperation:(UINavigationControllerOperation)operation duration:(float)duration{
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
|
||||
_duration = duration;
|
||||
|
||||
_operation = operation;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
|
||||
|
||||
return self.duration;
|
||||
}
|
||||
|
||||
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
|
||||
|
||||
if (self.operation == UINavigationControllerOperationPush) {
|
||||
|
||||
[self push:transitionContext];
|
||||
|
||||
}else if (self.operation == UINavigationControllerOperationPop) {
|
||||
|
||||
[self pop:transitionContext];
|
||||
|
||||
}else{}
|
||||
}
|
||||
|
||||
- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext {
|
||||
|
||||
UIViewController *to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
|
||||
|
||||
UIViewController *from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
|
||||
|
||||
UIView *containerView = [transitionContext containerView];
|
||||
|
||||
UIView *fromView = (from.ATTarget != nil ? from.ATTarget : from.view);
|
||||
|
||||
CGRect rect = [fromView convertRect:fromView.bounds toView:containerView];
|
||||
|
||||
contentView = [self GetImageView:[self screenCapture:to.view]];
|
||||
|
||||
contentView.frame = rect;
|
||||
|
||||
if (fromView.layer.cornerRadius > 0.0) {
|
||||
|
||||
contentView.layer.cornerRadius = fromView.layer.cornerRadius;
|
||||
|
||||
contentView.layer.masksToBounds = YES;
|
||||
}
|
||||
|
||||
[containerView addSubview:contentView];
|
||||
|
||||
|
||||
UIImageView *coverView = [self GetImageView: [self screenCapture:fromView]];
|
||||
|
||||
coverView.tag = DZM_TAG_COVER;
|
||||
|
||||
coverView.frame = CGRectMake(rect.origin.x - (rect.size.width / 2), rect.origin.y, rect.size.width, rect.size.height);
|
||||
|
||||
[containerView addSubview:coverView];
|
||||
|
||||
coverView.layer.anchorPoint = CGPointMake(0, 0.5);
|
||||
|
||||
coverView.opaque = YES;
|
||||
|
||||
coverImage = coverView.image;
|
||||
|
||||
CATransform3D transform = CATransform3DMakeRotation(- M_PI_2 , 0.0, 1.0, 0.0);
|
||||
|
||||
transform.m34 = 1.0f / 500.0f;
|
||||
|
||||
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
|
||||
|
||||
coverView.frame = to.view.bounds;
|
||||
|
||||
self->contentView.frame = to.view.bounds;
|
||||
|
||||
coverView.layer.transform = transform;
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
coverView.image = nil;
|
||||
|
||||
coverView.hidden = YES;
|
||||
|
||||
[self->contentView removeFromSuperview];
|
||||
|
||||
[containerView addSubview:to.view];
|
||||
|
||||
[transitionContext completeTransition:YES];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext {
|
||||
|
||||
UIViewController *to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
|
||||
|
||||
UIViewController *from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
|
||||
|
||||
UIView *containerView = [transitionContext containerView];
|
||||
|
||||
[containerView addSubview:to.view];
|
||||
|
||||
contentView = [self GetImageView:[self screenCapture:from.view]];
|
||||
|
||||
contentView.frame = from.view.bounds;
|
||||
|
||||
[containerView addSubview:contentView];
|
||||
|
||||
|
||||
UIImageView *coverView = [containerView viewWithTag:DZM_TAG_COVER];
|
||||
|
||||
coverView.image = coverImage;
|
||||
|
||||
coverView.hidden = NO;
|
||||
|
||||
[containerView addSubview:coverView];
|
||||
|
||||
|
||||
CATransform3D transform = CATransform3DMakeRotation(0.0, 0.0, 1.0, 0.0);
|
||||
|
||||
transform.m34 = 1.0f / 500.0f;
|
||||
|
||||
CGFloat book_width = BOOK_WIDTH;
|
||||
CGFloat book_height = BOOK_HEIGHT;
|
||||
|
||||
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionShowHideTransitionViews animations:^{
|
||||
|
||||
self->contentView.frame = CGRectMake(kHalfMargin, (kGeometricHeight(SCREEN_WIDTH, 3, 1) + kLabelHeight + kMargin + kQuarterMargin + PUB_NAVBAR_HEIGHT), book_width, book_height);
|
||||
|
||||
coverView.frame = CGRectMake(kHalfMargin, (kGeometricHeight(SCREEN_WIDTH, 3, 1) + kLabelHeight + kMargin + kQuarterMargin + PUB_NAVBAR_HEIGHT), book_width, book_height);
|
||||
|
||||
coverView.layer.transform = transform;
|
||||
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
[coverView removeFromSuperview];
|
||||
|
||||
[self->contentView removeFromSuperview];
|
||||
|
||||
[transitionContext completeTransition:YES];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (UIImageView *)GetImageView:(UIImage *)image {
|
||||
|
||||
UIImageView *imageView = [[UIImageView alloc] init];
|
||||
|
||||
imageView.image = image;
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)screenCapture:(nullable UIView *)target {
|
||||
|
||||
if (!target) { return nil; }
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(target.frame.size, NO, 0.0);
|
||||
|
||||
[target.layer renderInContext: UIGraphicsGetCurrentContext()];
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBar.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_BottomPayBarType) {
|
||||
WXYZ_BottomPayBarTypeDownload,
|
||||
WXYZ_BottomPayBarTypeBuyChapter
|
||||
};
|
||||
|
||||
typedef void(^PaySuccessChaptersBlock)(NSArray <NSString *>*success_chapter_ids);
|
||||
|
||||
typedef void(^PayCancleChapterBlock)(NSArray <NSString *>*fail_chapter_ids);
|
||||
|
||||
typedef void(^PayFailChaptersBlock)(NSArray <NSString *>*fail_chapter_ids);
|
||||
|
||||
typedef void(^BottomPayBarHiddenBlock)(void);
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBar : UIView
|
||||
|
||||
@property (nonatomic, copy) PaySuccessChaptersBlock paySuccessChaptersBlock;
|
||||
|
||||
@property (nonatomic, copy) PayCancleChapterBlock payCancleChapterBlock;
|
||||
|
||||
@property (nonatomic, copy) PayFailChaptersBlock payFailChaptersBlock;
|
||||
|
||||
@property (nonatomic, copy) BottomPayBarHiddenBlock bottomPayBarHiddenBlock;
|
||||
|
||||
@property (nonatomic, assign) BOOL canTouchHiddenView;
|
||||
|
||||
- (instancetype)initWithChapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType;
|
||||
|
||||
- (instancetype)initWithChapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType buyChapterNum:(NSInteger)buyChapterNum;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame chapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType;
|
||||
|
||||
- (void)showBottomPayBar;
|
||||
|
||||
- (void)hiddenBottomPayBar;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,470 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBar.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBar.h"
|
||||
|
||||
#import "TFRechargeViewController.h"
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarTitleTableViewCell.h"
|
||||
#import "WXYZ_ChapterBottomPayBarOptionTableViewCell.h"
|
||||
#import "WXYZ_ChapterBottomPayBarBalanceTableViewCell.h"
|
||||
#import "WXYZ_ChapterBottomPayBarAutoBuyTableViewCell.h"
|
||||
#import "WXYZ_ChapterBottomPayBarCostTableViewCell.h"
|
||||
|
||||
#import "TFReaderBookManager.h"
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBar () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (nonatomic, strong) UITableView *mainTableView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBar
|
||||
{
|
||||
// 选项卡选择下标
|
||||
NSInteger _optionSelectIndex;
|
||||
|
||||
NSInteger _buyChapterNum;
|
||||
|
||||
WXYZ_BottomPayBarType _barType;
|
||||
TFProductionType _productionType;
|
||||
|
||||
TFProductionChapterModel *_chapterModel;
|
||||
WXYZ_ChapterPayBarModel *_payBarModel;
|
||||
}
|
||||
|
||||
- (instancetype)initWithChapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType
|
||||
{
|
||||
if (self = [self initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) chapterModel:chapterModel barType:barType productionType:productionType buyChapterNum:1]) {
|
||||
[[TFViewHelper getWindowRootController].view addSubview:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithChapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType buyChapterNum:(NSInteger)buyChapterNum
|
||||
{
|
||||
if (self = [self initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) chapterModel:chapterModel barType:barType productionType:productionType buyChapterNum:buyChapterNum]) {
|
||||
[[TFViewHelper getWindowRootController].view addSubview:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame chapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType
|
||||
{
|
||||
if (self = [self initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) chapterModel:chapterModel barType:barType productionType:productionType buyChapterNum:1]) {
|
||||
[[TFViewHelper getWindowRootController].view addSubview:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame chapterModel:(TFProductionChapterModel *)chapterModel barType:(WXYZ_BottomPayBarType)barType productionType:(TFProductionType)productionType buyChapterNum:(NSInteger)buyChapterNum
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
_barType = barType;
|
||||
_productionType = productionType;
|
||||
|
||||
_chapterModel = chapterModel;
|
||||
|
||||
_optionSelectIndex = 0;
|
||||
_buyChapterNum = buyChapterNum;
|
||||
|
||||
_canTouchHiddenView = YES;
|
||||
|
||||
self.backgroundColor = kBlackTransparentColor;
|
||||
[self initialize];
|
||||
[self createSubViews];
|
||||
[self netRequest];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initialize
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(netRequest) name:Notification_Login_Success object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(netRequest) name:Notification_Recharge_Success object:nil];
|
||||
}
|
||||
|
||||
- (void)createSubViews
|
||||
{
|
||||
self.mainTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
self.mainTableView.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, CGFLOAT_MIN);
|
||||
self.mainTableView.backgroundColor = [UIColor clearColor];
|
||||
self.mainTableView.showsVerticalScrollIndicator = NO;
|
||||
self.mainTableView.showsHorizontalScrollIndicator = NO;
|
||||
self.mainTableView.estimatedRowHeight = 100;
|
||||
self.mainTableView.sectionFooterHeight = 10;
|
||||
self.mainTableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.mainTableView.delegate = self;
|
||||
self.mainTableView.dataSource = self;
|
||||
self.mainTableView.scrollEnabled = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.mainTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
[self addSubview:self.mainTableView];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
return 3;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
{
|
||||
return [self createPayBarTitleTabelViewCellWithTabelView:tableView];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
return [self createPayBarBalanceTabelViewCellWithTabelView:tableView];
|
||||
} else {
|
||||
WS(weakSelf)
|
||||
static NSString *cellName = @"WXYZ_ChapterBottomPayBarOptionTableViewCell";
|
||||
WXYZ_ChapterBottomPayBarOptionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
|
||||
if (!cell) {
|
||||
cell = [[WXYZ_ChapterBottomPayBarOptionTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellName];
|
||||
}
|
||||
cell.hiddenEndLine = NO;
|
||||
cell.pay_options = _payBarModel.pay_options;
|
||||
cell.payOptionClickBlock = ^(WXYZ_ChapterPayBarOptionModel * _Nonnull chapterOptionModel, NSInteger selectIndex) {
|
||||
_optionSelectIndex = selectIndex;
|
||||
_buyChapterNum = chapterOptionModel.buy_num;
|
||||
[weakSelf.mainTableView reloadData];
|
||||
};
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
return [self createPayBarCostTableViewCellWithTableView:tableView];
|
||||
} else {
|
||||
return [self createPayBarBalanceTabelViewCellWithTabelView:tableView];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
static NSString *cellName = @"WXYZ_ChapterBottomPayBarAutoBuyTableViewCell";
|
||||
WXYZ_ChapterBottomPayBarAutoBuyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
|
||||
if (!cell) {
|
||||
cell = [[WXYZ_ChapterBottomPayBarAutoBuyTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellName];
|
||||
}
|
||||
cell.hiddenEndLine = NO;
|
||||
|
||||
return cell;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
return [self createPayBarCostTableViewCellWithTableView:tableView];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return [[UITableViewCell alloc] init];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)createPayBarTitleTabelViewCellWithTabelView:(UITableView *)tableView
|
||||
{
|
||||
static NSString *cellName = @"WXYZ_ChapterBottomPayBarTitleTableViewCell";
|
||||
WXYZ_ChapterBottomPayBarTitleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
|
||||
if (!cell) {
|
||||
cell = [[WXYZ_ChapterBottomPayBarTitleTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellName];
|
||||
}
|
||||
cell.hiddenEndLine = NO;
|
||||
cell.buyOptionModel = [_payBarModel.pay_options objectOrNilAtIndex:_optionSelectIndex];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)createPayBarBalanceTabelViewCellWithTabelView:(UITableView *)tableView
|
||||
{
|
||||
static NSString *cellName = @"WXYZ_ChapterBottomPayBarBalanceTableViewCell";
|
||||
WXYZ_ChapterBottomPayBarBalanceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
|
||||
if (!cell) {
|
||||
cell = [[WXYZ_ChapterBottomPayBarBalanceTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellName];
|
||||
}
|
||||
cell.hiddenEndLine = NO;
|
||||
cell.base_info = _payBarModel.base_info;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)createPayBarCostTableViewCellWithTableView:(UITableView *)tableView
|
||||
{
|
||||
// WS(weakSelf)
|
||||
static NSString *cellName = @"WXYZ_ChapterBottomPayBarCostTableViewCell";
|
||||
WXYZ_ChapterBottomPayBarCostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellName];
|
||||
if (!cell) {
|
||||
cell = [[WXYZ_ChapterBottomPayBarCostTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellName];
|
||||
}
|
||||
cell.base_info = _payBarModel.base_info;
|
||||
cell.buyOptionModel = [_payBarModel.pay_options objectOrNilAtIndex:_optionSelectIndex];
|
||||
cell.buyChapterClickBlock = ^(BOOL needRecharge) {
|
||||
if (!TFUserInfoManager.isLogin) {
|
||||
[TFLoginOptionsViewController presentLoginView:nil];
|
||||
// [kMainWindow sendSubviewToBack:weakSelf];
|
||||
} else {
|
||||
if (needRecharge) {
|
||||
|
||||
TFRechargeViewController *vc = [[TFRechargeViewController alloc] init];
|
||||
vc.production_id = _chapterModel.production_id;
|
||||
vc.productionType = _productionType;
|
||||
TFNavigationController *t_nav = [[TFNavigationController alloc] initWithRootViewController:vc];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:t_nav animated:YES completion:nil];
|
||||
// [kMainWindow sendSubviewToBack:weakSelf];
|
||||
} else {
|
||||
WS(weakSelf)
|
||||
[weakSelf hiddenBottomPayBar];
|
||||
[weakSelf chapterPayRequest];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if ((_barType == WXYZ_BottomPayBarTypeDownload && indexPath.row == 2) || indexPath.row == 4) {
|
||||
return 60;
|
||||
}
|
||||
return 50;
|
||||
}
|
||||
|
||||
//section头间距
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return CGFLOAT_MIN;
|
||||
}
|
||||
//section头视图
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, CGFLOAT_MIN)];
|
||||
view.backgroundColor = [UIColor clearColor];
|
||||
return view;
|
||||
}
|
||||
|
||||
//section底部间距
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
|
||||
{
|
||||
return PUB_TABBAR_OFFSET;
|
||||
}
|
||||
//section底部视图
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
|
||||
{
|
||||
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, PUB_TABBAR_OFFSET)];
|
||||
view.backgroundColor = kColorRGBA(247, 248, 250, 1);
|
||||
return view;
|
||||
}
|
||||
|
||||
- (void)showBottomPayBar
|
||||
{
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
self.mainTableView.frame = CGRectMake(0, SCREEN_HEIGHT - (_barType == WXYZ_BottomPayBarTypeDownload?(2 * 50 + 60):(4 * 50 + 60)) - PUB_TABBAR_OFFSET, SCREEN_WIDTH, (_barType == WXYZ_BottomPayBarTypeDownload?(2 * 50 + 60):(4 * 50 + 60)) + PUB_TABBAR_OFFSET);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)hiddenBottomPayBar
|
||||
{
|
||||
if (!self.hidden) {
|
||||
|
||||
[UIView animateWithDuration:kAnimatedDurationFast animations:^{
|
||||
self.mainTableView.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, (_barType == WXYZ_BottomPayBarTypeDownload?(2 * 50 + 60):(4 * 50 + 60)) + PUB_TABBAR_OFFSET);
|
||||
} completion:^(BOOL finished) {
|
||||
[self removeAllSubviews];
|
||||
[self removeFromSuperview];
|
||||
self.hidden = YES;
|
||||
if (self.bottomPayBarHiddenBlock) {
|
||||
self.bottomPayBarHiddenBlock();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 系统方法
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
if (self.canTouchHiddenView) {
|
||||
UIView *touchView = [[touches anyObject] view];
|
||||
if (![touchView isDescendantOfView:self.mainTableView]) {
|
||||
[self hiddenBottomPayBar];
|
||||
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"您已取消购买")];
|
||||
|
||||
if (self.payCancleChapterBlock) {
|
||||
self.payCancleChapterBlock(_chapterModel.chapter_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
if (CGRectContainsPoint(CGRectMake(0, 0, SCREEN_WIDTH, PUB_NAVBAR_HEIGHT), point) && !self.canTouchHiddenView) {
|
||||
return nil;
|
||||
}
|
||||
return [super hitTest:point withEvent:event];
|
||||
}
|
||||
|
||||
- (void)netRequest
|
||||
{
|
||||
NSString *url = @"";
|
||||
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
|
||||
|
||||
switch (_productionType) {
|
||||
case TFProductionTypeNovel:
|
||||
case TFProductionTypeAi:
|
||||
{
|
||||
url = Book_Buy_Index;
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"book_id"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
[parameters setObject:@"down" forKey:@"page_from"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_buyChapterNum] forKey:@"num"];
|
||||
} else {
|
||||
[parameters setObject:@"read" forKey:@"page_from"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TFProductionTypeComic:
|
||||
{
|
||||
url = Comic_Buy_Index;
|
||||
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"comic_id"];
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
[parameters setObject:[_chapterModel.chapter_ids componentsJoinedByString:@","] forKey:@"chapter_id"];
|
||||
[parameters setObject:@"down" forKey:@"page_from"];
|
||||
} else {
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
[parameters setObject:@"read" forKey:@"page_from"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TFProductionTypeAudio:
|
||||
{
|
||||
url = Audio_Buy_Index;
|
||||
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"audio_id"];
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
[parameters setObject:[_chapterModel.chapter_ids componentsJoinedByString:@","] forKey:@"chapter_id"];
|
||||
[parameters setObject:@"down" forKey:@"page_from"];
|
||||
} else {
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
[parameters setObject:@"read" forKey:@"page_from"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
WS(weakSelf)
|
||||
[TFNetworkTools POST:url parameters:[parameters copy] model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
SS(strongSelf)
|
||||
[TFUserInfoManager shareInstance].totalRemain = [[[t_model objectForKey:@"data"] objectForKey:@"remain"] integerValue];
|
||||
if (strongSelf) {
|
||||
strongSelf->_payBarModel = [WXYZ_ChapterPayBarModel modelWithDictionary:[t_model objectForKey:@"data"]];
|
||||
}
|
||||
[weakSelf.mainTableView reloadData];
|
||||
}
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
- (void)chapterPayRequest
|
||||
{
|
||||
NSString *url = @"";
|
||||
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
|
||||
|
||||
switch (_productionType) {
|
||||
case TFProductionTypeNovel:
|
||||
case TFProductionTypeAi:
|
||||
{
|
||||
url = Book_Buy_Chapter;
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"book_id"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_buyChapterNum] forKey:@"num"];
|
||||
}
|
||||
break;
|
||||
case TFProductionTypeComic:
|
||||
{
|
||||
url = Comic_Buy_Chapter;
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"comic_id"];
|
||||
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
[parameters setObject:[_chapterModel.chapter_ids componentsJoinedByString:@","] forKey:@"chapter_id"];
|
||||
|
||||
} else {
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_buyChapterNum] forKey:@"num"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TFProductionTypeAudio:
|
||||
{
|
||||
url = Audio_Buy_Chapter;
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.production_id] forKey:@"audio_id"];
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_buyChapterNum] forKey:@"num"];
|
||||
|
||||
if (_barType == WXYZ_BottomPayBarTypeDownload) {
|
||||
[parameters setObject:[_chapterModel.chapter_ids componentsJoinedByString:@","] forKey:@"chapter_id"];
|
||||
} else {
|
||||
[parameters setObject:[TFUtilsHelper formatStringWithInteger:_chapterModel.chapter_id] forKey:@"chapter_id"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[TFNetworkTools POST:url parameters:parameters model:nil success:^(BOOL isSuccess, id _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
|
||||
NSArray<NSString *> *t_arr = [requestModel.data objectForKey:@"chapter_ids"];
|
||||
if (self.paySuccessChaptersBlock) {
|
||||
self.paySuccessChaptersBlock(t_arr);
|
||||
}
|
||||
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"购买成功")];
|
||||
NSInteger index = [TFReaderBookManager sharedManager].currentChapterIndex;
|
||||
for (NSInteger i = index; i < index + t_arr.count; i++) {
|
||||
[TFReaderBookManager sharedManager].bookModel.chapter_list[index].is_preview = NO;
|
||||
}
|
||||
if (_productionType == TFProductionTypeNovel || _productionType == TFProductionTypeAi) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:NSNotification_Retry_Chapter object:nil];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_Production_Pay_Success object:t_arr];
|
||||
|
||||
} else {
|
||||
!self.payFailChaptersBlock ?: self.payFailChaptersBlock(_chapterModel.chapter_ids);
|
||||
}
|
||||
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
||||
[TFPromptManager showPromptWithError:error defaultText:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarAutoBuyTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBarAutoBuyTableViewCell : TFBasicTableViewCell
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarAutoBuyTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarAutoBuyTableViewCell.h"
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import "KLSwitch.h"
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBarAutoBuyTableViewCell
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
WS(weakSelf)
|
||||
KLSwitch *autoBuySwitch = [[KLSwitch alloc] initWithFrame:CGRectMake(SCREEN_WIDTH - 51 - kMargin, 10, 51, 31) didChangeHandler:^(BOOL isOn) {
|
||||
[weakSelf autoBuyNetRequest];
|
||||
}];
|
||||
autoBuySwitch.transform = CGAffineTransformMakeScale(0.7, 0.7);//缩放
|
||||
autoBuySwitch.onTintColor = kMainColor;
|
||||
[autoBuySwitch setDefaultOnState:[TFUserInfoManager shareInstance].auto_sub];
|
||||
[self.contentView addSubview:autoBuySwitch];
|
||||
|
||||
[autoBuySwitch mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
make.right.mas_equalTo(self.contentView.mas_right).with.offset(- kMargin);
|
||||
make.width.mas_equalTo(51);
|
||||
make.height.mas_offset(31);
|
||||
}];
|
||||
|
||||
|
||||
UILabel *autoBuyTitleLabel = [[UILabel alloc] init];
|
||||
autoBuyTitleLabel.text = TFLocalizedString(@"自动购买下一章");
|
||||
autoBuyTitleLabel.textColor = kBlackColor;
|
||||
autoBuyTitleLabel.font = kMainFont;
|
||||
autoBuyTitleLabel.textAlignment = NSTextAlignmentLeft;
|
||||
[self.contentView addSubview:autoBuyTitleLabel];
|
||||
|
||||
[autoBuyTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.contentView.mas_left).with.offset(kMargin);
|
||||
make.right.mas_equalTo(autoBuySwitch.mas_left).with.offset(- kHalfMargin);
|
||||
make.top.mas_equalTo(self.contentView.mas_top);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)autoBuyNetRequest
|
||||
{
|
||||
[TFNetworkTools POST:Auto_Sub_Chapter parameters:nil model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) {
|
||||
if (isSuccess) {
|
||||
NSString *auto_sub_state = [NSString stringWithFormat:@"%@", [[t_model objectForKey:@"data"] objectForKey:@"auto_sub"]];
|
||||
if (auto_sub_state && auto_sub_state.length > 0) {
|
||||
AudioServicesPlaySystemSound(1519);
|
||||
[TFUserInfoManager shareInstance].auto_sub = [auto_sub_state isEqualToString:@"1"];
|
||||
}
|
||||
}
|
||||
} failure:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarBalanceTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
#import "WXYZ_ChapterPayBarModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBarBalanceTableViewCell : TFBasicTableViewCell
|
||||
|
||||
@property (nonatomic, strong) WXYZ_ChapterPayBarInfoModel *base_info;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarBalanceTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarBalanceTableViewCell.h"
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBarBalanceTableViewCell
|
||||
{
|
||||
UILabel *balanceDetailTitleLabel;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
UILabel *balanceTitleLabel = [[UILabel alloc] init];
|
||||
balanceTitleLabel.text = TFLocalizedString(@"账户余额");
|
||||
balanceTitleLabel.textColor = kBlackColor;
|
||||
balanceTitleLabel.font = kMainFont;
|
||||
balanceTitleLabel.textAlignment = NSTextAlignmentLeft;
|
||||
[self.contentView addSubview:balanceTitleLabel];
|
||||
|
||||
[balanceTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.contentView.mas_left).with.offset(kMargin);
|
||||
make.width.mas_equalTo(150);
|
||||
make.top.mas_equalTo(self.contentView.mas_top);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom);
|
||||
}];
|
||||
|
||||
balanceDetailTitleLabel = [[UILabel alloc] init];
|
||||
balanceDetailTitleLabel.text = [NSString stringWithFormat:@"0%@", Main_Unit_Name];
|
||||
balanceDetailTitleLabel.textColor = kGrayTextColor;
|
||||
balanceDetailTitleLabel.font = kFont12;
|
||||
balanceDetailTitleLabel.textAlignment = NSTextAlignmentRight;
|
||||
[self.contentView addSubview:balanceDetailTitleLabel];
|
||||
|
||||
[balanceDetailTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(balanceTitleLabel.mas_right);
|
||||
make.right.mas_equalTo(self.contentView.mas_right).with.offset(- kMargin);
|
||||
make.top.mas_equalTo(balanceTitleLabel.mas_top);
|
||||
make.height.mas_equalTo(balanceTitleLabel.mas_height);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setBase_info:(WXYZ_ChapterPayBarInfoModel *)base_info
|
||||
{
|
||||
if (_base_info != base_info) {
|
||||
_base_info = base_info;
|
||||
|
||||
if (TFUserInfoManager.isLogin) {
|
||||
NSString *constString = @"";
|
||||
if (base_info.gold_remain == 0 && base_info.silver_remain > 0) {
|
||||
constString = [constString stringByAppendingString:[NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:base_info.silver_remain], base_info.subUnit?:@""]];
|
||||
} else if (base_info.gold_remain > 0 && base_info.silver_remain == 0) {
|
||||
constString = [constString stringByAppendingString:[NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:base_info.gold_remain], base_info.unit?:@""]];
|
||||
} else {
|
||||
constString = [constString stringByAppendingString:[NSString stringWithFormat:@"%@%@ + %@%@", [TFUtilsHelper formatStringWithInteger:base_info.gold_remain], base_info.unit?:@"", [TFUtilsHelper formatStringWithInteger:base_info.silver_remain], base_info.subUnit?:@""]];
|
||||
}
|
||||
balanceDetailTitleLabel.text = constString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarCostTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
#import "WXYZ_ChapterPayBarModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBarCostTableViewCell : TFBasicTableViewCell
|
||||
|
||||
@property (nonatomic, copy) void (^buyChapterClickBlock)(BOOL needRecharge);
|
||||
|
||||
@property (nonatomic, strong) WXYZ_ChapterPayBarInfoModel *base_info;
|
||||
|
||||
@property (nonatomic, strong) WXYZ_ChapterPayBarOptionModel *buyOptionModel;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarCostTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarCostTableViewCell.h"
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBarCostTableViewCell
|
||||
{
|
||||
UIButton *buyChapterButton;
|
||||
UILabel *buyPriceLabel;
|
||||
UILabel *buyOriginalPriceLabel;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
self.contentView.backgroundColor = kColorRGBA(247, 248, 250, 1);
|
||||
|
||||
buyChapterButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
buyChapterButton.layer.cornerRadius = 4;
|
||||
buyChapterButton.clipsToBounds = YES;
|
||||
buyChapterButton.backgroundColor = kMainColor;
|
||||
[buyChapterButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[buyChapterButton.titleLabel setFont:kMainFont];
|
||||
[buyChapterButton addTarget:self action:@selector(buyChapterButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.contentView addSubview:buyChapterButton];
|
||||
|
||||
[buyChapterButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.mas_equalTo(self.contentView.mas_right).with.offset(- kMargin);
|
||||
make.centerY.mas_equalTo(self.contentView.mas_centerY);
|
||||
make.height.mas_equalTo(35);
|
||||
make.width.mas_equalTo(100);
|
||||
}];
|
||||
|
||||
buyOriginalPriceLabel = [[UILabel alloc] init];
|
||||
buyOriginalPriceLabel.backgroundColor = [UIColor clearColor];
|
||||
buyOriginalPriceLabel.font = kFont12;
|
||||
buyOriginalPriceLabel.textAlignment = NSTextAlignmentLeft;
|
||||
buyOriginalPriceLabel.numberOfLines = 1;
|
||||
buyOriginalPriceLabel.textColor = kBlackColor;
|
||||
[self.contentView addSubview:buyOriginalPriceLabel];
|
||||
|
||||
[buyOriginalPriceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.contentView.mas_left).with.offset(kMargin);
|
||||
make.right.mas_equalTo(buyChapterButton.mas_left).with.offset(- kHalfMargin);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom).with.offset(- kHalfMargin);
|
||||
make.height.mas_equalTo(20);
|
||||
}];
|
||||
|
||||
buyPriceLabel = [[UILabel alloc] init];
|
||||
buyPriceLabel.backgroundColor = [UIColor clearColor];
|
||||
buyPriceLabel.font = kMainFont;
|
||||
buyPriceLabel.textAlignment = NSTextAlignmentLeft;
|
||||
buyPriceLabel.numberOfLines = 1;
|
||||
[self.contentView addSubview:buyPriceLabel];
|
||||
|
||||
[buyPriceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(buyOriginalPriceLabel.mas_left);
|
||||
make.right.mas_equalTo(buyOriginalPriceLabel.mas_right);
|
||||
make.top.mas_equalTo(self.contentView.mas_top).with.offset(kHalfMargin);
|
||||
make.bottom.mas_equalTo(buyOriginalPriceLabel.mas_top);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setBuyOptionModel:(WXYZ_ChapterPayBarOptionModel *)buyOptionModel
|
||||
{
|
||||
_buyOptionModel = buyOptionModel;
|
||||
|
||||
if (TFUserInfoManager.isLogin) {
|
||||
if (self.base_info.remain - buyOptionModel.total_price >= 0) {
|
||||
[buyChapterButton setTitle:TFLocalizedString(@"确认购买") forState:UIControlStateNormal];
|
||||
} else {
|
||||
[buyChapterButton setTitle:TFLocalizedString(@"充值并购买") forState:UIControlStateNormal];
|
||||
}
|
||||
} else {
|
||||
[buyChapterButton setTitle:TFLocalizedString(@"登录后购买") forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
// 实付
|
||||
buyPriceLabel.attributedText = [self getPriceString];
|
||||
|
||||
// 原价
|
||||
buyOriginalPriceLabel.attributedText = [self getOriginalPriceString];
|
||||
|
||||
if (buyOptionModel.discount.length > 0) {
|
||||
[buyOriginalPriceLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_equalTo(20);
|
||||
}];
|
||||
} else {
|
||||
[buyOriginalPriceLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_equalTo(CGFLOAT_MIN);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBase_info:(WXYZ_ChapterPayBarInfoModel *)base_info
|
||||
{
|
||||
_base_info = base_info;
|
||||
}
|
||||
|
||||
- (void)buyChapterButtonClick
|
||||
{
|
||||
if (self.buyChapterClickBlock) {
|
||||
self.buyChapterClickBlock((self.base_info.remain - self.buyOptionModel.total_price <= 0));
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getPriceString
|
||||
{
|
||||
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] init];
|
||||
|
||||
NSString *suffix = nil;
|
||||
|
||||
if (self.buyOptionModel.actual_cost.gold_cost == 0 && self.buyOptionModel.actual_cost.silver_cost > 0) {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@", TFLocalizedString(@"实付:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.subUnit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.subUnit?:@""];
|
||||
|
||||
} else if (self.buyOptionModel.actual_cost.gold_cost > 0 && self.buyOptionModel.actual_cost.silver_cost == 0) {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@", TFLocalizedString(@"实付:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.unit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.unit?:@""];
|
||||
|
||||
} else {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@ + %@%@", TFLocalizedString(@"实付:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.unit?:@"", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.silver_cost], self.base_info.subUnit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@ + %@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.gold_cost], self.base_info.unit?:@"", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.actual_cost.silver_cost], self.base_info.subUnit?:@""];
|
||||
}
|
||||
|
||||
|
||||
[attributedStr addAttribute:NSForegroundColorAttributeName value:kMainColor range:NSMakeRange(attributedStr.length - suffix.length, suffix.length)];
|
||||
return attributedStr;
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getOriginalPriceString
|
||||
{
|
||||
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] init];
|
||||
|
||||
NSString *suffix = nil;
|
||||
|
||||
if (self.buyOptionModel.original_cost.gold_cost == 0 && self.buyOptionModel.original_cost.silver_cost > 0) {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@", TFLocalizedString(@"原价:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.subUnit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.subUnit?:@""];
|
||||
|
||||
} else if (self.buyOptionModel.original_cost.gold_cost > 0 && self.buyOptionModel.original_cost.silver_cost == 0) {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@", TFLocalizedString(@"原价:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.unit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.unit?:@""];
|
||||
|
||||
} else {
|
||||
[attributedStr appendString:[NSString stringWithFormat:@"%@%@%@ + %@%@", TFLocalizedString(@"原价:"), [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.unit?:@"", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.silver_cost], self.base_info.subUnit?:@""]];
|
||||
suffix = [NSString stringWithFormat:@"%@%@ + %@%@", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.gold_cost], self.base_info.unit?:@"", [TFUtilsHelper formatStringWithInteger:self.buyOptionModel.original_cost.silver_cost], self.base_info.subUnit?:@""];
|
||||
|
||||
}
|
||||
|
||||
[attributedStr addAttribute:NSForegroundColorAttributeName value:kGrayTextColor range:NSMakeRange(0, attributedStr.length)];
|
||||
[attributedStr addAttributes:@{NSStrikethroughStyleAttributeName : @(NSUnderlinePatternSolid | NSUnderlineStyleSingle), NSBaselineOffsetAttributeName : @(0)} range:NSMakeRange(attributedStr.length - suffix.length, suffix.length)];
|
||||
return attributedStr;
|
||||
}
|
||||
|
||||
@end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarOptionTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
#import "WXYZ_ChapterPayBarModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBarOptionTableViewCell : TFBasicTableViewCell
|
||||
|
||||
@property (nonatomic, copy) void (^payOptionClickBlock)(WXYZ_ChapterPayBarOptionModel *chapterOptionModel, NSInteger selectIndex);
|
||||
|
||||
@property (nonatomic, strong) NSArray <WXYZ_ChapterPayBarOptionModel *> *pay_options;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarOptionTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarOptionTableViewCell.h"
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBarOptionTableViewCell
|
||||
{
|
||||
UIScrollView *optionScorllView;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
optionScorllView = [[UIScrollView alloc] init];
|
||||
optionScorllView.showsVerticalScrollIndicator = NO;
|
||||
optionScorllView.showsHorizontalScrollIndicator = NO;
|
||||
optionScorllView.backgroundColor = [UIColor whiteColor];
|
||||
[self.contentView addSubview:optionScorllView];
|
||||
|
||||
[optionScorllView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.contentView.mas_left);
|
||||
make.top.mas_equalTo(self.contentView.mas_top);
|
||||
make.width.mas_equalTo(self.contentView.mas_width);
|
||||
make.height.mas_equalTo(self.contentView.mas_height);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setPay_options:(NSArray<WXYZ_ChapterPayBarOptionModel *> *)pay_options
|
||||
{
|
||||
if (_pay_options != pay_options) {
|
||||
_pay_options = pay_options;
|
||||
|
||||
[optionScorllView removeAllSubviews];
|
||||
|
||||
NSInteger buttonNum = pay_options.count;
|
||||
CGFloat button_H = 30;//按钮高
|
||||
CGFloat margin_X = kMargin;//第一个按钮的X坐标
|
||||
CGFloat margin_Y = 10;//第一个按钮的Y坐标
|
||||
CGFloat space_X = kHalfMargin;//按钮间距
|
||||
CGFloat button_X = margin_X;
|
||||
CGFloat button_W = - 10;
|
||||
for (NSInteger i = 0; i < buttonNum; i++) {
|
||||
WXYZ_ChapterPayBarOptionModel *option = [pay_options objectOrNilAtIndex:i];
|
||||
button_X = button_X + button_W + space_X;
|
||||
button_W = [TFViewHelper getDynamicWidthWithLabelFont:kMainFont labelHeight:30 labelText:option.label] + 10;//按钮宽
|
||||
|
||||
UIButton *optionButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
optionButton.frame = CGRectMake(button_X, margin_Y, button_W, button_H);
|
||||
optionButton.layer.cornerRadius = 4;
|
||||
optionButton.backgroundColor = [UIColor whiteColor];
|
||||
optionButton.tag = i;
|
||||
[optionButton.titleLabel setFont:kMainFont];
|
||||
[optionButton setTitle:option.label?:@"" forState:UIControlStateNormal];
|
||||
[optionButton setTitleColor:kGrayTextColor forState:UIControlStateNormal];
|
||||
[optionButton addTarget:self action:@selector(optionButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[optionScorllView addSubview:optionButton];
|
||||
|
||||
if (i == buttonNum - 1) {
|
||||
[optionScorllView setContentSize:CGSizeMake(optionButton.right, 0)];
|
||||
}
|
||||
|
||||
if (buttonNum > 0 && i == 0) {
|
||||
optionButton.backgroundColor = kMainColor;
|
||||
[optionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)optionButtonClick:(UIButton *)sender
|
||||
{
|
||||
[optionScorllView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([obj isKindOfClass:[UIButton class]]) {
|
||||
UIButton *button = (UIButton *)obj;
|
||||
button.backgroundColor = [UIColor whiteColor];
|
||||
[button setTitleColor:kGrayTextColor forState:UIControlStateNormal];
|
||||
}
|
||||
}];
|
||||
|
||||
sender.backgroundColor = kMainColor;
|
||||
[sender setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
||||
if (self.payOptionClickBlock) {
|
||||
self.payOptionClickBlock([self.pay_options objectOrNilAtIndex:sender.tag], sender.tag);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarTitleTableViewCell.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFBasicTableViewCell.h"
|
||||
#import "WXYZ_ChapterPayBarModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_ChapterBottomPayBarTitleTableViewCell : TFBasicTableViewCell
|
||||
|
||||
@property (nonatomic, strong) WXYZ_ChapterPayBarOptionModel *buyOptionModel;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// WXYZ_ChapterBottomPayBarTitleTableViewCell.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/7/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterBottomPayBarTitleTableViewCell.h"
|
||||
|
||||
@implementation WXYZ_ChapterBottomPayBarTitleTableViewCell
|
||||
{
|
||||
UILabel *payBarTitleLabel;
|
||||
|
||||
// 打折信息
|
||||
UILabel *discountTitleLabel;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[super createSubviews];
|
||||
|
||||
payBarTitleLabel = [[UILabel alloc] init];
|
||||
payBarTitleLabel.text = TFLocalizedString(@"从本章开始购买");
|
||||
payBarTitleLabel.textColor = kBlackColor;
|
||||
payBarTitleLabel.font = kMainFont;
|
||||
payBarTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:payBarTitleLabel];
|
||||
|
||||
[payBarTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.mas_equalTo(self.contentView.mas_top);
|
||||
make.centerX.mas_equalTo(self.contentView.mas_centerX).with.offset(CGFLOAT_MIN);
|
||||
make.width.mas_equalTo(SCREEN_WIDTH);
|
||||
make.bottom.mas_equalTo(self.contentView.mas_bottom);
|
||||
}];
|
||||
|
||||
discountTitleLabel = [[UILabel alloc] init];
|
||||
discountTitleLabel.textColor = kWhiteColor;
|
||||
discountTitleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
discountTitleLabel.font = kFont10;
|
||||
discountTitleLabel.backgroundColor = kRedColor;
|
||||
discountTitleLabel.layer.cornerRadius = 2;
|
||||
discountTitleLabel.clipsToBounds = YES;
|
||||
[self.contentView addSubview:discountTitleLabel];
|
||||
|
||||
[discountTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(payBarTitleLabel.mas_right).with.offset(kQuarterMargin);
|
||||
make.centerY.mas_equalTo(payBarTitleLabel.mas_centerY);
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
make.height.mas_equalTo(16);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setBuyOptionModel:(WXYZ_ChapterPayBarOptionModel *)buyOptionModel
|
||||
{
|
||||
_buyOptionModel = buyOptionModel;
|
||||
|
||||
payBarTitleLabel.text = buyOptionModel.label?:TFLocalizedString(@"从本章开始购买");
|
||||
|
||||
if (buyOptionModel.discount.length > 0 && buyOptionModel.discount) {
|
||||
discountTitleLabel.text = buyOptionModel.discount?:@"";
|
||||
|
||||
CGFloat t_width = [TFViewHelper getDynamicWidthWithLabel:discountTitleLabel] + kQuarterMargin;
|
||||
[discountTitleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo(t_width);
|
||||
}];
|
||||
|
||||
[payBarTitleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self.contentView.mas_centerX).with.offset(- t_width / 2);
|
||||
make.width.mas_equalTo([TFViewHelper getDynamicWidthWithLabel:payBarTitleLabel]);
|
||||
}];
|
||||
} else {
|
||||
[discountTitleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
}];
|
||||
|
||||
[payBarTitleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.mas_equalTo(self.contentView.mas_centerX).with.offset(CGFLOAT_MIN);
|
||||
make.width.mas_equalTo(SCREEN_WIDTH);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// WXYZ_ChapterPayBarModel.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/7/15.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class WXYZ_ChapterPayBarInfoModel, WXYZ_ChapterPayBarOptionModel, Actual_Cost, Original_Cost;
|
||||
@interface WXYZ_ChapterPayBarModel : NSObject
|
||||
|
||||
@property (nonatomic, strong) WXYZ_ChapterPayBarInfoModel *base_info;
|
||||
|
||||
@property (nonatomic, strong) NSArray <WXYZ_ChapterPayBarOptionModel *> *pay_options;
|
||||
|
||||
@end
|
||||
|
||||
@interface WXYZ_ChapterPayBarInfoModel : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSInteger remain;
|
||||
|
||||
@property (nonatomic, assign) NSInteger gold_remain;
|
||||
|
||||
@property (nonatomic, assign) NSInteger silver_remain;
|
||||
|
||||
@property (nonatomic, copy) NSString *unit;
|
||||
|
||||
@property (nonatomic, copy) NSString *subUnit;
|
||||
|
||||
@property (nonatomic, assign) NSInteger chapter_id;
|
||||
|
||||
@property (nonatomic, assign) NSInteger auto_sub;
|
||||
|
||||
@end
|
||||
|
||||
@interface WXYZ_ChapterPayBarOptionModel : NSObject
|
||||
|
||||
@property (nonatomic, strong) Original_Cost *original_cost; // 原价
|
||||
|
||||
@property (nonatomic, assign) NSInteger buy_num;
|
||||
|
||||
@property (nonatomic, assign) NSInteger original_price;
|
||||
|
||||
@property (nonatomic, strong) Actual_Cost *actual_cost; // 实际需要消费的金额
|
||||
|
||||
@property (nonatomic, copy) NSString *discount;
|
||||
|
||||
@property (nonatomic, copy) NSString *label;
|
||||
|
||||
@property (nonatomic, assign) NSInteger total_price;
|
||||
|
||||
@end
|
||||
|
||||
@interface Actual_Cost : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSInteger gold_cost;
|
||||
|
||||
@property (nonatomic, assign) NSInteger silver_cost;
|
||||
|
||||
@end
|
||||
|
||||
@interface Original_Cost : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSInteger gold_cost;
|
||||
|
||||
@property (nonatomic, assign) NSInteger silver_cost;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// WXYZ_ChapterPayBarModel.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/7/15.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ChapterPayBarModel.h"
|
||||
|
||||
@implementation WXYZ_ChapterPayBarModel
|
||||
|
||||
+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass{
|
||||
return @{@"pay_options" : [WXYZ_ChapterPayBarOptionModel class]};
|
||||
}
|
||||
|
||||
+ (NSDictionary *)modelCustomPropertyMapper {
|
||||
return @{
|
||||
@"pay_options" :@"buy_option"
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ChapterPayBarInfoModel
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation WXYZ_ChapterPayBarOptionModel
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation Actual_Cost
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation Original_Cost
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// WXYZ_ShadowView.h
|
||||
// WXYZ_ShadowView
|
||||
//
|
||||
// Created by Sunshine on 2019/1/29.
|
||||
// Copyright © 2019 com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, YCShadowSide) {
|
||||
YCShadowSideTop = 1 << 0,
|
||||
YCShadowSideBottom = 1 << 1,
|
||||
YCShadowSideLeft = 1 << 2,
|
||||
YCShadowSideRight = 1 << 3,
|
||||
YCShadowSideAllSides = ~0UL
|
||||
};
|
||||
|
||||
@interface WXYZ_ShadowView : UIView
|
||||
|
||||
/**
|
||||
* 设置四周阴影: shaodwRadius:5 shadowColor:[UIColor colorWithWhite:0 alpha:0.3]
|
||||
*/
|
||||
- (void)yc_shaodw;
|
||||
/**
|
||||
* 设置垂直方向的阴影
|
||||
*
|
||||
* @param shadowRadius 阴影半径
|
||||
* @param shadowColor 阴影颜色
|
||||
* @param shadowOffset 阴影b偏移
|
||||
*/
|
||||
- (void)yc_verticalShaodwRadius:(CGFloat)shadowRadius
|
||||
shadowColor:(UIColor *)shadowColor
|
||||
shadowOffset:(CGSize)shadowOffset;
|
||||
/**
|
||||
* 设置水平方向的阴影
|
||||
*
|
||||
* @param shadowRadius 阴影半径
|
||||
* @param shadowColor 阴影颜色
|
||||
* @param shadowOffset 阴影b偏移
|
||||
*/
|
||||
- (void)yc_horizontalShaodwRadius:(CGFloat)shadowRadius
|
||||
shadowColor:(UIColor *)shadowColor
|
||||
shadowOffset:(CGSize)shadowOffset;
|
||||
/**
|
||||
* 设置阴影
|
||||
*
|
||||
* @param shadowRadius 阴影半径
|
||||
* @param shadowColor 阴影颜色
|
||||
* @param shadowOffset 阴影b偏移
|
||||
* @param shadowSide 阴影边
|
||||
*/
|
||||
- (void)yc_shaodwRadius:(CGFloat)shadowRadius
|
||||
shadowColor:(UIColor *)shadowColor
|
||||
shadowOffset:(CGSize)shadowOffset
|
||||
byShadowSide:(YCShadowSide)shadowSide;
|
||||
|
||||
/**
|
||||
* 设置圆角(四周)
|
||||
*
|
||||
* @param cornerRadius 圆角半径
|
||||
*/
|
||||
- (void)yc_cornerRadius:(CGFloat)cornerRadius;
|
||||
/**
|
||||
* 设置圆角
|
||||
*
|
||||
* @param cornerRadius 圆角半径
|
||||
* @param corners 圆角边
|
||||
*/
|
||||
- (void)yc_cornerRadius:(CGFloat)cornerRadius
|
||||
byRoundingCorners:(UIRectCorner)corners;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,249 @@
|
||||
//
|
||||
// WXYZ_ShadowView.m
|
||||
// WXYZ_ShadowView
|
||||
//
|
||||
// Created by Sunshine on 2019/1/29.
|
||||
// Copyright © 2019 com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ShadowView.h"
|
||||
|
||||
@interface WXYZ_ShadowView()
|
||||
|
||||
@property(nonatomic, strong) UIColor *shadowColor;
|
||||
@property(nonatomic, assign) CGSize shadowOffset;
|
||||
@property(nonatomic, assign) CGFloat shadowRadius;
|
||||
@property(nonatomic, assign) NSInteger shadowSide;
|
||||
|
||||
@property(nonatomic, assign) CGFloat cornerRadius;
|
||||
@property(nonatomic, assign) UIRectCorner rectCorner;
|
||||
|
||||
@property(nonatomic, strong)UIView *backContentView;
|
||||
|
||||
@property(nonatomic, strong)CALayer *topShadowLayer;
|
||||
@property(nonatomic, strong)CALayer *botShadowLayer;
|
||||
@property(nonatomic, strong)CALayer *leftShadowLayer;
|
||||
@property(nonatomic, strong)CALayer *rightShadowLayer;
|
||||
@property(nonatomic, strong)CAShapeLayer *maskLayer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ShadowView
|
||||
#pragma mark - init 初始化自身
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setup];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if (self = [super initWithCoder:aDecoder]) {
|
||||
[self setup];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
#pragma mark - Life Cycle 生命周期的方法
|
||||
|
||||
#pragma mark - override 重载父类的方法
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor {
|
||||
self.backContentView.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (void)addSubview:(UIView *)view {
|
||||
if (view == _backContentView) {
|
||||
[super addSubview:view];
|
||||
} else {
|
||||
[self.backContentView addSubview:view];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.backContentView.frame = self.bounds;
|
||||
[self _setShadowWithShadowRadius:_shadowRadius shadowColor:_shadowColor shadowOffset:_shadowOffset byShadowSide:_shadowSide];
|
||||
[self _setCornerRadius:_cornerRadius byRoundingCorners:_rectCorner];
|
||||
}
|
||||
|
||||
#pragma mark - setupXxx Methods 初始化的方法
|
||||
- (void)setup {
|
||||
[self addSubview:self.backContentView];
|
||||
}
|
||||
|
||||
#pragma mark - public method 共有方法
|
||||
- (void)yc_shaodw {
|
||||
[self yc_shaodwRadius:5 shadowColor:[UIColor colorWithWhite:0 alpha:0.3] shadowOffset:CGSizeMake(0, 0) byShadowSide:YCShadowSideAllSides];
|
||||
}
|
||||
- (void)yc_verticalShaodwRadius:(CGFloat)shadowRadius shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset {
|
||||
[self yc_shaodwRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideTop|YCShadowSideBottom];
|
||||
}
|
||||
- (void)yc_horizontalShaodwRadius:(CGFloat)shadowRadius shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset {
|
||||
[self yc_shaodwRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideLeft|YCShadowSideRight];
|
||||
}
|
||||
- (void)yc_shaodwRadius:(CGFloat)shadowRadius shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset byShadowSide:(YCShadowSide)shadowSide {
|
||||
_shadowRadius = shadowRadius;
|
||||
_shadowColor = shadowColor;
|
||||
_shadowOffset = shadowOffset;
|
||||
_shadowSide = shadowSide;
|
||||
}
|
||||
- (void)yc_cornerRadius:(CGFloat)cornerRadius {
|
||||
_cornerRadius = cornerRadius;
|
||||
_rectCorner = UIRectCornerAllCorners;
|
||||
}
|
||||
- (void)yc_cornerRadius:(CGFloat)cornerRadius byRoundingCorners:(UIRectCorner)corners {
|
||||
_cornerRadius = cornerRadius;
|
||||
_rectCorner = corners;
|
||||
}
|
||||
|
||||
#pragma mark - Setter/Getter Methods set/get方法
|
||||
- (UIView *)backContentView {
|
||||
if (!_backContentView) {
|
||||
_backContentView = [[UIView alloc] init];
|
||||
_backContentView.backgroundColor = [UIColor whiteColor];
|
||||
_backContentView.layer.masksToBounds = YES;
|
||||
_backContentView.clipsToBounds = YES;
|
||||
[self insertSubview:_backContentView atIndex:0];
|
||||
}
|
||||
return _backContentView;
|
||||
}
|
||||
|
||||
- (CALayer *)topShadowLayer {
|
||||
if (!_topShadowLayer) {
|
||||
_topShadowLayer = [[CALayer alloc] init];
|
||||
}
|
||||
return _topShadowLayer;
|
||||
}
|
||||
- (CALayer *)botShadowLayer {
|
||||
if (!_botShadowLayer) {
|
||||
_botShadowLayer = [[CALayer alloc] init];
|
||||
}
|
||||
return _botShadowLayer;
|
||||
}
|
||||
- (CALayer *)leftShadowLayer {
|
||||
if (!_leftShadowLayer) {
|
||||
_leftShadowLayer = [[CALayer alloc] init];
|
||||
}
|
||||
return _leftShadowLayer;
|
||||
}
|
||||
- (CALayer *)rightShadowLayer {
|
||||
if (!_rightShadowLayer) {
|
||||
_rightShadowLayer = [[CALayer alloc] init];
|
||||
}
|
||||
return _rightShadowLayer;
|
||||
}
|
||||
- (CAShapeLayer *)maskLayer {
|
||||
if (!_maskLayer) {
|
||||
_maskLayer = [[CAShapeLayer alloc] init];
|
||||
}
|
||||
return _maskLayer;
|
||||
}
|
||||
|
||||
#pragma mark - private method 私有方法
|
||||
// 绘制圆角
|
||||
- (void)_setCornerRadius:(CGFloat)cornerRadius byRoundingCorners:(UIRectCorner)corners {
|
||||
|
||||
if (cornerRadius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
|
||||
self.maskLayer.frame = self.bounds;
|
||||
self.maskLayer.path = maskPath.CGPath;
|
||||
self.backContentView.layer.mask = self.maskLayer;
|
||||
}
|
||||
|
||||
// 绘制阴影
|
||||
- (void)_setShadowWithShadowRadius:(CGFloat)shadowRadius shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset byShadowSide:(YCShadowSide)shadowSide {
|
||||
|
||||
if (shadowRadius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shadowSide & YCShadowSideTop) {
|
||||
[self _setSingleSideShadowWithShadowRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideTop];
|
||||
}
|
||||
|
||||
if (shadowSide & YCShadowSideBottom) {
|
||||
[self _setSingleSideShadowWithShadowRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideBottom];
|
||||
}
|
||||
|
||||
if (shadowSide & YCShadowSideLeft) {
|
||||
[self _setSingleSideShadowWithShadowRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideLeft];
|
||||
}
|
||||
|
||||
if (shadowSide & YCShadowSideRight) {
|
||||
[self _setSingleSideShadowWithShadowRadius:shadowRadius shadowColor:shadowColor shadowOffset:shadowOffset byShadowSide:YCShadowSideRight];
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制单边阴影
|
||||
- (void)_setSingleSideShadowWithShadowRadius:(CGFloat)shadowRadius shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset byShadowSide:(YCShadowSide)shadowSide {
|
||||
|
||||
CALayer *shadowLayer = nil;
|
||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
||||
|
||||
if (shadowSide & YCShadowSideTop) {
|
||||
shadowLayer = self.topShadowLayer;
|
||||
shadowLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height*0.5);
|
||||
[path moveToPoint:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5)];
|
||||
[path addLineToPoint:(CGPointMake(0, 0))];
|
||||
[path addLineToPoint:(CGPointMake(self.bounds.size.width, 0))];
|
||||
} else if (shadowSide & YCShadowSideLeft) {
|
||||
shadowLayer = self.leftShadowLayer;
|
||||
shadowLayer.frame = CGRectMake(0, 0, self.frame.size.height*0.5, self.frame.size.height);
|
||||
[path moveToPoint:CGPointMake(self.bounds.size.width*0.5, self.bounds.size.height*0.5)];
|
||||
[path addLineToPoint:(CGPointMake(0, 0))];
|
||||
[path addLineToPoint:(CGPointMake(0, self.bounds.size.height))];
|
||||
} else if (shadowSide & YCShadowSideRight) {
|
||||
shadowLayer = self.rightShadowLayer;
|
||||
shadowLayer.frame = CGRectMake(self.frame.size.width*0.5, 0, self.frame.size.width*0.5, self.frame.size.height);
|
||||
[path moveToPoint:CGPointMake(0, self.bounds.size.height*0.5)];
|
||||
[path addLineToPoint:(CGPointMake(self.frame.size.width*0.5, 0))];
|
||||
[path addLineToPoint:(CGPointMake(self.frame.size.width*0.5, self.bounds.size.height))];
|
||||
} else if (shadowSide & YCShadowSideBottom) {
|
||||
shadowLayer = self.botShadowLayer;
|
||||
shadowLayer.frame = CGRectMake(0, self.frame.size.height*0.5, self.frame.size.width, self.frame.size.height*0.5);
|
||||
[path moveToPoint:CGPointMake(self.bounds.size.width*0.5, 0)];
|
||||
[path addLineToPoint:(CGPointMake(0, self.bounds.size.height*0.5))];
|
||||
[path addLineToPoint:(CGPointMake(self.bounds.size.width, self.bounds.size.height*0.5))];
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
|
||||
[self.layer insertSublayer:shadowLayer atIndex:0];
|
||||
|
||||
shadowLayer.masksToBounds = NO;
|
||||
|
||||
CGFloat components[4];
|
||||
[self _getRGBAComponents:components forColor:_shadowColor];
|
||||
|
||||
shadowLayer.shadowColor = [UIColor colorWithRed:components[0] green:components[1] blue:components[2] alpha:1.0].CGColor;
|
||||
shadowLayer.shadowOpacity = components[3];
|
||||
shadowLayer.shadowRadius = _shadowRadius;
|
||||
shadowLayer.shadowOffset = CGSizeMake(0, 0);
|
||||
shadowLayer.shadowPath = path.CGPath;
|
||||
}
|
||||
|
||||
- (void)_getRGBAComponents:(CGFloat [4])components forColor:(UIColor *)color {
|
||||
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
unsigned char resultingPixel[4] = {0};
|
||||
CGContextRef context = CGBitmapContextCreate(&resultingPixel,
|
||||
1,
|
||||
1,
|
||||
8,
|
||||
4,
|
||||
rgbColorSpace,
|
||||
kCGImageAlphaPremultipliedLast);
|
||||
CGContextSetFillColorWithColor(context, [color CGColor]);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(rgbColorSpace);
|
||||
|
||||
CGFloat a = resultingPixel[3] / 255.0;
|
||||
CGFloat unpremultiply = (a != 0.0) ? 1.0 / a / 255.0 : 0.0;
|
||||
for (int component = 0; component < 3; component++) {
|
||||
components[component] = resultingPixel[component] * unpremultiply;
|
||||
}
|
||||
components[3] = a;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// WXYZ_CountDownView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/4/9.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_CountDownView : UIView
|
||||
|
||||
@property (nonatomic, assign) NSInteger timeStamp;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,249 @@
|
||||
//
|
||||
// WXYZ_CountDownView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/4/9.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_CountDownView.h"
|
||||
#import "WXYZ_GCDTimer.h"
|
||||
|
||||
@interface WXYZ_CountDownView ()
|
||||
|
||||
@property (nonatomic, strong) UILabel *countDownLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *endActiveLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *hoursLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *minutesLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *secondsLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *hoursPointLabel;
|
||||
|
||||
@property (nonatomic, strong) UILabel *minutesPointLabel;
|
||||
|
||||
@property (nonatomic, strong) WXYZ_GCDTimer *timer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_CountDownView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self createSubViews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubViews
|
||||
{
|
||||
self.countDownLabel.hidden = NO;
|
||||
[self addSubview:self.countDownLabel];
|
||||
|
||||
self.endActiveLabel.hidden = YES;
|
||||
[self addSubview:self.endActiveLabel];
|
||||
|
||||
[self.countDownLabel addSubview:self.hoursLabel];
|
||||
[self.countDownLabel addSubview:self.minutesLabel];
|
||||
[self.countDownLabel addSubview:self.secondsLabel];
|
||||
[self.countDownLabel addSubview:self.hoursPointLabel];
|
||||
[self.countDownLabel addSubview:self.minutesPointLabel];
|
||||
|
||||
[self.hoursLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
make.height.mas_equalTo(self.mas_height);
|
||||
}];
|
||||
|
||||
[self.minutesLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.hoursLabel.mas_right).with.offset(5);
|
||||
make.top.mas_equalTo(self.hoursLabel.mas_top);
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
make.height.mas_equalTo(self.hoursLabel.mas_height);
|
||||
}];
|
||||
|
||||
[self.secondsLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.minutesLabel.mas_right).with.offset(5);
|
||||
make.top.mas_equalTo(self.hoursLabel.mas_top);
|
||||
make.width.mas_equalTo(CGFLOAT_MIN);
|
||||
make.height.mas_equalTo(self.hoursLabel.mas_height);
|
||||
}];
|
||||
|
||||
[self.hoursPointLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.hoursLabel.mas_right);
|
||||
make.top.mas_equalTo(self.hoursLabel.mas_top);
|
||||
make.width.mas_equalTo(5);
|
||||
make.height.mas_equalTo(self.hoursLabel.mas_height);
|
||||
}];
|
||||
|
||||
[self.minutesPointLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(self.minutesLabel.mas_right);
|
||||
make.top.mas_equalTo(self.minutesLabel.mas_top);
|
||||
make.width.mas_equalTo(5);
|
||||
make.height.mas_equalTo(self.minutesLabel.mas_height);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setTimeStamp:(NSInteger)timeStamp
|
||||
{
|
||||
_timeStamp = timeStamp;
|
||||
|
||||
if (_timeStamp != 0) {
|
||||
[self.timer stopTimer];
|
||||
[self.timer startTimerWithTimeDuration:timeStamp];
|
||||
} else {
|
||||
self.countDownLabel.hidden = YES;
|
||||
self.endActiveLabel.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getDetailTimeWithTimeStamp:(NSInteger)timeStamp
|
||||
{
|
||||
NSInteger ms = timeStamp;
|
||||
NSInteger ss = 1;
|
||||
NSInteger mi = ss * 60;
|
||||
NSInteger hh = mi * 60;
|
||||
|
||||
// 剩余的
|
||||
NSInteger hour = (ms) / hh;// 时
|
||||
NSInteger minute = (ms - hour * hh) / mi;// 分
|
||||
NSInteger second = (ms - hour * hh - minute * mi) / ss;// 秒
|
||||
|
||||
self.hoursLabel.text = [self formatTimeStringWithTimeStamp:hour];
|
||||
self.minutesLabel.text = [self formatTimeStringWithTimeStamp:minute];
|
||||
self.secondsLabel.text = [self formatTimeStringWithTimeStamp:second];
|
||||
self.hoursPointLabel.hidden = NO;
|
||||
self.minutesPointLabel.hidden = NO;
|
||||
|
||||
[self.hoursLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo([TFViewHelper getDynamicWidthWithLabel:self.hoursLabel]);
|
||||
}];
|
||||
|
||||
[self.minutesLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo([TFViewHelper getDynamicWidthWithLabel:self.minutesLabel]);
|
||||
}];
|
||||
|
||||
[self.secondsLabel mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.mas_equalTo([TFViewHelper getDynamicWidthWithLabel:self.secondsLabel]);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)formatTimeStringWithTimeStamp:(NSInteger)timeStamp
|
||||
{
|
||||
return [[NSString stringWithFormat:@"%zd", timeStamp] intValue] < 10?[NSString stringWithFormat:@"0%zd", timeStamp]:[NSString stringWithFormat:@"%zd", timeStamp];
|
||||
}
|
||||
|
||||
- (UILabel *)countDownLabel
|
||||
{
|
||||
if (!_countDownLabel) {
|
||||
_countDownLabel = [self basicLabel];
|
||||
}
|
||||
return _countDownLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)endActiveLabel
|
||||
{
|
||||
if (!_endActiveLabel) {
|
||||
_endActiveLabel = [self basicCountDownLabel];
|
||||
_endActiveLabel.font = kFont12;
|
||||
_endActiveLabel.text = TFLocalizedString(@"活动已结束");
|
||||
}
|
||||
return _endActiveLabel;
|
||||
}
|
||||
|
||||
- (WXYZ_GCDTimer *)timer
|
||||
{
|
||||
if (!_timer) {
|
||||
WS(weakSelf)
|
||||
_timer = [[WXYZ_GCDTimer alloc] initCountdownTimerWithTimeDuration:10 immediatelyCallBack:YES];
|
||||
_timer.timerRunningBlock = ^(NSUInteger runTimes, CGFloat currentTime) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.endActiveLabel.hidden = YES;
|
||||
weakSelf.countDownLabel.hidden = NO;
|
||||
[weakSelf getDetailTimeWithTimeStamp:currentTime];
|
||||
});
|
||||
};
|
||||
_timer.timerFinishedBlock = ^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.countDownLabel.hidden = YES;
|
||||
weakSelf.endActiveLabel.hidden = NO;
|
||||
});
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:Notification_EndOfTimeLimit object:nil];
|
||||
};
|
||||
}
|
||||
return _timer;
|
||||
}
|
||||
|
||||
- (UILabel *)hoursLabel
|
||||
{
|
||||
if (!_hoursLabel) {
|
||||
_hoursLabel = [self basicCountDownLabel];
|
||||
}
|
||||
return _hoursLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)minutesLabel
|
||||
{
|
||||
if (!_minutesLabel) {
|
||||
_minutesLabel = [self basicCountDownLabel];
|
||||
}
|
||||
return _minutesLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)secondsLabel
|
||||
{
|
||||
if (!_secondsLabel) {
|
||||
_secondsLabel = [self basicCountDownLabel];
|
||||
}
|
||||
return _secondsLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)hoursPointLabel
|
||||
{
|
||||
if (!_hoursPointLabel) {
|
||||
_hoursPointLabel = [self basicLabel];
|
||||
_hoursPointLabel.hidden = YES;
|
||||
_hoursPointLabel.text = @":";
|
||||
_hoursPointLabel.textColor = kBlackColor;
|
||||
_hoursPointLabel.textAlignment = NSTextAlignmentCenter;
|
||||
}
|
||||
return _hoursPointLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)minutesPointLabel
|
||||
{
|
||||
if (!_minutesPointLabel) {
|
||||
_minutesPointLabel = [self basicLabel];
|
||||
_minutesPointLabel.hidden = YES;
|
||||
_minutesPointLabel.text = @":";
|
||||
_minutesPointLabel.textColor = kBlackColor;
|
||||
_minutesPointLabel.textAlignment = NSTextAlignmentCenter;
|
||||
}
|
||||
return _minutesPointLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)basicLabel
|
||||
{
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
label.numberOfLines = 1;
|
||||
label.font = kFont12;
|
||||
return label;
|
||||
}
|
||||
|
||||
- (UILabel *)basicCountDownLabel
|
||||
{
|
||||
UILabel *label = [self basicLabel];
|
||||
label.layer.cornerRadius = 4;
|
||||
label.backgroundColor = kRedColor;
|
||||
label.textColor = kWhiteColor;
|
||||
label.clipsToBounds = YES;
|
||||
return label;
|
||||
}
|
||||
|
||||
@end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// WXYZ_CompositeEmbeddedTableView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/6/13.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_CompositeEmbeddedTableView : UITableView
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// WXYZ_CompositeEmbeddedTableView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2019/6/13.
|
||||
// Copyright © 2019 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_CompositeEmbeddedTableView.h"
|
||||
|
||||
@implementation WXYZ_CompositeEmbeddedTableView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
|
||||
{
|
||||
if (self = [super initWithFrame:frame style:style]) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.showsVerticalScrollIndicator = NO;
|
||||
self.showsHorizontalScrollIndicator = NO;
|
||||
self.sectionFooterHeight = 10;
|
||||
self.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// WXYZ_GuideView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/5/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_GuideType) {
|
||||
WXYZ_GuideTypeBookReader
|
||||
};
|
||||
|
||||
@interface WXYZ_GuideView : UIView
|
||||
|
||||
- (instancetype)initWithGuideType:(WXYZ_GuideType)guideType;
|
||||
|
||||
- (void)hidden;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// WXYZ_GuideView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/5/27.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_GuideView.h"
|
||||
|
||||
@implementation WXYZ_GuideView
|
||||
{
|
||||
WXYZ_GuideType _guideType;
|
||||
}
|
||||
|
||||
- (instancetype)initWithGuideType:(WXYZ_GuideType)guideType
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
_guideType = guideType;
|
||||
[self createSubviews];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hidden)]];
|
||||
|
||||
UILabel *leftView = [[UILabel alloc] init];
|
||||
leftView.backgroundColor = kColorRGBA(0, 0, 0, 0.4);
|
||||
leftView.numberOfLines = 0;
|
||||
leftView.text = TFLocalizedString(@"上\n一\n页");
|
||||
leftView.textColor = kWhiteColor;
|
||||
leftView.textAlignment = NSTextAlignmentCenter;
|
||||
leftView.font = [UIFont boldSystemFontOfSize:50];
|
||||
[self addSubview:leftView];
|
||||
|
||||
[leftView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(0);
|
||||
make.top.mas_equalTo(0);
|
||||
make.width.mas_equalTo(SCREEN_WIDTH / 3);
|
||||
make.height.mas_equalTo(self.mas_height);
|
||||
}];
|
||||
|
||||
NSMutableAttributedString * attriStr = [[NSMutableAttributedString alloc] initWithString:TFLocalizedString(@"菜单\n")];
|
||||
NSTextAttachment *attchImage = [[NSTextAttachment alloc] init];
|
||||
attchImage.image = [UIImage imageNamed:@"book_guide_tap"];
|
||||
attchImage.bounds = CGRectMake(0, 0, 50, 50);
|
||||
NSAttributedString *stringImage = [NSAttributedString attributedStringWithAttachment:attchImage];
|
||||
[attriStr appendAttributedString:stringImage];
|
||||
|
||||
UILabel *centerView = [[UILabel alloc] init];
|
||||
centerView.backgroundColor = kColorRGBA(0, 0, 0, 0.7);
|
||||
centerView.numberOfLines = 2;
|
||||
centerView.textColor = kWhiteColor;
|
||||
centerView.textAlignment = NSTextAlignmentCenter;
|
||||
centerView.font = [UIFont boldSystemFontOfSize:50];
|
||||
centerView.attributedText = attriStr;
|
||||
[self addSubview:centerView];
|
||||
|
||||
[centerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(leftView.mas_right);
|
||||
make.top.mas_equalTo(leftView.mas_top);
|
||||
make.width.mas_equalTo(leftView.mas_width);
|
||||
make.height.mas_equalTo(leftView.mas_height);
|
||||
}];
|
||||
|
||||
UILabel *rightView = [[UILabel alloc] init];
|
||||
rightView.backgroundColor = kColorRGBA(0, 0, 0, 0.4);
|
||||
rightView.numberOfLines = 0;
|
||||
rightView.text = TFLocalizedString(@"下\n一\n页");
|
||||
rightView.textColor = kWhiteColor;
|
||||
rightView.textAlignment = NSTextAlignmentCenter;
|
||||
rightView.font = [UIFont boldSystemFontOfSize:50];
|
||||
[self addSubview:rightView];
|
||||
|
||||
[rightView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.mas_equalTo(centerView.mas_right);
|
||||
make.top.mas_equalTo(leftView.mas_top);
|
||||
make.width.mas_equalTo(leftView.mas_width);
|
||||
make.height.mas_equalTo(leftView.mas_height);
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)hidden
|
||||
{
|
||||
[self removeAllSubviews];
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// CXCustomTextView.h
|
||||
// CXTextView_Example
|
||||
//
|
||||
// Created by caixiang on 2019/5/6.
|
||||
// Copyright © 2019年 616704162@qq.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CXCustomTextView : UITextView
|
||||
/**
|
||||
占位文字
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *placeholder;
|
||||
/**
|
||||
占位文字的颜色
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *placeholderColor;
|
||||
/**
|
||||
占位符的文字位置
|
||||
*/
|
||||
@property(nonatomic,assign)CGPoint placePoint;
|
||||
/**
|
||||
最大字数
|
||||
*/
|
||||
@property (nonatomic, assign)NSInteger maxLength;
|
||||
/**
|
||||
文字改变的回调(只是赋值的回调,编辑的改变不回调 有KVO监听)
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textDidChangeHandlerBlock)(CXCustomTextView *textView);
|
||||
/**
|
||||
触发到最大字数的回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textLengthDidMaxHandlerBlock)(CXCustomTextView *textView);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// CXCustomTextView.m
|
||||
// CXTextView_Example
|
||||
//
|
||||
// Created by caixiang on 2019/5/6.
|
||||
// Copyright © 2019年 616704162@qq.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CXCustomTextView.h"
|
||||
#import "TFKeyboardManager.h"
|
||||
|
||||
@interface CXCustomTextView()
|
||||
#pragma mark - Data Perproty
|
||||
@property (nonatomic, assign) BOOL isNameTextFieldEnbable;
|
||||
@end
|
||||
|
||||
@implementation CXCustomTextView
|
||||
|
||||
#pragma mark - life Cycle
|
||||
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
[self initilize];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initilize];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[self removeObserver:self forKeyPath:@"text"];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect{
|
||||
if (self.hasText) return;
|
||||
// 文字属性
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
|
||||
attrs[NSFontAttributeName] = self.font;
|
||||
attrs[NSForegroundColorAttributeName] = self.placeholderColor?self.placeholderColor:[UIColor grayColor];
|
||||
// 画文字
|
||||
CGFloat x = self.placePoint.x;
|
||||
CGFloat w = rect.size.width - 2 * x;
|
||||
CGFloat y = self.placePoint.y;
|
||||
CGFloat h = rect.size.height - 2 * y;
|
||||
CGRect placeholderRect = CGRectMake(x, y, w, h);
|
||||
[self.placeholder drawInRect:placeholderRect withAttributes:attrs];
|
||||
}
|
||||
|
||||
#pragma mark - NSNotification
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
|
||||
//返回删除键
|
||||
if ([text isEqualToString:@"\n"]) {
|
||||
//切换下一个
|
||||
return NO;
|
||||
}
|
||||
if ([text isEqualToString:@""]) {
|
||||
return YES;
|
||||
}else{
|
||||
return _isNameTextFieldEnbable;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
/**
|
||||
* 监听文字改变
|
||||
*/
|
||||
- (void)textDidChange:(NSNotification *)notification {
|
||||
self.isNameTextFieldEnbable = YES;
|
||||
UITextInputMode *mode = (UITextInputMode *)[UITextInputMode activeInputModes][0];
|
||||
NSString *lang = mode.primaryLanguage;
|
||||
//汉字
|
||||
if ([lang isEqualToString:@"zh-Hans"]) {
|
||||
UITextRange *selectedRange = [self markedTextRange];
|
||||
UITextPosition *position = [self positionFromPosition:selectedRange.start offset:0];
|
||||
if (!position) {
|
||||
if (self.text.length > self.maxLength) {
|
||||
self.text = [self.text substringToIndex:self.maxLength];
|
||||
self.isNameTextFieldEnbable = NO;
|
||||
!self.textLengthDidMaxHandlerBlock?:self.textLengthDidMaxHandlerBlock(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
//非汉字状态
|
||||
else{
|
||||
if (self.text.length > self.maxLength) {
|
||||
self.text = [self.text substringToIndex:self.maxLength];
|
||||
self.isNameTextFieldEnbable = NO;
|
||||
!self.textLengthDidMaxHandlerBlock?:self.textLengthDidMaxHandlerBlock(self);
|
||||
}
|
||||
}
|
||||
// 重绘(重新调用)
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
-(void)initilize{
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
|
||||
self.returnKeyType = UIReturnKeySend;
|
||||
self.placePoint = CGPointMake(5, 0);
|
||||
[self addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
|
||||
|
||||
UIToolbar *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)];
|
||||
|
||||
[toolBar setItems:@[flexibleSpaceItem, doneItem]];
|
||||
|
||||
self.inputAccessoryView = toolBar;
|
||||
}
|
||||
|
||||
- (void)hideKeyboard
|
||||
{
|
||||
[TFKeyboardManager hideKeyboard];
|
||||
}
|
||||
|
||||
#pragma mark -KVO
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
|
||||
if (object == self && [keyPath isEqualToString:@"text"]) {
|
||||
!self.textDidChangeHandlerBlock?:self.textDidChangeHandlerBlock(self);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - getter & setter
|
||||
- (void)setPlaceholder:(NSString *)placeholder {
|
||||
_placeholder = [placeholder copy];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderColor:(UIColor *)placeholderColor {
|
||||
_placeholderColor = placeholderColor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text {
|
||||
[super setText:text];
|
||||
//为了让kvo 监听的textviewb内容变化 能撑起contentSize,默认只是对self.text赋值self.scrollEnabled = NO;
|
||||
self.scrollEnabled = YES;
|
||||
//如果输入的文字超出最大行数,清零的时候需要将contentSizeOffset置空,可以让绘制的placeholde位置能正确显示
|
||||
if (text.length == 0) {
|
||||
[self setContentOffset:CGPointZero];
|
||||
}
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font {
|
||||
[super setFont:font];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
-(void)setPlacePoint:(CGPoint)placePoint{
|
||||
_placePoint = placePoint;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// CXTextView.h
|
||||
// CXTextView_Example
|
||||
//
|
||||
// Created by caixiang on 2019/5/6.
|
||||
// Copyright © 2019年 616704162@qq.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class CXCustomTextView;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CXTextView : UIView
|
||||
|
||||
/**
|
||||
真实的textView
|
||||
*/
|
||||
@property (nonatomic, strong) CXCustomTextView *textView;
|
||||
/**
|
||||
竖直方向上下间距 默认为8
|
||||
*/
|
||||
@property (nonatomic, assign)CGFloat v_margin;
|
||||
/**
|
||||
水平方向上下间距 默认为0
|
||||
*/
|
||||
@property(nonatomic,assign)CGFloat h_margin;
|
||||
/**
|
||||
初始需要展示的行数 默认为1
|
||||
*/
|
||||
@property(nonatomic,assign)NSInteger initiLine;
|
||||
/**
|
||||
最大行数 默认为无穷大
|
||||
*/
|
||||
@property(nonatomic,assign)NSInteger maxLine;
|
||||
/**
|
||||
占位文字
|
||||
*/
|
||||
@property(nonatomic,strong)NSString *placeholder;
|
||||
/**
|
||||
占位文字的颜色
|
||||
*/
|
||||
@property (nonatomic, strong) UIColor *placeholderColor;
|
||||
/**
|
||||
最大字数
|
||||
*/
|
||||
@property (nonatomic, assign)NSInteger maxLength;
|
||||
/**
|
||||
默认为17
|
||||
*/
|
||||
@property(nonatomic,strong)UIFont *font;
|
||||
/**
|
||||
置占位符的位置,竖直方向设置v_margin即可 CGPointMake(5, 0);//占位文字的起始位置
|
||||
*/
|
||||
@property(nonatomic,assign)CGPoint placePoint;
|
||||
/**
|
||||
文字
|
||||
*/
|
||||
@property (copy, nonatomic) NSString *text;
|
||||
/**
|
||||
文字颜色
|
||||
*/
|
||||
@property (strong, nonatomic) UIColor *textColor;
|
||||
/**
|
||||
高度变化回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textHeightChangeBlock)(CGFloat height);
|
||||
/**
|
||||
开始编辑回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textViewDidBeginEditingBlock)(UITextView *textView);
|
||||
/**
|
||||
结束编辑回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textViewDidEndEditingBlock)(UITextView *textView);
|
||||
/**
|
||||
最大值回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textLengthDidMaxHandlerBlock)(UITextView *textView);
|
||||
/**
|
||||
文字变动改变回调
|
||||
*/
|
||||
@property(nonatomic,copy)void (^textDidChangeHandlerBlock)(UITextView *textView);
|
||||
|
||||
/**
|
||||
发送按钮
|
||||
*/
|
||||
@property (nonatomic, copy) void(^returnHandlerBlock)(void);
|
||||
|
||||
- (void)textViewBecomeFirstResponder;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// CXTextView.m
|
||||
// CXTextView_Example
|
||||
//
|
||||
// Created by caixiang on 2019/5/6.
|
||||
// Copyright © 2019年 616704162@qq.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CXTextView.h"
|
||||
#import "CXCustomTextView.h"
|
||||
|
||||
@interface CXTextView()<UITextViewDelegate>
|
||||
@end
|
||||
|
||||
@implementation CXTextView
|
||||
|
||||
@synthesize text = _text;
|
||||
|
||||
#pragma mark - life Cycle
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
|
||||
self = [super initWithCoder: aDecoder];
|
||||
if (self) {
|
||||
[self initilize];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initilize];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
self.textView.width = self.frame.size.width - 2 *self.h_margin;
|
||||
}
|
||||
|
||||
#pragma mark - UITextViewDelegate
|
||||
-(void)textViewDidChange:(UITextView *)textView{
|
||||
!self.textDidChangeHandlerBlock?:self.textDidChangeHandlerBlock(textView);
|
||||
[self updateTextViewHeight];
|
||||
!self.textHeightChangeBlock?:self.textHeightChangeBlock(self.height);
|
||||
[textView scrollRangeToVisible:NSMakeRange(textView.selectedRange.location, 1)];
|
||||
}
|
||||
|
||||
- (void)textViewDidBeginEditing:(UITextView *)textView {
|
||||
!self.textViewDidBeginEditingBlock?:self.textViewDidBeginEditingBlock(textView);
|
||||
}
|
||||
|
||||
- (void)textViewDidEndEditing:(UITextView *)textView {
|
||||
!self.textViewDidEndEditingBlock?:self.textViewDidEndEditingBlock(textView);
|
||||
}
|
||||
|
||||
- (BOOL)textView:(YYTextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
if ([text isEqualToString:@"\n"]){
|
||||
NSString *sendMessage = [textView.text stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
if (sendMessage.length > 0) {
|
||||
if (self.returnHandlerBlock) {
|
||||
self.returnHandlerBlock();
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)updateTextViewHeight {
|
||||
CGFloat contentSizeH = ceilf(self.textView.contentSize.height);
|
||||
//内容高度
|
||||
if (self.textView.text.length == 0) {
|
||||
contentSizeH = ceilf(self.font.lineHeight * self.initiLine);
|
||||
}
|
||||
//最大高度
|
||||
CGFloat maxHeight = ceilf(self.font.lineHeight * self.maxLine);
|
||||
//初始高度
|
||||
CGFloat initiTextViewHeight = ceilf(self.initiLine *self.font.lineHeight);
|
||||
if (contentSizeH <= maxHeight) {
|
||||
if (contentSizeH <= initiTextViewHeight) {
|
||||
self.textView.height = ceilf(initiTextViewHeight);
|
||||
}else{
|
||||
self.textView.height = ceilf(contentSizeH);
|
||||
}
|
||||
}else{
|
||||
self.textView.height = ceilf(maxHeight);
|
||||
}
|
||||
self.height = self.textView.height + 2 * self.v_margin;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - private
|
||||
//初始化
|
||||
- (void)initilize{
|
||||
self.h_margin = 0;
|
||||
self.v_margin = 8;
|
||||
self.initiLine = 1;
|
||||
self.maxLine = CGFLOAT_MAX;
|
||||
self.maxLength = CGFLOAT_MAX;
|
||||
self.font = [UIFont systemFontOfSize:16];
|
||||
[self addSubview:self.textView];
|
||||
}
|
||||
|
||||
- (void)updateTextViewFrame{
|
||||
self.textView.frame = CGRectMake(self.h_margin, self.v_margin, self.frame.size.width - 2 *self.h_margin, ceilf(self.initiLine *self.font.lineHeight));
|
||||
self.height = self.v_margin *2 + ceilf(self.initiLine *self.font.lineHeight);
|
||||
}
|
||||
|
||||
- (void)textViewBecomeFirstResponder
|
||||
{
|
||||
[self.textView becomeFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - getter & setter
|
||||
- (void)setMaxLine:(NSInteger)maxLine{
|
||||
_maxLine = maxLine;
|
||||
}
|
||||
|
||||
- (void)setH_margin:(CGFloat)h_margin{
|
||||
_h_margin = h_margin;
|
||||
[self updateTextViewFrame];
|
||||
}
|
||||
|
||||
- (void)setV_margin:(CGFloat)v_margin{
|
||||
_v_margin = v_margin;
|
||||
[self updateTextViewFrame];
|
||||
}
|
||||
|
||||
- (void)setInitiLine:(NSInteger)initiLine{
|
||||
_initiLine = initiLine;
|
||||
[self updateTextViewFrame];
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font{
|
||||
_font = font;
|
||||
self.textView.font = font;
|
||||
[self updateTextViewFrame];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderColor:(UIColor *)placeholderColor {
|
||||
_placeholderColor = placeholderColor;
|
||||
self.textView.placeholderColor = placeholderColor;
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)textColor {
|
||||
_textColor = textColor;
|
||||
self.textView.textColor = textColor;
|
||||
}
|
||||
|
||||
- (void)setPlaceholder:(NSString *)placeholder{
|
||||
_placeholder = placeholder;
|
||||
self.textView.placeholder = placeholder;
|
||||
}
|
||||
|
||||
- (void)setPlacePoint:(CGPoint)placePoint{
|
||||
_placePoint = placePoint;
|
||||
self.textView.placePoint = placePoint;
|
||||
}
|
||||
|
||||
-(void)setMaxLength:(NSInteger)maxLength {
|
||||
_maxLength = maxLength;
|
||||
self.textView.maxLength = maxLength;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text {
|
||||
_text = [text copy];
|
||||
self.textView.text = text;
|
||||
}
|
||||
|
||||
- (NSString *)text {
|
||||
return self.textView.text;
|
||||
}
|
||||
|
||||
- (CXCustomTextView *)textView{
|
||||
if (!_textView) {
|
||||
_textView = [[CXCustomTextView alloc] initWithFrame:CGRectMake(self.h_margin, self.v_margin, self.frame.size.width - 2 *self.h_margin, self.initiLine *self.font.lineHeight)];
|
||||
_textView.textContainerInset = UIEdgeInsetsZero;
|
||||
_textView.delegate = self;
|
||||
_textView.backgroundColor = [UIColor whiteColor];
|
||||
_textView.textColor = [UIColor blackColor];
|
||||
_textView.placeholder = TFLocalizedString(@"请输入");
|
||||
__weak __typeof(self)weakSelf = self;
|
||||
_textView.textLengthDidMaxHandlerBlock = ^(CXCustomTextView *textView) {
|
||||
__strong __typeof(weakSelf)sSelf = weakSelf;
|
||||
!sSelf.textLengthDidMaxHandlerBlock?:sSelf.textLengthDidMaxHandlerBlock(textView);
|
||||
};
|
||||
_textView.textDidChangeHandlerBlock = ^(CXCustomTextView *textView) {
|
||||
__strong __typeof(weakSelf)sSelf = weakSelf;
|
||||
!sSelf.textDidChangeHandlerBlock?:sSelf.textDidChangeHandlerBlock(textView);
|
||||
[sSelf updateTextViewHeight];
|
||||
if (textView.text.length > 0) {
|
||||
!sSelf.textHeightChangeBlock?:sSelf.textHeightChangeBlock(sSelf.height);
|
||||
} else {
|
||||
!sSelf.textHeightChangeBlock?:sSelf.textHeightChangeBlock(self.v_margin *2 + ceilf(self.initiLine *self.font.lineHeight));
|
||||
}
|
||||
};
|
||||
}
|
||||
return _textView;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// CXTextViewHelper.h
|
||||
// CXTextView
|
||||
//
|
||||
// Created by caixiang on 2019/5/7.
|
||||
// Copyright © 2019年 616704162@qq.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifndef CXTextViewHelper_h
|
||||
#define CXTextViewHelper_h
|
||||
//#import "UIView+CXPostion.h"
|
||||
#import "CXTextView.h"
|
||||
#import "CXCustomTextView.h"
|
||||
|
||||
UIKIT_STATIC_INLINE UIEdgeInsets cx_viewSafeArea(UIView *view) {
|
||||
|
||||
#ifdef __IPHONE_11_0
|
||||
if (@available(iOS 11.0, *)) {
|
||||
return view.safeAreaInsets;
|
||||
}
|
||||
#endif
|
||||
return UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
UIKIT_STATIC_INLINE UIColor *CXRGBA(NSInteger r,NSInteger g,NSInteger b,CGFloat a) {
|
||||
return [UIColor colorWithRed:(float)r/255.0f green:(float)g/255.0f blue:(float)b/255.0f alpha:a];
|
||||
}
|
||||
|
||||
UIKIT_STATIC_INLINE UIColor *CXRGB(NSInteger r,NSInteger g,NSInteger b) {
|
||||
return CXRGBA(r,g,b,1.0f);
|
||||
}
|
||||
|
||||
UIKIT_STATIC_INLINE UIColor *CXUIColorFromRGBA(uint32_t rgbValue, CGFloat a) {
|
||||
return [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0f
|
||||
green:((float)((rgbValue & 0xFF00) >> 8))/255.0f
|
||||
blue:((float)(rgbValue & 0xFF))/255.0f
|
||||
alpha:a];
|
||||
}
|
||||
|
||||
UIKIT_STATIC_INLINE UIColor *CXUIColorFromRGB(uint32_t rgbValue) {
|
||||
return CXUIColorFromRGBA(rgbValue,1.0f);
|
||||
}
|
||||
|
||||
#endif /* CXTextViewHelper_h */
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// KLSwitch.h
|
||||
// KLSwitch
|
||||
//
|
||||
// Created by Kieran Lafferty on 2013-06-15.
|
||||
// Copyright (c) 2013 Kieran Lafferty. All rights reserved.
|
||||
//
|
||||
// https://github.com/KieranLafferty/KLSwitch
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#define LOCK_IMAGE_SUBVIEW 100
|
||||
|
||||
typedef void(^changeHandler)(BOOL isOn);
|
||||
|
||||
@interface KLSwitch : UIControl <NSCoding>
|
||||
//UISwitch interface
|
||||
@property(nonatomic, strong) UIImage *onImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; //Currently this does nothing
|
||||
@property(nonatomic, strong) UIImage *offImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; //Currently this does nothing
|
||||
|
||||
@property(nonatomic, strong) UIColor *onTintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
|
||||
@property(nonatomic, strong) UIColor *tintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;
|
||||
@property(nonatomic, strong) UIColor *thumbTintColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR;
|
||||
|
||||
|
||||
///Additional color options provided by KLSwitch only
|
||||
@property(nonatomic, strong) UIColor *contrastColor;
|
||||
@property(nonatomic, strong) UIColor *thumbBorderColor;
|
||||
|
||||
|
||||
@property(nonatomic, getter=isOn) BOOL on;
|
||||
@property(nonatomic, getter=isLocked) BOOL locked;
|
||||
|
||||
//Custom completion block initiated by value change (on/off)
|
||||
@property(nonatomic, copy) changeHandler didChangeHandler;
|
||||
|
||||
//Percent (0.0 - 1.0) of the control to travel while panning before a switch toggle is activated
|
||||
@property(nonatomic, assign) CGFloat panActivationThreshold;
|
||||
|
||||
//Set to true if you want to maintain 51x31 proportions, false if you want to set the frame to anything
|
||||
@property(nonatomic, assign) BOOL shouldConstrainFrame;
|
||||
|
||||
//Initializers
|
||||
- (id)initWithFrame:(CGRect)frame;
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
didChangeHandler:(changeHandler) didChangeHandler;
|
||||
|
||||
//Events
|
||||
- (void)setOn:(BOOL)on animated:(BOOL)animated;
|
||||
- (void)setLocked:(BOOL)locked;
|
||||
|
||||
- (void)setDefaultOnState:(BOOL)on;
|
||||
|
||||
@end
|
||||
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
//
|
||||
// KLSwitch.m
|
||||
// KLSwitch
|
||||
//
|
||||
// Created by Kieran Lafferty on 2013-06-15.
|
||||
// Copyright (c) 2013 Kieran Lafferty. All rights reserved.
|
||||
//
|
||||
// https://github.com/KieranLafferty/KLSwitch
|
||||
|
||||
#import "KLSwitch.h"
|
||||
|
||||
#define kConstrainsFrameToProportions YES
|
||||
#define kHeightWidthRatio 1.6451612903 //Magic number as a result of dividing the height by the width on the default UISwitch size (51/31)
|
||||
|
||||
//NSCoding Keys
|
||||
#define kCodingOnKey @"on"
|
||||
#define kCodingLockedKey @"off"
|
||||
#define kCodingOnTintColorKey @"onColor"
|
||||
#define kCodingOnColorKey @"onTintColor" //Not implemented
|
||||
#define kCodingTintColorKey @"tintColor"
|
||||
#define kCodingThumbTintColorKey @"thumbTintColor"
|
||||
#define kCodingOnImageKey @"onImage"
|
||||
#define kCodingOffImageKey @"offImage"
|
||||
#define kCodingConstrainFrameKey @"constrainFrame"
|
||||
|
||||
//Appearance Defaults - Colors
|
||||
//Track Colors
|
||||
#define kDefaultTrackOnColor [UIColor colorWithRed:83/255.0 green: 214/255.0 blue: 105/255.0 alpha: 1]
|
||||
#define kDefaultTrackOffColor [UIColor colorWithWhite: 0.9f alpha:1.0f]
|
||||
#define kDefaultTrackContrastColor [UIColor whiteColor]
|
||||
|
||||
//Thumb Colors
|
||||
#define kDefaultThumbTintColor [UIColor whiteColor]
|
||||
#define kDefaultThumbBorderColor [UIColor colorWithWhite: 0.9f alpha:1.0f]
|
||||
|
||||
//Appearance - Layout
|
||||
|
||||
//Size of knob with respect to the control - Must be a multiple of 2
|
||||
#define kThumbOffset 1
|
||||
#define kThumbTrackingGrowthRatio 1.2f //Amount to grow the thumb on press down
|
||||
|
||||
#define kDefaultPanActivationThreshold 0.7 //Number between 0.0 - 1.0 describing how far user must drag before initiating the switch
|
||||
|
||||
//Appearance - Animations
|
||||
#define kDefaultAnimationSlideLength 0.25f //Length of time to slide the thumb from left/right to right/left
|
||||
#define kDefaultAnimationScaleLength 0.15f //Length of time for the thumb to grow on press down
|
||||
#define kDefaultAnimationContrastResizeLength 0.25f //Length of time for the thumb to grow on press down
|
||||
|
||||
#define kSwitchTrackContrastViewShrinkFactor 0.0001f //Must be very low but not 0 or else causes iOS 5 issues
|
||||
|
||||
typedef enum {
|
||||
KLSwitchThumbJustifyLeft,
|
||||
KLSwitchThumbJustifyRight
|
||||
} KLSwitchThumbJustify;
|
||||
|
||||
@interface KLSwitchThumb : UIView
|
||||
@property (nonatomic, assign) BOOL isTracking;
|
||||
-(void) growThumbWithJustification:(KLSwitchThumbJustify) justification;
|
||||
-(void) shrinkThumbWithJustification:(KLSwitchThumbJustify) justification;
|
||||
@end
|
||||
|
||||
@interface KLSwitchTrack : UIView
|
||||
@property(nonatomic, getter=isOn) BOOL on;
|
||||
@property (nonatomic, strong) UIColor* contrastColor;
|
||||
@property (nonatomic, strong) UIColor* onTintColor;
|
||||
@property (nonatomic, strong) UIColor* tintColor;
|
||||
-(id) initWithFrame:(CGRect)frame
|
||||
onColor:(UIColor*) onColor
|
||||
offColor:(UIColor*) offColor
|
||||
contrastColor:(UIColor*) contrastColor;
|
||||
-(void) growContrastView;
|
||||
-(void) shrinkContrastView;
|
||||
-(void) setOn:(BOOL) on
|
||||
animated:(BOOL) animated;
|
||||
@end
|
||||
|
||||
|
||||
@interface KLSwitch () <UIGestureRecognizerDelegate>
|
||||
@property (nonatomic, strong) KLSwitchTrack* track;
|
||||
@property (nonatomic, strong) KLSwitchThumb* thumb;
|
||||
|
||||
//Gesture Recognizers
|
||||
@property (nonatomic, strong) UIPanGestureRecognizer* panGesture;
|
||||
@property (nonatomic, strong) UITapGestureRecognizer* tapGesture;
|
||||
-(void) configureSwitch;
|
||||
-(void) initializeDefaults;
|
||||
-(void) toggleState;
|
||||
-(void) setThumbOn:(BOOL) on
|
||||
animated:(BOOL) animated;
|
||||
|
||||
@end
|
||||
|
||||
@implementation KLSwitch
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[super encodeWithCoder: aCoder];
|
||||
|
||||
[aCoder encodeBool: _on
|
||||
forKey: kCodingOnKey];
|
||||
|
||||
[aCoder encodeObject: _onTintColor
|
||||
forKey: kCodingOnTintColorKey];
|
||||
|
||||
[aCoder encodeObject: _tintColor
|
||||
forKey: kCodingTintColorKey];
|
||||
|
||||
[aCoder encodeObject: _thumbTintColor
|
||||
forKey: kCodingThumbTintColorKey];
|
||||
|
||||
[aCoder encodeObject: _onImage
|
||||
forKey: kCodingOnImageKey];
|
||||
|
||||
[aCoder encodeObject: _offImage
|
||||
forKey: kCodingOffImageKey];
|
||||
|
||||
[aCoder encodeBool: _shouldConstrainFrame
|
||||
forKey: kCodingConstrainFrameKey];
|
||||
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
[self initializeDefaults];
|
||||
if (self = [super initWithCoder: aDecoder]) {
|
||||
|
||||
_on = [aDecoder decodeBoolForKey:kCodingOnKey];
|
||||
_locked = [aDecoder decodeBoolForKey:kCodingLockedKey];
|
||||
_onTintColor = [aDecoder decodeObjectForKey: kCodingOnTintColorKey];
|
||||
_tintColor = [aDecoder decodeObjectForKey: kCodingTintColorKey];
|
||||
_thumbTintColor = [aDecoder decodeObjectForKey: kCodingThumbTintColorKey];
|
||||
_onImage = [aDecoder decodeObjectForKey: kCodingOnImageKey];
|
||||
_offImage = [aDecoder decodeObjectForKey: kCodingOffImageKey];
|
||||
_onTintColor = [aDecoder decodeObjectForKey: kCodingOnTintColorKey];
|
||||
_shouldConstrainFrame = [aDecoder decodeBoolForKey: kCodingConstrainFrameKey];
|
||||
|
||||
[self configureSwitch];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self configureSwitch];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
didChangeHandler:(changeHandler) didChangeHandler {
|
||||
if (self = [self initWithFrame: frame]) {
|
||||
_didChangeHandler = didChangeHandler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
-(void) setFrame:(CGRect)frame {
|
||||
if (self.shouldConstrainFrame) {
|
||||
[super setFrame: CGRectMake(frame.origin.x, frame.origin.y, frame.size.height*kHeightWidthRatio, frame.size.height)];
|
||||
}
|
||||
else [super setFrame: frame];
|
||||
}
|
||||
|
||||
#pragma mark - Defaults and layout/appearance
|
||||
|
||||
-(void) initializeDefaults {
|
||||
_onTintColor = kDefaultTrackOnColor;
|
||||
_tintColor = kDefaultTrackOffColor;
|
||||
_thumbTintColor = kDefaultThumbTintColor;
|
||||
_thumbBorderColor = kDefaultThumbBorderColor;
|
||||
_contrastColor = kDefaultThumbTintColor;
|
||||
_panActivationThreshold = kDefaultPanActivationThreshold;
|
||||
_shouldConstrainFrame = kConstrainsFrameToProportions;
|
||||
}
|
||||
-(void) configureSwitch {
|
||||
[self initializeDefaults];
|
||||
|
||||
//Configure visual properties of self
|
||||
[self setBackgroundColor: [UIColor clearColor]];
|
||||
|
||||
|
||||
// tap gesture for toggling the switch
|
||||
self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(didTap:)];
|
||||
[self.tapGesture setDelegate:self];
|
||||
[self addGestureRecognizer:self.tapGesture];
|
||||
|
||||
|
||||
// pan gesture for moving the switch knob manually
|
||||
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(didDrag:)];
|
||||
[self.panGesture setDelegate:self];
|
||||
[self addGestureRecognizer:self.panGesture];
|
||||
|
||||
/*
|
||||
Subview layering as follows :
|
||||
|
||||
TOP
|
||||
thumb
|
||||
track
|
||||
BOTTOM
|
||||
*/
|
||||
// Initialization code
|
||||
if (!_track) {
|
||||
_track = [[KLSwitchTrack alloc] initWithFrame: self.bounds
|
||||
onColor: self.onTintColor
|
||||
offColor: self.tintColor
|
||||
contrastColor: self.contrastColor];
|
||||
[_track setOn: self.isOn
|
||||
animated: NO];
|
||||
[self addSubview: self.track];
|
||||
}
|
||||
if (!_thumb) {
|
||||
_thumb = [[KLSwitchThumb alloc] initWithFrame:CGRectMake(kThumbOffset, kThumbOffset, self.bounds.size.height - 2 * kThumbOffset, self.bounds.size.height - 2 * kThumbOffset)];
|
||||
[self addSubview: _thumb];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) setOnTintColor:(UIColor *)onTintColor {
|
||||
_onTintColor = onTintColor;
|
||||
[self.track setOnTintColor: _onTintColor];
|
||||
}
|
||||
-(void) setTintColor:(UIColor *)tintColor {
|
||||
_tintColor = tintColor;
|
||||
[self.track setTintColor: _tintColor];
|
||||
}
|
||||
-(void) setContrastColor:(UIColor *)contrastColor {
|
||||
_contrastColor = contrastColor;
|
||||
[self.track setContrastColor: _contrastColor];
|
||||
}
|
||||
-(void) setThumbBorderColor:(UIColor *)thumbBorderColor {
|
||||
_thumbBorderColor = thumbBorderColor;
|
||||
[self.thumb.layer setBorderColor: [_thumbBorderColor CGColor]];
|
||||
}
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
[super drawRect:rect];
|
||||
// Drawing code
|
||||
//[self.trackingKnob setTintColor: self.thumbTintColor];
|
||||
[_thumb setBackgroundColor: [UIColor whiteColor]];
|
||||
|
||||
//Make the knob a circle and add a shadow
|
||||
CGFloat roundedCornerRadius = _thumb.frame.size.height/2.0f;
|
||||
[_thumb.layer setBorderWidth: 0.5];
|
||||
[_thumb.layer setBorderColor: [self.thumbBorderColor CGColor]];
|
||||
[_thumb.layer setCornerRadius: roundedCornerRadius];
|
||||
[_thumb.layer setShadowColor: [[UIColor grayColor] CGColor]];
|
||||
[_thumb.layer setShadowOffset: CGSizeMake(0, 3)];
|
||||
[_thumb.layer setShadowOpacity: 0.40f];
|
||||
[_thumb.layer setShadowRadius: 0.8];
|
||||
}
|
||||
|
||||
#pragma mark - UIGestureRecognizer implementations
|
||||
-(void) didTap:(UITapGestureRecognizer*) gesture {
|
||||
if (gesture.state == UIGestureRecognizerStateEnded) {
|
||||
[self toggleState];
|
||||
}
|
||||
}
|
||||
-(void) didDrag:(UIPanGestureRecognizer*) gesture {
|
||||
if (gesture.state == UIGestureRecognizerStateBegan) {
|
||||
//Grow the thumb horizontally towards center by defined ratio
|
||||
[self setThumbIsTracking: YES
|
||||
animated: YES];
|
||||
}
|
||||
else if (gesture.state == UIGestureRecognizerStateChanged) {
|
||||
//If touch crosses the threshold then toggle the state
|
||||
CGPoint locationInThumb = [gesture locationInView: self.thumb];
|
||||
|
||||
//Toggle the switch if the user pans left or right past the switch thumb bounds
|
||||
if ((self.isOn && locationInThumb.x <= 0)
|
||||
|| (!self.isOn && locationInThumb.x >= self.thumb.bounds.size.width)) {
|
||||
[self toggleState];
|
||||
}
|
||||
|
||||
CGPoint locationOfTouch = [gesture locationInView:self];
|
||||
if (CGRectContainsPoint(self.bounds, locationOfTouch))
|
||||
[self sendActionsForControlEvents:UIControlEventTouchDragInside];
|
||||
else
|
||||
[self sendActionsForControlEvents:UIControlEventTouchDragOutside];
|
||||
}
|
||||
else if (gesture.state == UIGestureRecognizerStateEnded) {
|
||||
[self setThumbIsTracking: NO
|
||||
animated: YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Event Handlers
|
||||
|
||||
-(void) toggleState {
|
||||
//Alternate between on/off
|
||||
[self setOn: self.isOn ? NO : YES
|
||||
animated: YES];
|
||||
}
|
||||
|
||||
- (void)setDefaultOnState:(BOOL)on
|
||||
{
|
||||
if (_on == on) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Move the thumb to the new position
|
||||
[self setThumbOn: on
|
||||
animated: NO];
|
||||
|
||||
//Animate the contrast view of the track
|
||||
[self.track setOn: on
|
||||
animated: NO];
|
||||
|
||||
_on = on;
|
||||
|
||||
[self sendActionsForControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
|
||||
- (void)setOn:(BOOL)on
|
||||
animated:(BOOL)animated {
|
||||
//Cancel notification to parent if attempting to set to current state
|
||||
if (_on == on) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Move the thumb to the new position
|
||||
[self setThumbOn: on
|
||||
animated: animated];
|
||||
|
||||
//Animate the contrast view of the track
|
||||
[self.track setOn: on
|
||||
animated: animated];
|
||||
|
||||
_on = on;
|
||||
|
||||
//Trigger the completion block if exists
|
||||
if (self.didChangeHandler) {
|
||||
self.didChangeHandler(_on);
|
||||
}
|
||||
[self sendActionsForControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
|
||||
- (void) setOn:(BOOL)on {
|
||||
[self setOn: on animated: NO];
|
||||
}
|
||||
|
||||
- (void) setLocked:(BOOL)locked {
|
||||
//Cancel notification to parent if attempting to set to current state
|
||||
if (_locked == locked) {
|
||||
return;
|
||||
}
|
||||
_locked = locked;
|
||||
|
||||
UIImageView *lockImageView = (UIImageView *)[_track viewWithTag:LOCK_IMAGE_SUBVIEW];
|
||||
|
||||
if (!locked && (lockImageView != nil)) {
|
||||
|
||||
[lockImageView removeFromSuperview];
|
||||
lockImageView = nil;
|
||||
|
||||
} else if (locked && (lockImageView == nil)) {
|
||||
|
||||
UIImage *lockImage = [UIImage imageNamed:@"lock-icon.png"];
|
||||
|
||||
lockImageView = [[UIImageView alloc] initWithImage:lockImage];
|
||||
|
||||
lockImageView.frame = CGRectMake(7, 8, lockImage.size.width, lockImage.size.height);
|
||||
lockImageView.tag = LOCK_IMAGE_SUBVIEW;
|
||||
|
||||
[_track addSubview:lockImageView];
|
||||
[_track bringSubviewToFront:lockImageView];
|
||||
}
|
||||
}
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
[self sendActionsForControlEvents:UIControlEventTouchDown];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
[self sendActionsForControlEvents:UIControlEventTouchUpOutside];
|
||||
}
|
||||
|
||||
-(void) setThumbIsTracking:(BOOL)isTracking {
|
||||
if (isTracking) {
|
||||
//Grow
|
||||
[self.thumb growThumbWithJustification: self.isOn ? KLSwitchThumbJustifyRight : KLSwitchThumbJustifyLeft];
|
||||
}
|
||||
else {
|
||||
//Shrink
|
||||
[self.thumb shrinkThumbWithJustification: self.isOn ? KLSwitchThumbJustifyRight : KLSwitchThumbJustifyLeft];
|
||||
}
|
||||
[self.thumb setIsTracking: isTracking];
|
||||
}
|
||||
-(void) setThumbIsTracking:(BOOL)isTracking
|
||||
animated:(BOOL) animated {
|
||||
__weak id weakSelf = self;
|
||||
[UIView animateWithDuration: kDefaultAnimationScaleLength
|
||||
delay: fabs(kDefaultAnimationSlideLength - kDefaultAnimationScaleLength)
|
||||
options: UIViewAnimationOptionCurveEaseOut
|
||||
animations: ^{
|
||||
[weakSelf setThumbIsTracking: isTracking];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
-(void) setThumbOn:(BOOL) on
|
||||
animated:(BOOL) animated {
|
||||
if (animated) {
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
[self setThumbOn:on animated:NO];
|
||||
}];
|
||||
}
|
||||
CGRect thumbFrame = self.thumb.frame;
|
||||
if (on) {
|
||||
thumbFrame.origin.x = self.bounds.size.width - (thumbFrame.size.width + kThumbOffset);
|
||||
}
|
||||
else {
|
||||
thumbFrame.origin.x = kThumbOffset;
|
||||
}
|
||||
[self.thumb setFrame: thumbFrame];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation KLSwitchThumb
|
||||
|
||||
-(void) growThumbWithJustification:(KLSwitchThumbJustify) justification {
|
||||
if (self.isTracking)
|
||||
return;
|
||||
|
||||
CGRect thumbFrame = self.frame;
|
||||
|
||||
CGFloat deltaWidth = self.frame.size.width * (kThumbTrackingGrowthRatio - 1);
|
||||
thumbFrame.size.width += deltaWidth;
|
||||
if (justification == KLSwitchThumbJustifyRight) {
|
||||
thumbFrame.origin.x -= deltaWidth;
|
||||
}
|
||||
[self setFrame: thumbFrame];
|
||||
}
|
||||
-(void) shrinkThumbWithJustification:(KLSwitchThumbJustify) justification {
|
||||
if (!self.isTracking)
|
||||
return;
|
||||
|
||||
CGRect thumbFrame = self.frame;
|
||||
|
||||
CGFloat deltaWidth = self.frame.size.width * (1 - 1 / (kThumbTrackingGrowthRatio));
|
||||
thumbFrame.size.width -= deltaWidth;
|
||||
if (justification == KLSwitchThumbJustifyRight) {
|
||||
thumbFrame.origin.x += deltaWidth;
|
||||
}
|
||||
[self setFrame: thumbFrame];
|
||||
|
||||
}
|
||||
@end
|
||||
|
||||
@interface KLSwitchTrack ()
|
||||
@property (nonatomic, strong) UIView* contrastView;
|
||||
@property (nonatomic, strong) UIView* onView;
|
||||
@end
|
||||
|
||||
@implementation KLSwitchTrack
|
||||
|
||||
-(id) initWithFrame:(CGRect)frame
|
||||
onColor:(UIColor*) onColor
|
||||
offColor:(UIColor*) offColor
|
||||
contrastColor:(UIColor*) contrastColor {
|
||||
if (self = [super initWithFrame: frame]) {
|
||||
_onTintColor = onColor;
|
||||
_tintColor = offColor;
|
||||
|
||||
CGFloat cornerRadius = frame.size.height/2.0f;
|
||||
[self.layer setCornerRadius: cornerRadius];
|
||||
[self setBackgroundColor: _tintColor];
|
||||
|
||||
CGRect contrastRect = frame;
|
||||
contrastRect.size.width = frame.size.width - 2*kThumbOffset;
|
||||
contrastRect.size.height = frame.size.height - 2*kThumbOffset;
|
||||
CGFloat contrastRadius = contrastRect.size.height/2.0f;
|
||||
|
||||
_contrastView = [[UIView alloc] initWithFrame:contrastRect];
|
||||
[_contrastView setBackgroundColor: contrastColor];
|
||||
[_contrastView setCenter: self.center];
|
||||
[_contrastView.layer setCornerRadius: contrastRadius];
|
||||
[self addSubview: _contrastView];
|
||||
|
||||
_onView = [[UIView alloc] initWithFrame:frame];
|
||||
[_onView setBackgroundColor: _onTintColor];
|
||||
[_onView setCenter: self.center];
|
||||
[_onView.layer setCornerRadius: cornerRadius];
|
||||
[self addSubview: _onView];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) setOn:(BOOL)on {
|
||||
if (on) {
|
||||
[self.onView setAlpha: 1.0];
|
||||
[self shrinkContrastView];
|
||||
}
|
||||
else {
|
||||
[self.onView setAlpha: 0.0];
|
||||
[self growContrastView];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) setOn:(BOOL)on
|
||||
animated:(BOOL)animated {
|
||||
if (animated) {
|
||||
__weak id weakSelf = self;
|
||||
//First animate the color switch
|
||||
[UIView animateWithDuration: kDefaultAnimationContrastResizeLength
|
||||
delay: 0.0
|
||||
options: UIViewAnimationOptionCurveEaseOut
|
||||
animations:^{
|
||||
[weakSelf setOn: on
|
||||
animated: NO];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
else {
|
||||
[self setOn: on];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) setOnTintColor:(UIColor *)onTintColor {
|
||||
_onTintColor = onTintColor;
|
||||
[self.onView setBackgroundColor: _onTintColor];
|
||||
}
|
||||
|
||||
-(void) setTintColor:(UIColor *)tintColor {
|
||||
_tintColor = tintColor;
|
||||
[self setBackgroundColor: _tintColor];
|
||||
}
|
||||
|
||||
-(void) setContrastColor:(UIColor *)contrastColor {
|
||||
_contrastColor = contrastColor;
|
||||
[self.contrastView setBackgroundColor: _contrastColor];
|
||||
}
|
||||
|
||||
-(void) growContrastView {
|
||||
//Start out with contrast view small and centered
|
||||
[self.contrastView setTransform: CGAffineTransformMakeScale(kSwitchTrackContrastViewShrinkFactor, kSwitchTrackContrastViewShrinkFactor)];
|
||||
[self.contrastView setTransform: CGAffineTransformMakeScale(1, 1)];
|
||||
}
|
||||
|
||||
-(void) shrinkContrastView {
|
||||
//Start out with contrast view the size of the track
|
||||
[self.contrastView setTransform: CGAffineTransformMakeScale(1, 1)];
|
||||
[self.contrastView setTransform: CGAffineTransformMakeScale(kSwitchTrackContrastViewShrinkFactor, kSwitchTrackContrastViewShrinkFactor)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// WXYZ_PlayingInfoCenter.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/4/18.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol WXYZ_PlayingInfoCenterDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
// 播放
|
||||
- (void)playRemoteCommand;
|
||||
|
||||
// 暂停
|
||||
- (void)pauseRemoteCommand;
|
||||
|
||||
// 上一首
|
||||
- (void)lastRemoteCommand;
|
||||
|
||||
// 下一首
|
||||
- (void)nextRemoteCommand;
|
||||
|
||||
@end
|
||||
|
||||
@interface WXYZ_PlayingInfoCenter : NSObject
|
||||
|
||||
@property (nullable, nonatomic, weak) id <WXYZ_PlayingInfoCenterDelegate> delegate;
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (void)showPlayingInfoCenterWithProductionChapterModel:(TFProductionChapterModel *)productionChapterModel;
|
||||
|
||||
- (void)hiddenPlayingInfoCenter;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// WXYZ_PlayingInfoCenter.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/4/18.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_PlayingInfoCenter.h"
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface WXYZ_PlayingInfoCenter ()
|
||||
|
||||
@property (nonatomic, strong) MPRemoteCommand *playCommand;
|
||||
|
||||
@property (nonatomic, strong) MPRemoteCommand *pauseCommand;
|
||||
|
||||
@property (nonatomic, strong) MPRemoteCommand *lastCommand;
|
||||
|
||||
@property (nonatomic, strong) MPRemoteCommand *nextCommand;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_PlayingInfoCenter
|
||||
|
||||
implementation_singleton(WXYZ_PlayingInfoCenter)
|
||||
|
||||
- (void)showPlayingInfoCenterWithProductionChapterModel:(TFProductionChapterModel *)productionChapterModel
|
||||
{
|
||||
if (!productionChapterModel) {
|
||||
kCodeSync([[UIApplication sharedApplication] endReceivingRemoteControlEvents]);
|
||||
return;
|
||||
}
|
||||
|
||||
kCodeSync([[UIApplication sharedApplication] endReceivingRemoteControlEvents]);
|
||||
kCodeSync([[UIApplication sharedApplication] beginReceivingRemoteControlEvents]);
|
||||
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
[session setActive:YES error:nil];
|
||||
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
|
||||
|
||||
[self.playCommand setEnabled:YES];
|
||||
|
||||
[self.pauseCommand setEnabled:YES];
|
||||
|
||||
if (productionChapterModel.last_chapter > 0 && productionChapterModel.last_chapter) {
|
||||
[self.lastCommand setEnabled:YES];
|
||||
} else {
|
||||
[self.lastCommand setEnabled:NO];
|
||||
}
|
||||
|
||||
if (productionChapterModel.next_chapter > 0 && productionChapterModel.next_chapter) {
|
||||
[self.nextCommand setEnabled:YES];
|
||||
} else {
|
||||
[self.nextCommand setEnabled:NO];
|
||||
}
|
||||
|
||||
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:CGSizeMake(BOOK_WIDTH, BOOK_HEIGHT) requestHandler:^UIImage * _Nonnull(CGSize size) {
|
||||
return [[YYImageCache sharedCache] getImageForKey:productionChapterModel.cover];
|
||||
}];
|
||||
|
||||
NSMutableDictionary *info = [NSMutableDictionary dictionary];
|
||||
[info setObject:productionChapterModel.chapter_title?:@"" forKey:MPMediaItemPropertyTitle];
|
||||
[info setObject:productionChapterModel.name?:@"" forKey:MPMediaItemPropertyArtist];
|
||||
[info setObject:artwork forKey:MPMediaItemPropertyArtwork];
|
||||
//完成设置
|
||||
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:info];
|
||||
}
|
||||
|
||||
- (void)hiddenPlayingInfoCenter
|
||||
{
|
||||
// [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
|
||||
}
|
||||
|
||||
- (MPRemoteCommand *)playCommand
|
||||
{
|
||||
if (!_playCommand) {
|
||||
WS(weakSelf)
|
||||
MPRemoteCommand *playCommand = [[MPRemoteCommandCenter sharedCommandCenter] playCommand];
|
||||
[playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
|
||||
if ([weakSelf.delegate respondsToSelector:@selector(playRemoteCommand)]) {
|
||||
[weakSelf.delegate playRemoteCommand];
|
||||
}
|
||||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}];
|
||||
_playCommand = playCommand;
|
||||
}
|
||||
return _playCommand;
|
||||
}
|
||||
|
||||
- (MPRemoteCommand *)pauseCommand
|
||||
{
|
||||
if (!_pauseCommand) {
|
||||
WS(weakSelf)
|
||||
MPRemoteCommand *pauseCommand = [[MPRemoteCommandCenter sharedCommandCenter] pauseCommand];
|
||||
[pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
|
||||
if ([weakSelf.delegate respondsToSelector:@selector(pauseRemoteCommand)]) {
|
||||
[weakSelf.delegate pauseRemoteCommand];
|
||||
}
|
||||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}];
|
||||
_pauseCommand = pauseCommand;
|
||||
}
|
||||
return _pauseCommand;
|
||||
}
|
||||
|
||||
- (MPRemoteCommand *)lastCommand
|
||||
{
|
||||
if (!_lastCommand) {
|
||||
WS(weakSelf)
|
||||
MPRemoteCommand *lastCommand = [[MPRemoteCommandCenter sharedCommandCenter] previousTrackCommand];
|
||||
[lastCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
|
||||
if ([weakSelf.delegate respondsToSelector:@selector(lastRemoteCommand)]) {
|
||||
[weakSelf.delegate lastRemoteCommand];
|
||||
}
|
||||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}];
|
||||
_lastCommand = lastCommand;
|
||||
}
|
||||
return _lastCommand;
|
||||
}
|
||||
|
||||
- (MPRemoteCommand *)nextCommand
|
||||
{
|
||||
if (!_nextCommand) {
|
||||
WS(weakSelf)
|
||||
MPRemoteCommand *nextCommand = [[MPRemoteCommandCenter sharedCommandCenter] nextTrackCommand];
|
||||
[nextCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
|
||||
if ([weakSelf.delegate respondsToSelector:@selector(nextRemoteCommand)]) {
|
||||
[weakSelf.delegate nextRemoteCommand];
|
||||
}
|
||||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}];
|
||||
_nextCommand = nextCommand;
|
||||
}
|
||||
return _nextCommand;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// WXYZ_TouchAssistantView.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/20.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface WXYZ_TouchAssistantView : UIWindow
|
||||
|
||||
@property (nonatomic ,assign ,readonly) TFProductionType productionType;
|
||||
|
||||
interface_singleton
|
||||
|
||||
- (void)setPlayerProductionType:(TFProductionType)productionType;
|
||||
|
||||
/*
|
||||
* 显示小助手
|
||||
*/
|
||||
- (void)showAssistiveTouchView;
|
||||
|
||||
/*
|
||||
* 显示小助手
|
||||
*/
|
||||
- (void)showAssistiveTouchViewWithImageCover:(NSString *)imageCover productionType:(TFProductionType)productionType;
|
||||
|
||||
/*
|
||||
* 隐藏小助手
|
||||
*/
|
||||
- (void)hiddenAssistiveTouchView;
|
||||
|
||||
/*
|
||||
* 开启播放状态
|
||||
*/
|
||||
- (void)playPlayerState;
|
||||
|
||||
/*
|
||||
* 暂停播放状态
|
||||
*/
|
||||
- (void)pausePlayerState;
|
||||
|
||||
/*
|
||||
* 停止播放状态
|
||||
*/
|
||||
- (void)stopPlayerState;
|
||||
|
||||
/*
|
||||
* 改变播放进度
|
||||
*/
|
||||
- (void)changePlayProgress:(CGFloat)progress;
|
||||
|
||||
// 关闭悬浮框
|
||||
- (void)closeButtonClick;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,484 @@
|
||||
//
|
||||
// WXYZ_TouchAssistantView.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/3/20.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_TouchAssistantView.h"
|
||||
|
||||
#if TF_Enable_Ai
|
||||
#import "WXYZ_BookAiPlayPageViewController.h"
|
||||
#endif
|
||||
|
||||
#if TF_Enable_Audio
|
||||
#import "TFAudioPlayViewController.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<iflyMSC/IFlyMSC.h>)
|
||||
#import "iflyMSC/IFlyMSC.h"
|
||||
#endif
|
||||
#import "WXYZ_Player.h"
|
||||
|
||||
#define kSystemKeyboardWindowLevel 10000000
|
||||
#define kRotationalAnimationKey @"rotationAnimation"
|
||||
|
||||
@interface WXYZ_TouchAssistantView ()
|
||||
{
|
||||
CGPoint beginCenter; //记录首次点击坐标
|
||||
UITapGestureRecognizer *tapGR; //点击手势
|
||||
TFProductionType _productionType;
|
||||
BOOL _isPause;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) UIWindow *mainWindow;
|
||||
@property (nonatomic, strong) UIView *miniPlayPageView; // 小助手
|
||||
@property (nonatomic, strong) UIImageView *miniPlayIcon; // 作品图标
|
||||
@property (nonatomic, strong) CABasicAnimation *rotationAnimation; // 作品旋转动画
|
||||
@property (nonatomic, strong) UIButton *miniPlayButton;
|
||||
@property (nonatomic, strong) UIButton *closeButton;
|
||||
@property (nonatomic, strong) CAShapeLayer *rewardLayer;
|
||||
@property (nonatomic, copy) NSString *cacheCover;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_TouchAssistantView
|
||||
|
||||
implementation_singleton(WXYZ_TouchAssistantView)
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[self createSubviews];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeRotate:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createSubviews
|
||||
{
|
||||
self.mainWindow = kMainWindow;
|
||||
|
||||
// 点击手势
|
||||
tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
|
||||
tapGR.numberOfTouchesRequired = 1;
|
||||
tapGR.numberOfTapsRequired = 1;
|
||||
tapGR.enabled = YES;
|
||||
|
||||
// 拖动手势
|
||||
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
|
||||
pan.minimumNumberOfTouches = 1;
|
||||
|
||||
self.miniPlayPageView = [[UIView alloc] initWithFrame:[self assistiveViewFrame]];
|
||||
self.miniPlayPageView.hidden = YES;
|
||||
self.miniPlayPageView.backgroundColor = kColorRGBA(255, 255, 255, 0.9);
|
||||
self.miniPlayPageView.layer.cornerRadius = [self assistiveViewFrame].size.height / 2;
|
||||
self.miniPlayPageView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
self.miniPlayPageView.layer.shadowOffset = CGSizeMake(0, 0);
|
||||
self.miniPlayPageView.layer.shadowOpacity = 0.1f;
|
||||
self.miniPlayPageView.layer.shadowRadius = 4.0f;
|
||||
self.miniPlayPageView.userInteractionEnabled = YES;
|
||||
self.miniPlayPageView.tag = 31415926.0;
|
||||
[self.miniPlayPageView addGestureRecognizer:pan];
|
||||
[self.miniPlayPageView addGestureRecognizer:tapGR];
|
||||
[self.mainWindow addSubview:self.miniPlayPageView];
|
||||
|
||||
self.miniPlayIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, [self assistiveViewFrame].size.height, [self assistiveViewFrame].size.height)];
|
||||
self.miniPlayIcon.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[self.miniPlayIcon zy_cornerRadiusAdvance:[self assistiveViewFrame].size.height / 2 rectCornerType:UIRectCornerAllCorners];
|
||||
[self.miniPlayIcon zy_attachBorderWidth:3 color:kWhiteColor];
|
||||
[self.miniPlayPageView addSubview:self.miniPlayIcon];
|
||||
|
||||
UIView *icon = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [self assistiveViewFrame].size.height, [self assistiveViewFrame].size.height)];
|
||||
icon.backgroundColor = [UIColor clearColor];
|
||||
[self.miniPlayPageView addSubview:icon];
|
||||
|
||||
self.rewardLayer = [CAShapeLayer layer];
|
||||
self.rewardLayer.frame = CGRectMake(0, 0, self.miniPlayIcon.height, self.miniPlayIcon.height);
|
||||
self.rewardLayer.strokeColor = kMainColor.CGColor;
|
||||
self.rewardLayer.fillColor = [[UIColor clearColor] CGColor];
|
||||
self.rewardLayer.lineWidth = 3;
|
||||
self.rewardLayer.lineCap = @"round";
|
||||
[icon.layer addSublayer:self.rewardLayer];
|
||||
|
||||
self.miniPlayButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.miniPlayButton.frame = CGRectMake(self.miniPlayIcon.right, self.miniPlayIcon.top, self.miniPlayIcon.width, self.miniPlayIcon.height);
|
||||
self.miniPlayButton.adjustsImageWhenHighlighted = NO;
|
||||
self.miniPlayButton.tintColor = kGrayTextColor;
|
||||
[self.miniPlayButton setImage:[[UIImage imageNamed:@"audio_mini_play"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
[self.miniPlayButton setImageEdgeInsets:UIEdgeInsetsMake(13, 20, 13, 6)];
|
||||
[self.miniPlayButton addTarget:self action:@selector(miniPlayButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.miniPlayPageView addSubview:self.miniPlayButton];
|
||||
|
||||
self.closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
self.closeButton.frame = CGRectMake(self.miniPlayButton.right, self.miniPlayButton.top, self.miniPlayButton.width, self.miniPlayButton.height);
|
||||
self.closeButton.adjustsImageWhenHighlighted = NO;
|
||||
self.closeButton.tintColor = kGrayTextColor;
|
||||
[self.closeButton setImage:[[UIImage imageNamed:@"audio_mini_close"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
[self.closeButton setImageEdgeInsets:UIEdgeInsetsMake(17, 15, 17, 19)];
|
||||
[self.closeButton addTarget:self action:@selector(closeButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.miniPlayPageView addSubview:self.closeButton];
|
||||
}
|
||||
|
||||
#pragma mark - 点击事件
|
||||
- (void)tap:(UITapGestureRecognizer *)gr
|
||||
{
|
||||
if ([[TFViewHelper getCurrentViewController] isKindOfClass:[NSClassFromString(@"TFReadNovelViewController") class]]) {
|
||||
|
||||
BOOL skip = NO;
|
||||
for (UIViewController *t_vc in [TFViewHelper getCurrentViewController].navigationController.viewControllers) {
|
||||
if ([t_vc isKindOfClass:[NSClassFromString(@"WXYZ_BookAiPlayPageViewController") class]] || [t_vc isKindOfClass:[NSClassFromString(@"TFAudioPlayViewController") class]]) {
|
||||
skip = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
[[TFViewHelper getCurrentViewController].navigationController popViewControllerAnimated:YES];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_productionType == TFProductionTypeNovel) {
|
||||
#if TF_Enable_Ai
|
||||
WXYZ_BookAiPlayPageViewController *vc = [WXYZ_BookAiPlayPageViewController sharedManager];
|
||||
if (vc.stoped) {
|
||||
[vc loadDataWithBookModel:nil chapterModel:nil];
|
||||
}
|
||||
TFNavigationController *nav = [[TFNavigationController alloc] initWithRootViewController:vc];
|
||||
if ([[TFViewHelper getCurrentViewController] isKindOfClass:[NSClassFromString(@"TFReadNovelViewController") class]]) {
|
||||
nav.view.tag = 2345;
|
||||
}
|
||||
[[TFViewHelper getWindowRootController] presentViewController:nav animated:YES completion:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_productionType == TFProductionTypeAudio) {
|
||||
#if TF_Enable_Audio
|
||||
TFAudioPlayViewController *vc = [TFAudioPlayViewController sharedManager];
|
||||
if (vc.stoped) {
|
||||
[vc loadDataWithAudio_id:0 chapter_id:0];
|
||||
}
|
||||
TFNavigationController *nav = [[TFNavigationController alloc] initWithRootViewController:vc];
|
||||
if ([[TFViewHelper getCurrentViewController] isKindOfClass:[NSClassFromString(@"TFReadNovelViewController") class]]) {
|
||||
nav.view.tag = 2345;
|
||||
}
|
||||
[[TFViewHelper getWindowRootController] presentViewController:nav animated:YES completion:nil];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 通知
|
||||
//屏幕旋转通知
|
||||
- (void)changeRotate:(NSNotification *)notification
|
||||
{
|
||||
[self calculateAssistiveCenter:self.miniPlayPageView.center];
|
||||
}
|
||||
|
||||
#pragma mark - 拖动事件(动画逻辑)
|
||||
|
||||
- (void)pan:(UIPanGestureRecognizer *)recognizer
|
||||
{
|
||||
//移动点
|
||||
CGPoint point = [recognizer translationInView:self.mainWindow];
|
||||
CGPoint center = self.miniPlayPageView.center;
|
||||
center.x += point.x;
|
||||
center.y += point.y;
|
||||
|
||||
if (recognizer.state == UIGestureRecognizerStateBegan) {//开始
|
||||
|
||||
beginCenter = self.miniPlayPageView.center;
|
||||
|
||||
} else if (recognizer.state == UIGestureRecognizerStateChanged) {//改变
|
||||
|
||||
[self setAssistiveViewCenter:center];
|
||||
[recognizer setTranslation:CGPointZero inView:recognizer.view];
|
||||
|
||||
} else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled ) {//结束 || 取消
|
||||
[self calculateAssistiveCenter:center];
|
||||
|
||||
} else if (recognizer.state == UIGestureRecognizerStateFailed) {//失败
|
||||
[self setAssistiveViewCenter:beginCenter];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 小助手坐标计算方法
|
||||
- (void)calculateAssistiveCenter:(CGPoint)center
|
||||
{
|
||||
CGFloat topSide = center.y - [self assistiveViewFrame].size.height / 2;//上边 y 坐标值
|
||||
CGFloat bottomSide = center.y + [self assistiveViewFrame].size.height / 2;//下边 y 坐标值
|
||||
CGFloat margin = 0;
|
||||
|
||||
[UIView animateWithDuration:kAnimatedDuration animations:^{
|
||||
|
||||
if (center.x <= SCREEN_WIDTH / 2) { // 贴左处理
|
||||
|
||||
if (topSide < PUB_NAVBAR_HEIGHT) { // 贴左上
|
||||
[self setAssistiveViewOrigin:CGPointMake(margin, PUB_NAVBAR_HEIGHT)];
|
||||
} else if (bottomSide > SCREEN_HEIGHT - PUB_TABBAR_HEIGHT) { // 贴左下
|
||||
[self setAssistiveViewOrigin:CGPointMake(margin, SCREEN_HEIGHT - PUB_TABBAR_HEIGHT - [self assistiveViewFrame].size.height)];
|
||||
} else { // 正常贴左
|
||||
[self setAssistiveViewOrigin:CGPointMake(margin, topSide)];
|
||||
}
|
||||
|
||||
} else if (center.x > SCREEN_WIDTH / 2) { // 贴右处理
|
||||
|
||||
if (topSide < PUB_NAVBAR_HEIGHT) { // 贴右上
|
||||
[self setAssistiveViewOrigin:CGPointMake(SCREEN_WIDTH - [self assistiveViewFrame].size.width - margin, PUB_NAVBAR_HEIGHT)];
|
||||
} else if (bottomSide > SCREEN_HEIGHT - PUB_TABBAR_HEIGHT) { // 贴右下
|
||||
[self setAssistiveViewOrigin:CGPointMake(SCREEN_WIDTH - [self assistiveViewFrame].size.width - margin, SCREEN_HEIGHT - PUB_TABBAR_HEIGHT - [self assistiveViewFrame].size.height)];
|
||||
} else { // 正常贴右
|
||||
[self setAssistiveViewOrigin:CGPointMake(SCREEN_WIDTH - [self assistiveViewFrame].size.width - margin, topSide)];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)miniPlayButtonClick
|
||||
{
|
||||
if (_productionType == TFProductionTypeNovel) {
|
||||
#if TF_Enable_Ai
|
||||
WXYZ_BookAiPlayPageViewController *vc = [WXYZ_BookAiPlayPageViewController sharedManager];
|
||||
if (vc.speaking) {
|
||||
[[IFlySpeechSynthesizer sharedInstance] pauseSpeaking];
|
||||
} else if (!vc.stoped) {
|
||||
[[IFlySpeechSynthesizer sharedInstance] resumeSpeaking];
|
||||
} else {
|
||||
[vc loadDataWithBookModel:nil chapterModel:nil];
|
||||
TFNavigationController *nav = [[TFNavigationController alloc] initWithRootViewController:vc];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_productionType == TFProductionTypeAudio) {
|
||||
#if TF_Enable_Audio
|
||||
TFAudioPlayViewController *vc = [TFAudioPlayViewController sharedManager];
|
||||
if (vc.speaking) {
|
||||
[[WXYZ_Player sharedPlayer] pause];
|
||||
} else if (!vc.stoped) {
|
||||
[[WXYZ_Player sharedPlayer] play];
|
||||
} else {
|
||||
[vc loadDataWithAudio_id:0 chapter_id:0];
|
||||
TFNavigationController *nav = [[TFNavigationController alloc] initWithRootViewController:vc];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeButtonClick
|
||||
{
|
||||
// if (_productionType == TFProductionTypeNovel) {
|
||||
#if __has_include(<iflyMSC/IFlyMSC.h>)
|
||||
[[IFlySpeechSynthesizer sharedInstance] pauseSpeaking];
|
||||
#endif
|
||||
// }
|
||||
|
||||
// if (_productionType == TFProductionTypeAudio) {
|
||||
[[WXYZ_Player sharedPlayer] pause];
|
||||
// }
|
||||
|
||||
self.cacheCover = nil;
|
||||
|
||||
[self stopPlayerState];
|
||||
|
||||
[self hiddenAssistiveTouchView];
|
||||
|
||||
kCodeSync([[UIApplication sharedApplication] endReceivingRemoteControlEvents]);
|
||||
}
|
||||
|
||||
#pragma mark - 变量
|
||||
- (CGRect)assistiveViewFrame
|
||||
{
|
||||
return CGRectMake(0, SCREEN_HEIGHT - 200, 45 * 3, 45);
|
||||
}
|
||||
|
||||
//设置小助手 && 菜单栏Center
|
||||
- (void)setAssistiveViewCenter:(CGPoint)newCenter
|
||||
{
|
||||
self.miniPlayPageView.center = newCenter;
|
||||
}
|
||||
|
||||
- (void)setAssistiveViewOrigin:(CGPoint)newPoint
|
||||
{
|
||||
self.miniPlayPageView.origin = newPoint;
|
||||
}
|
||||
|
||||
- (CABasicAnimation *)rotationAnimation
|
||||
{
|
||||
if (!_rotationAnimation) {
|
||||
CABasicAnimation *rotationAnimation;
|
||||
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
||||
rotationAnimation.fromValue = [NSNumber numberWithFloat:0];
|
||||
rotationAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
|
||||
rotationAnimation.duration = 10;
|
||||
rotationAnimation.repeatCount = HUGE_VALF;
|
||||
rotationAnimation.removedOnCompletion = NO;
|
||||
_rotationAnimation = rotationAnimation;
|
||||
}
|
||||
return _rotationAnimation;
|
||||
}
|
||||
|
||||
// 暂停动画
|
||||
- (void)pauseAnimate
|
||||
{
|
||||
if (_isPause) return;
|
||||
_isPause = YES;
|
||||
|
||||
CFTimeInterval pauseTime = [self.miniPlayIcon.layer convertTime:CACurrentMediaTime() fromLayer:nil];
|
||||
self.miniPlayIcon.layer.speed = 0;
|
||||
self.miniPlayIcon.layer.timeOffset = pauseTime;
|
||||
}
|
||||
|
||||
// 继续动画
|
||||
- (void)resumeAnimate
|
||||
{
|
||||
if (!_isPause) return;
|
||||
_isPause = NO;
|
||||
|
||||
CFTimeInterval pauseTime = self.miniPlayIcon.layer.timeOffset;
|
||||
self.miniPlayIcon.layer.speed = 1.0;
|
||||
self.miniPlayIcon.layer.timeOffset = 0.0;
|
||||
self.miniPlayIcon.layer.beginTime = 0.0;
|
||||
CFTimeInterval timeSincePause = [self.miniPlayIcon.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pauseTime;
|
||||
self.miniPlayIcon.layer.beginTime = timeSincePause;
|
||||
}
|
||||
|
||||
// 停止动画
|
||||
- (void)stopAnimate
|
||||
{
|
||||
self.rewardLayer.path = [UIBezierPath bezierPathWithArcCenter:self.miniPlayIcon.center radius:(self.miniPlayIcon.height - 2) / 2 startAngle:- M_PI_2 endAngle:- M_PI_2 clockwise:YES].CGPath;
|
||||
[self.miniPlayIcon.layer removeAnimationForKey:kRotationalAnimationKey];
|
||||
}
|
||||
|
||||
// 开始动画
|
||||
- (void)startAnimate
|
||||
{
|
||||
if (![self.miniPlayIcon.layer animationForKey:kRotationalAnimationKey]) {
|
||||
[self.miniPlayIcon.layer addAnimation:self.rotationAnimation forKey:kRotationalAnimationKey];
|
||||
}
|
||||
[self resumeAnimate];
|
||||
}
|
||||
|
||||
// 显示小助手
|
||||
- (void)showAssistiveTouchViewWithImageCover:(NSString *)imageCover productionType:(TFProductionType)productionType
|
||||
{
|
||||
_productionType = productionType;
|
||||
|
||||
if (self.cacheCover != imageCover) {
|
||||
self.cacheCover = imageCover;
|
||||
[self stopAnimate];
|
||||
}
|
||||
|
||||
[self showAssistiveTouchView];
|
||||
}
|
||||
|
||||
- (void)showAssistiveTouchView
|
||||
{
|
||||
if (self.cacheCover.length == 0 || !self.cacheCover) {
|
||||
return;
|
||||
}
|
||||
#if TF_Enable_Audio
|
||||
TFAudioPlayViewController *audioViewController = [TFAudioPlayViewController sharedManager];
|
||||
#endif
|
||||
#if TF_Enable_Ai
|
||||
WXYZ_BookAiPlayPageViewController *aiViewController = [WXYZ_BookAiPlayPageViewController sharedManager];
|
||||
#endif
|
||||
|
||||
[self.miniPlayIcon setImageWithURL:[NSURL URLWithString:self.cacheCover] placeholder:HoldImage options:YYWebImageOptionSetImageWithFadeAnimation completion:nil];
|
||||
|
||||
if (_productionType == TFProductionTypeNovel) {
|
||||
#if TF_Enable_Ai
|
||||
if (aiViewController.speaking) {
|
||||
[self playPlayerState];
|
||||
} else {
|
||||
if (!audioViewController.speaking) {
|
||||
[self pausePlayerState];
|
||||
}
|
||||
}
|
||||
self.rewardLayer.hidden = YES;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_productionType == TFProductionTypeAudio) {
|
||||
#if TF_Enable_Audio
|
||||
if (audioViewController.speaking) {
|
||||
[self playPlayerState];
|
||||
} else {
|
||||
if (!aiViewController.speaking) {
|
||||
[self pausePlayerState];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
self.rewardLayer.hidden = NO;
|
||||
}
|
||||
|
||||
self.miniPlayPageView.hidden = NO;
|
||||
|
||||
[self.mainWindow bringSubviewToFront:self.miniPlayPageView];
|
||||
}
|
||||
|
||||
- (void)setPlayerProductionType:(TFProductionType)productionType
|
||||
{
|
||||
_productionType = productionType;
|
||||
}
|
||||
|
||||
// 隐藏小助手
|
||||
- (void)hiddenAssistiveTouchView
|
||||
{
|
||||
self.miniPlayPageView.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)changePlayProgress:(CGFloat)progress
|
||||
{
|
||||
WS(weakSelf)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.rewardLayer.path = [UIBezierPath bezierPathWithArcCenter:weakSelf.miniPlayIcon.center
|
||||
radius:(weakSelf.miniPlayIcon.height - 2) / 2
|
||||
startAngle:- M_PI_2 endAngle:(M_PI * 2)* progress - M_PI_2
|
||||
clockwise:YES].CGPath;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
- (void)playPlayerState
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self startAnimate];
|
||||
[self.miniPlayButton setImage:[[UIImage imageNamed:@"audio_mini_pause"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pausePlayerState
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self pauseAnimate];
|
||||
[self.miniPlayButton setImage:[[UIImage imageNamed:@"audio_mini_play"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopPlayerState
|
||||
{
|
||||
#if TF_Enable_Audio
|
||||
TFAudioPlayViewController *audioViewController = [TFAudioPlayViewController sharedManager];
|
||||
#endif
|
||||
#if TF_Enable_Ai
|
||||
WXYZ_BookAiPlayPageViewController *aiViewController = [WXYZ_BookAiPlayPageViewController sharedManager];
|
||||
if (!aiViewController.speaking && !audioViewController.speaking) {
|
||||
[self coerceStopPlayerState];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// 强制停止
|
||||
- (void)coerceStopPlayerState
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self stopAnimate];
|
||||
[self.miniPlayButton setImage:[[UIImage imageNamed:@"audio_mini_play"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user