You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
18 KiB
484 lines
18 KiB
// |
|
// 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
|
|
|