diff --git a/YuMi.xcodeproj/project.pbxproj b/YuMi.xcodeproj/project.pbxproj index 0afb3d09..bed8a1ba 100644 --- a/YuMi.xcodeproj/project.pbxproj +++ b/YuMi.xcodeproj/project.pbxproj @@ -500,6 +500,8 @@ 4C6E1F752CEAEC3C0073D0A3 /* ShoppingMallTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F742CEAEC3C0073D0A3 /* ShoppingMallTagView.m */; }; 4C6E1F792CEB12780073D0A3 /* UIView+GradientLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F782CEB12780073D0A3 /* UIView+GradientLayer.m */; }; 4C6E1F7C2CEB25B10073D0A3 /* ShoppingMallItemPreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F7B2CEB25B10073D0A3 /* ShoppingMallItemPreview.m */; }; + 4C71C69F2D069D2B00ECCA24 /* GiftAnimationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C71C69E2D069D2B00ECCA24 /* GiftAnimationHelper.m */; }; + 4C71C6A22D06DB3D00ECCA24 /* GiftAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C71C6A12D06DB3D00ECCA24 /* GiftAnimationManager.m */; }; 4C815A172CFEB758002A46A6 /* SuperBlockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C815A162CFEB758002A46A6 /* SuperBlockViewController.m */; }; 4CC6195A2CEC7770008C1EE8 /* MyDressingDataPresent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC619592CEC7770008C1EE8 /* MyDressingDataPresent.m */; }; 4CC6195D2CEC996E008C1EE8 /* MyDressingDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC6195C2CEC996E008C1EE8 /* MyDressingDataModel.m */; }; @@ -2588,6 +2590,10 @@ 4C6E1F782CEB12780073D0A3 /* UIView+GradientLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+GradientLayer.m"; sourceTree = ""; }; 4C6E1F7A2CEB25B10073D0A3 /* ShoppingMallItemPreview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShoppingMallItemPreview.h; sourceTree = ""; }; 4C6E1F7B2CEB25B10073D0A3 /* ShoppingMallItemPreview.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShoppingMallItemPreview.m; sourceTree = ""; }; + 4C71C69D2D069D2B00ECCA24 /* GiftAnimationHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GiftAnimationHelper.h; sourceTree = ""; }; + 4C71C69E2D069D2B00ECCA24 /* GiftAnimationHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GiftAnimationHelper.m; sourceTree = ""; }; + 4C71C6A02D06DB3D00ECCA24 /* GiftAnimationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GiftAnimationManager.h; sourceTree = ""; }; + 4C71C6A12D06DB3D00ECCA24 /* GiftAnimationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GiftAnimationManager.m; sourceTree = ""; }; 4C815A152CFEB758002A46A6 /* SuperBlockViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SuperBlockViewController.h; sourceTree = ""; }; 4C815A162CFEB758002A46A6 /* SuperBlockViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SuperBlockViewController.m; sourceTree = ""; }; 4CC619582CEC7770008C1EE8 /* MyDressingDataPresent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyDressingDataPresent.h; sourceTree = ""; }; @@ -8271,6 +8277,10 @@ E8998D7F28597B0300C68558 /* XPRoomLuckyBigPrizeView.m */, E838D99E275E1BF60079E0B5 /* XPRoomAnimationView.h */, E838D99F275E1BF60079E0B5 /* XPRoomAnimationView.m */, + 4C71C6A02D06DB3D00ECCA24 /* GiftAnimationManager.h */, + 4C71C6A12D06DB3D00ECCA24 /* GiftAnimationManager.m */, + 4C71C69D2D069D2B00ECCA24 /* GiftAnimationHelper.h */, + 4C71C69E2D069D2B00ECCA24 /* GiftAnimationHelper.m */, 23E9EA782A8385CC00B792F2 /* XPTreasureFairyGiftView.h */, 23E9EA772A8385CB00B792F2 /* XPTreasureFairyGiftView.m */, E8E21A9928B4BD92008F7C9D /* XPRoomGraffitiGiftAnimationView.h */, @@ -12378,6 +12388,7 @@ 238B37C52AC55A2C00BFC9D5 /* XPTreasureFairyMoreView.m in Sources */, E811FFF72742367B00918544 /* XPGiftEmptyCollectionViewCell.m in Sources */, 189DD67E26E1FD8900AB55B1 /* UIImage+Utils.m in Sources */, + 4C71C6A22D06DB3D00ECCA24 /* GiftAnimationManager.m in Sources */, E82D5C73276AE94800858D6D /* CarModel.m in Sources */, E85E7B0B2A4EB0D200B6D00A /* XPGuildSetNamePresenter.m in Sources */, E87E62762A3F5907002F68C9 /* XPNewHomePlayTableViewCell.m in Sources */, @@ -12758,6 +12769,7 @@ E81DCCD0282B63FD0039E5C5 /* XPMomentsRecommendViewController.m in Sources */, 9B7D804A2753783D003DAC0C /* SessionViewController.m in Sources */, E85E7BA92A4EC99300B6D00A /* XPMineConfirmGiveDiamondView.m in Sources */, + 4C71C69F2D069D2B00ECCA24 /* GiftAnimationHelper.m in Sources */, 54C9A1102C3D3E1700C6D970 /* XPMineGameMateOrderView.m in Sources */, E85E7BA72A4EC99300B6D00A /* XPMineGiveDiamondPasswordView.m in Sources */, 54283CEE2CE48B97009729B5 /* ShoppingMallDataPresent.m in Sources */, diff --git a/YuMi/Modules/YMLogin/Presenter/LoginPresenter.m b/YuMi/Modules/YMLogin/Presenter/LoginPresenter.m index bc6d0ddd..bef8661c 100644 --- a/YuMi/Modules/YMLogin/Presenter/LoginPresenter.m +++ b/YuMi/Modules/YMLogin/Presenter/LoginPresenter.m @@ -21,8 +21,8 @@ #import "ThirdUserInfo.h" #import "AccountModel.h" #import "DESEncrypt.h" -#import -#import +//#import +//#import #import "YuMi-swift.h" #import "FeedBackConfigModel.h" @@ -33,25 +33,25 @@ - (instancetype)init{ self = [super init]; if(self){ - [[NSNotificationCenter defaultCenter] addObserverForName:FBSDKProfileDidChangeNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock: - ^(NSNotification *notification) { - if ([FBSDKProfile currentProfile]) { - //获取当前用户名 - [FBSDKProfile loadCurrentProfileWithCompletion: - ^(FBSDKProfile *profile, NSError *error) { - if (profile) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - AccountInfoStorage *accountStorage = [AccountInfoStorage instance]; - accountStorage.thirdUserInfo.userName = profile.name; - accountStorage.thirdUserInfo.avatarUrl = profile.imageURL.absoluteString; - }); - } - }]; - } - }]; +// [[NSNotificationCenter defaultCenter] addObserverForName:FBSDKProfileDidChangeNotification +// object:nil +// queue:[NSOperationQueue mainQueue] +// usingBlock: +// ^(NSNotification *notification) { +// if ([FBSDKProfile currentProfile]) { +// //获取当前用户名 +// [FBSDKProfile loadCurrentProfileWithCompletion: +// ^(FBSDKProfile *profile, NSError *error) { +// if (profile) { +// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +// AccountInfoStorage *accountStorage = [AccountInfoStorage instance]; +// accountStorage.thirdUserInfo.userName = profile.name; +// accountStorage.thirdUserInfo.avatarUrl = profile.imageURL.absoluteString; +// }); +// } +// }]; +// } +// }]; } return self; } @@ -200,31 +200,31 @@ }]; } -(void)thirdLoginByFBWithPresentingViewController:(UIViewController *)presentingViewController { - [FBSDKProfile enableUpdatesOnAccessTokenChange:YES]; - [FBSDKAccessToken setCurrentAccessToken:nil]; - FBSDKLoginManager *manager = [[FBSDKLoginManager alloc] init]; - [manager logOut]; - [XNDJTDDLoadingTool showOnlyView:kWindow]; - @kWeakify(self); - [manager logInWithPermissions:@[@"public_profile"] - fromViewController:presentingViewController - handler:^(FBSDKLoginManagerLoginResult * _Nullable result, NSError * _Nullable error) { - @kStrongify(self); - if (error) { - [XNDJTDDLoadingTool hideOnlyView:kWindow]; - [[self getView] showErrorToast:YMLocalizedString(@"LoginPresenter1")]; - } else if (result.isCancelled) { - [XNDJTDDLoadingTool hideOnlyView:kWindow]; - [[self getView] showErrorToast:YMLocalizedString(@"LoginPresenter0")]; - } else { - ThirdUserInfo * userInfo = [[ThirdUserInfo alloc] init]; - userInfo.openid = result.token.userID; - userInfo.access_token = result.token.tokenString; - userInfo.unionid = result.token.userID; - [AccountInfoStorage instance].thirdUserInfo = userInfo; - [self loginWithThirdPartWithType:ThirdLoginType_FB]; - } - }]; +// [FBSDKProfile enableUpdatesOnAccessTokenChange:YES]; +// [FBSDKAccessToken setCurrentAccessToken:nil]; +// FBSDKLoginManager *manager = [[FBSDKLoginManager alloc] init]; +// [manager logOut]; +// [XNDJTDDLoadingTool showOnlyView:kWindow]; +// @kWeakify(self); +// [manager logInWithPermissions:@[@"public_profile"] +// fromViewController:presentingViewController +// handler:^(FBSDKLoginManagerLoginResult * _Nullable result, NSError * _Nullable error) { +// @kStrongify(self); +// if (error) { +// [XNDJTDDLoadingTool hideOnlyView:kWindow]; +// [[self getView] showErrorToast:YMLocalizedString(@"LoginPresenter1")]; +// } else if (result.isCancelled) { +// [XNDJTDDLoadingTool hideOnlyView:kWindow]; +// [[self getView] showErrorToast:YMLocalizedString(@"LoginPresenter0")]; +// } else { +// ThirdUserInfo * userInfo = [[ThirdUserInfo alloc] init]; +// userInfo.openid = result.token.userID; +// userInfo.access_token = result.token.tokenString; +// userInfo.unionid = result.token.userID; +// [AccountInfoStorage instance].thirdUserInfo = userInfo; +// [self loginWithThirdPartWithType:ThirdLoginType_FB]; +// } +// }]; } -(void)thirdLoginByGoogleWithPresentingViewController:(UIViewController *)presentingViewController configuration:(GIDConfiguration *)configuration{ @kWeakify(self); diff --git a/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.h b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.h new file mode 100644 index 00000000..a661f34e --- /dev/null +++ b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.h @@ -0,0 +1,29 @@ +// +// GiftAnimationManager.h +// YuMi +// +// Created by P on 2024/12/9. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GiftAnimationHelper : NSObject + +/** + * Begins a gift animation from a start point to an end point + * @param giftUrl URL of the gift image to display + * @param startPoint Starting position of the animation + * @param endPoint Ending position of the animation + * @param isGiftCombing Flag to determine if this is a combo animation + */ +- (void)beginGiftAnimation:(NSString *)giftUrl + startPoint:(CGPoint)startPoint + endPoint:(CGPoint)endPoint + isGiftCombing:(BOOL)isGiftCombing + toTargetView:(UIView *)targetView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.m b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.m new file mode 100644 index 00000000..6c7fc106 --- /dev/null +++ b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationHelper.m @@ -0,0 +1,335 @@ +// +// GiftAnimationManager.m +// YuMi +// +// Created by P on 2024/12/9. +// + +#import "GiftAnimationHelper.h" + +// Constants +static const CGFloat kGiftImageSize = 55.0f; +static const CGFloat kStandardAnimationDuration = 3.2f; +static const CGFloat kComboAnimationDuration = 1.0f; +static const CGFloat kCleanupDelay = 0.25f; + +static const CGFloat kComboInitialScale = 0.4f; +static const CGFloat kComboFinalScale = 2.0f; +static const CGFloat kComboScaleDuration = 0.1f; +static const CGFloat kComboMoveDuration = 0.5f; +static const CGFloat kComboTotalDuration = 0.6f; + +static const CGFloat kStandardScaleDuration = 0.8f; +static const CGFloat kStandardTotalDuration = 3.2f; +static const CGFloat kStandardSecondPhaseDelay = 0.8f; +static const CGFloat kStandardFinalPhaseDelay = 2.6f; + +@interface GiftAnimationHelper () + +@property (nonatomic, strong) NSMutableSet *giftReuseArray; +@property (nonatomic, strong) NSMutableSet *giftVisibleArray; +@property (nonatomic, strong) UIView *lowLevelView; +@end + +@implementation GiftAnimationHelper + +- (instancetype)init { + if (self = [super init]) { + self.giftReuseArray = [NSMutableSet set]; + self.giftVisibleArray = [NSMutableSet set]; + } + return self; +} + +- (void)beginGiftAnimation:(NSString *)giftUrl + startPoint:(CGPoint)startPoint + endPoint:(CGPoint)endPoint + isGiftCombing:(BOOL)isGiftCombing + toTargetView:(UIView *)targetView { + // Input validation + if (!giftUrl.length || CGPointEqualToPoint(startPoint, endPoint)) { + NSLog(@"Invalid input parameters for gift animation"); + return; + } + + if (targetView == nil) { + return; + } + + self.lowLevelView = targetView; + + // Get or create gift image view + NetImageView *giftImageView = [self dequeueGiftImageView]; + [self configureGiftImageView:giftImageView + withGiftUrl:giftUrl + startPoint:startPoint]; + + // Add to view hierarchy + [self.lowLevelView addSubview:giftImageView]; + + // Create and apply animation + CGFloat animationDuration = isGiftCombing ? kComboAnimationDuration : kStandardAnimationDuration; + CAAnimationGroup *animationGroup = isGiftCombing ? + [self createGiftComboAnimationStartPoint:startPoint endPoint:endPoint] : + [self createGiftAnimationStartPoint:startPoint endPoint:endPoint]; + + // Ensure correct starting position + giftImageView.layer.position = startPoint; + [giftImageView.layer addAnimation:animationGroup forKey:@"giftDisplayViewAnimation"]; + + // Schedule cleanup + [self performSelector:@selector(animationDidFinish:) + withObject:giftImageView + afterDelay:(animationDuration + kCleanupDelay)]; +} + +- (NetImageView *)dequeueGiftImageView { + NetImageView *giftImageView = [self.giftReuseArray anyObject]; + if (giftImageView) { + [self.giftReuseArray removeObject:giftImageView]; + } else { + giftImageView = [[NetImageView alloc] initWithFrame:CGRectMake(0, 0, kGiftImageSize, kGiftImageSize)]; + [self.giftVisibleArray addObject:giftImageView]; + } + return giftImageView; +} + +- (void)configureGiftImageView:(NetImageView *)giftImageView + withGiftUrl:(NSString *)giftUrl + startPoint:(CGPoint)startPoint { + giftImageView.center = startPoint; + giftImageView.alpha = 1.0; + giftImageView.layer.anchorPoint = CGPointMake(0.5, 0.5); + giftImageView.imageUrl = giftUrl; + giftImageView.hidden = NO; +} + +- (void)animationDidFinish:(NetImageView *)giftImageView { + [giftImageView removeFromSuperview]; + giftImageView.hidden = YES; + [self.giftReuseArray addObject:giftImageView]; +} + +- (CAAnimationGroup *)createGiftComboAnimationStartPoint:(CGPoint)startPoint + endPoint:(CGPoint)endPoint { + // Initial shrink animation + CAKeyframeAnimation *initialScale = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; + initialScale.duration = kComboScaleDuration; + initialScale.values = @[@1.0, @(kComboInitialScale)]; + initialScale.repeatCount = 1; + initialScale.calculationMode = kCAAnimationCubic; + initialScale.removedOnCompletion = NO; + initialScale.fillMode = kCAFillModeForwards; + + // Movement animation + CAKeyframeAnimation *movement = [CAKeyframeAnimation animationWithKeyPath:@"position"]; + movement.duration = kComboMoveDuration; + movement.beginTime = kComboScaleDuration; + movement.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + movement.values = @[ + [NSValue valueWithCGPoint:startPoint], + [NSValue valueWithCGPoint:endPoint] + ]; + movement.repeatCount = 1; + movement.removedOnCompletion = NO; + movement.fillMode = kCAFillModeForwards; + + // Growth during movement animation + CAKeyframeAnimation *growthDuringMove = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; + growthDuringMove.duration = kComboMoveDuration; + growthDuringMove.beginTime = kComboScaleDuration; + growthDuringMove.values = @[@(kComboInitialScale), @(kComboFinalScale)]; + growthDuringMove.repeatCount = 1; + growthDuringMove.calculationMode = kCAAnimationCubic; + growthDuringMove.removedOnCompletion = NO; + growthDuringMove.fillMode = kCAFillModeForwards; + + // Combine animations + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.duration = kComboTotalDuration; + group.animations = @[initialScale, movement, growthDuringMove]; + group.repeatCount = 1; + group.removedOnCompletion = NO; + group.fillMode = kCAFillModeForwards; + + return group; +} + +/** + * Creates a complex gift animation with multiple phases + * @param startPoint Initial position of the gift + * @param endPoint Final position of the gift + * @return CAAnimationGroup containing multiple scale and position animations + */ +- (CAAnimationGroup *)createGiftAnimationStartPoint:(CGPoint)startPoint + endPoint:(CGPoint)endPoint { + CGPoint centerPoint = CGPointMake(KScreenWidth / 2, KScreenHeight / 2); + + // Initial scale up animation + CAKeyframeAnimation *initialScale = [CAKeyframeAnimation animation]; + initialScale.duration = kStandardScaleDuration; + initialScale.keyPath = @"transform.scale"; + initialScale.values = @[@1.0, @1.5, @2.0, @1.5]; + initialScale.repeatCount = 1; + initialScale.calculationMode = kCAAnimationCubic; + initialScale.removedOnCompletion = NO; + initialScale.fillMode = kCAFillModeForwards; + + // Second phase scale animation + CAKeyframeAnimation *secondScale = [CAKeyframeAnimation animation]; + secondScale.duration = kStandardScaleDuration; + secondScale.beginTime = kStandardSecondPhaseDelay; + secondScale.keyPath = @"transform.scale"; + secondScale.values = @[@1.5, @2.0, @2.5, @3.0]; + secondScale.repeatCount = 1; + secondScale.calculationMode = kCAAnimationCubic; + secondScale.removedOnCompletion = NO; + secondScale.fillMode = kCAFillModeForwards; + + // Move to center animation + CAKeyframeAnimation *moveToCenter = [CAKeyframeAnimation animation]; + moveToCenter.duration = kStandardScaleDuration; + moveToCenter.beginTime = kStandardSecondPhaseDelay; + moveToCenter.keyPath = @"position"; + moveToCenter.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + moveToCenter.values = @[ + [NSValue valueWithCGPoint:startPoint], + [NSValue valueWithCGPoint:centerPoint] + ]; + moveToCenter.repeatCount = 1; + moveToCenter.removedOnCompletion = NO; + moveToCenter.fillMode = kCAFillModeForwards; + + // Final scale down animation + CAKeyframeAnimation *finalScale = [CAKeyframeAnimation animation]; + finalScale.duration = kStandardScaleDuration; + finalScale.beginTime = kStandardFinalPhaseDelay; + finalScale.keyPath = @"transform.scale"; + finalScale.values = @[@3.0, @2.5, @2.0, @1.5, @1.0]; + finalScale.repeatCount = 1; + + // Move to final position animation + CAKeyframeAnimation *moveToEnd = [CAKeyframeAnimation animation]; + moveToEnd.duration = kStandardScaleDuration; + moveToEnd.beginTime = kStandardFinalPhaseDelay; + moveToEnd.keyPath = @"position"; + moveToEnd.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + moveToEnd.values = @[ + [NSValue valueWithCGPoint:centerPoint], + [NSValue valueWithCGPoint:endPoint] + ]; + moveToEnd.repeatCount = 1; + + // Combine all animations + CAAnimationGroup *group = [CAAnimationGroup animation]; + group.duration = kStandardTotalDuration; + group.animations = @[initialScale, secondScale, moveToCenter, finalScale, moveToEnd]; + group.repeatCount = 1; + group.removedOnCompletion = NO; + group.fillMode = kCAFillModeForwards; + + return group; +} + +//- (CAAnimationGroup *)createGiftComboAnimationStartPoint:(CGPoint)startPoint +// endPoint:(CGPoint)endPoint { +// // 缩放动画1: 动画开始时缩放至 0.4 +// CAKeyframeAnimation *scaleAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; +// scaleAnimation1.duration = 0.1; +// scaleAnimation1.values = @[@1.0, @0.4]; +// scaleAnimation1.repeatCount = 1; +// scaleAnimation1.calculationMode = kCAAnimationCubic; +// scaleAnimation1.removedOnCompletion = NO; +// scaleAnimation1.fillMode = kCAFillModeForwards; +// +// // 位移动画: 0.5秒内从起点移动到目标点 +// CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; +// positionAnimation.duration = 0.5; +// positionAnimation.beginTime = 0.1; // 缩放结束后开始位移 +// positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; +// positionAnimation.values = @[[NSValue valueWithCGPoint:startPoint], [NSValue valueWithCGPoint:endPoint]]; +// positionAnimation.repeatCount = 1; +// positionAnimation.removedOnCompletion = NO; +// positionAnimation.fillMode = kCAFillModeForwards; +// +// // 缩放动画2: 在位移的过程中逐渐变大到 1.2 +// CAKeyframeAnimation *scaleAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; +// scaleAnimation2.duration = 0.5; +// scaleAnimation2.beginTime = 0.1; // 同时与位移动画进行 +// scaleAnimation2.values = @[@0.4, @2]; +// scaleAnimation2.repeatCount = 1; +// scaleAnimation2.calculationMode = kCAAnimationCubic; +// scaleAnimation2.removedOnCompletion = NO; +// scaleAnimation2.fillMode = kCAFillModeForwards; +// +// // 创建动画组 +// CAAnimationGroup *group = [CAAnimationGroup animation]; +// group.duration = 0.6; // 总时间为 0.1 (缩放) + 0.5 (位移与缩放) +// group.animations = @[scaleAnimation1, positionAnimation, scaleAnimation2]; +// group.repeatCount = 1; +// group.removedOnCompletion = NO; +// group.fillMode = kCAFillModeForwards; +// +// return group; +//} +// +///// 图片运动的动画组 +///// @param startPoint 开始的点 +///// @param endPoint 结束的点 +//- (CAAnimationGroup *)createGiftAnimationStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint { +// CGPoint centerPoint = CGPointMake(KScreenWidth / 2, KScreenHeight / 2); +// CAKeyframeAnimation *animation0 = [CAKeyframeAnimation animation]; +// animation0.duration = 0.8; +// animation0.keyPath = @"transform.scale"; +// animation0.values = @[@1.0,@1.5,@2.0,@1.5]; +// animation0.repeatCount = 1; +// animation0.calculationMode = kCAAnimationCubic; +// animation0.removedOnCompletion = NO; +// animation0.fillMode = kCAFillModeForwards; +// +// CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation]; +// animation1.duration = 0.8; +// animation1.beginTime = 0.8; +// animation1.keyPath = @"transform.scale"; +// animation1.values = @[@1.5,@2.0,@2.5,@3.0]; +// animation1.repeatCount = 1; +// animation1.calculationMode = kCAAnimationCubic; +// animation1.removedOnCompletion = NO; +// animation1.fillMode = kCAFillModeForwards; +// +// CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animation]; +// animation2.duration = 0.8; +// animation2.beginTime = 0.8; +// animation2.keyPath = @"position"; +// animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];; +// animation2.values = @[[NSValue valueWithCGPoint:startPoint],[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)]]; +// animation2.repeatCount = 1; +// animation2.removedOnCompletion = NO; +// animation2.fillMode = kCAFillModeForwards; +// +// CAKeyframeAnimation *animation3 = [CAKeyframeAnimation animation]; +// animation3.duration = 0.8; +// animation3.beginTime = 2.6;//0.8+0.8+1 +// animation3.keyPath = @"transform.scale"; +// animation3.values = @[@3,@2.5,@2,@1.5,@1]; +// animation3.repeatCount = 1; +// +// CAKeyframeAnimation *animation4 = [CAKeyframeAnimation animation]; +// animation4.duration = 0.8; +// animation4.beginTime = 2.6; +// animation4.keyPath = @"position"; +// animation4.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; +// animation4.values = @[[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)],[NSValue valueWithCGPoint:endPoint]]; +// animation4.repeatCount = 1; +// +// CAAnimationGroup *group = [CAAnimationGroup animation]; +// group.duration = 3.2; +// group.animations = @[animation0,animation1,animation2, animation3,animation4]; +// group.repeatCount = 1; +// group.removedOnCompletion = NO; +// group.fillMode = kCAFillModeForwards; +// +// return group; +//} + +@end diff --git a/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.h b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.h new file mode 100644 index 00000000..0b02e35a --- /dev/null +++ b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.h @@ -0,0 +1,57 @@ +// +// GiftAnimationManager.h +// YuMi +// +// Created by P on 2024/12/9. +// + +#import +@class GiftReceiveInfoModel; + +NS_ASSUME_NONNULL_BEGIN + +@protocol GiftAnimationDelegate + +/** + * Returns the animation point for a given user ID in the stage view + * @param uid The user ID to get the animation point for + * @return CGPoint The point in the view's coordinate system where the animation should occur + */ +- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid; + +@optional +/** + * Called when a gift animation starts + * @param giftInfo The gift information model + */ +//- (void)didStartGiftAnimation:(GiftInfoModel *)giftInfo; + +/** + * Called when a gift animation completes + * @param giftInfo The gift information model + */ +//- (void)didCompleteGiftAnimation:(GiftInfoModel *)giftInfo; + +/** + * Called when the gift queue becomes empty + */ +- (void)didEmptyGiftQueue; + +@end + +@interface GiftAnimationManager : NSObject +@property (nonatomic, weak) id delegate; +@property (nonatomic, strong) UIView *containerView; + +// Configurable properties +@property (nonatomic, assign) NSTimeInterval animationInterval; +@property (nonatomic, assign) NSTimeInterval comboAnimationDelay; +@property (nonatomic, assign) NSTimeInterval standardAnimationDelay; + +- (instancetype)initWithContainerView:(UIView *)containerView; +- (void)enqueueGift:(GiftReceiveInfoModel *)giftInfo; +- (void)startGiftQueue; +- (void)stopGiftQueue; +@end + +NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.m b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.m new file mode 100644 index 00000000..9b7c7bdc --- /dev/null +++ b/YuMi/Modules/YMRoom/View/AnimationView/GiftAnimationManager.m @@ -0,0 +1,195 @@ +// +// GiftAnimationManager.m +// YuMi +// +// Created by P on 2024/12/9. +// + +#import "GiftAnimationManager.h" + +#import "GiftComboManager.h" +#import "GiftAnimationHelper.h" +#import "GiftReceiveInfoModel.h" + +@interface GiftAnimationManager () + +@property (nonatomic, strong) dispatch_source_t giftTimer; +@property (nonatomic, strong) NSMutableArray *giftQueue; +@property (nonatomic, strong) GiftAnimationHelper *animationHelper; + +@end + +@implementation GiftAnimationManager + +- (instancetype)initWithContainerView:(UIView *)containerView { + self = [super init]; + if (self) { + _containerView = containerView; + _giftQueue = [NSMutableArray array]; + _animationHelper = [[GiftAnimationHelper alloc] init]; + _animationInterval = 0.25; + _comboAnimationDelay = 0.25; + _standardAnimationDelay = 0.5; + } + return self; +} + +- (void)startGiftQueue { + if (self.giftTimer) return; + + dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + + dispatch_source_set_timer(timer, + DISPATCH_TIME_NOW, + self.animationInterval * NSEC_PER_SEC, + 0); + + @kWeakify(self); + dispatch_source_set_event_handler(timer, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + @kStrongify(self); + [self processNextGift]; + }); + }); + + dispatch_resume(timer); + self.giftTimer = timer; +} + +- (void)processNextGift { + if (self.giftQueue.count == 0) { + [self stopGiftQueue]; + return; + } + + GiftReceiveInfoModel *giftInfo = self.giftQueue.firstObject; + [self distributeGiftAnimation:giftInfo]; + [self.giftQueue removeObjectAtIndex:0]; +} + +- (void)distributeGiftAnimation:(GiftReceiveInfoModel *)giftInfo { + NSArray *targetUids = [self resolveTargetUids:giftInfo]; + CGPoint startPoint = [self calculateAnimationPoint:giftInfo.uid isEndPoint:NO]; + + BOOL isComboAnimation = [self shouldUseComboAnimationForSender:giftInfo.uid]; + NSTimeInterval delay = isComboAnimation ? self.comboAnimationDelay : self.standardAnimationDelay; + + for (NSString *targetUid in targetUids) { + CGPoint endPoint = [self calculateAnimationPoint:targetUid isEndPoint:YES]; + [self scheduleAnimationWithDelay:delay + giftInfo:giftInfo.gift + startPoint:startPoint + endPoint:endPoint + isComboAnimation:isComboAnimation]; + } +} + +- (NSArray *)resolveTargetUids:(GiftReceiveInfoModel *)giftInfo { + if (giftInfo.isLuckyBagGift) { + return @[giftInfo.targetUid]; + } + + if (giftInfo.targetUids.count > 0) { + return [giftInfo.targetUids valueForKey:@"stringValue"]; + } + + if (giftInfo.targetUsers) { + NSArray *uidDatas = [self ensureArrayContainsOnlyStrings:[giftInfo.targetUsers valueForKeyPath:@"uid"]]; + + return uidDatas;//[giftInfo.targetUsers valueForKeyPath:@"uid"]; + } + + return [NSString isEmpty:giftInfo.targetUid] ? @[] : @[giftInfo.targetUid]; +} + +- (NSArray *)ensureArrayContainsOnlyStrings:(NSArray *)inputArray { + // 用于存放最终结果 + NSMutableArray *resultArray = [NSMutableArray array]; + + for (id item in inputArray) { + if ([item isKindOfClass:[NSString class]]) { + // 如果是 NSString,直接添加到结果数组 + [resultArray addObject:item]; + } else if ([item isKindOfClass:[NSNumber class]]) { + // 如果是 NSNumber,转换为 NSString 后添加 + [resultArray addObject:[item stringValue]]; + } else { + // 对于非 NSString 或 NSNumber 的类型,可以选择忽略或者抛出异常 + NSLog(@"Warning: Unsupported item type: %@", [item class]); + } + } + + return [resultArray copy]; // 返回不可变数组 +} + +- (CGPoint)calculateAnimationPoint:(NSString *)uid isEndPoint:(BOOL)isEndPoint { + CGPoint point = [self.delegate animationPointAtStageViewByUid:uid]; + + if (point.x <= 0 || point.y <= 0) { + point = [self fallbackPointForEndPoint:isEndPoint]; + } + + if ([self shouldUseComboAnimationForSender:uid] && !isEndPoint) { + point = [self comboAnimationStartPoint]; + } + + return point; +} + +- (void)scheduleAnimationWithDelay:(NSTimeInterval)delay + giftInfo:(GiftInfoModel *)giftInfo + startPoint:(CGPoint)startPoint + endPoint:(CGPoint)endPoint + isComboAnimation:(BOOL)isComboAnimation { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + [self.animationHelper beginGiftAnimation:giftInfo.giftUrl + startPoint:startPoint + endPoint:endPoint + isGiftCombing:isComboAnimation + toTargetView:self.containerView]; + }); +} + +- (void)stopGiftQueue { + if (self.giftTimer) { + dispatch_source_cancel(self.giftTimer); + self.giftTimer = nil; + } +} + +- (void)enqueueGift:(GiftReceiveInfoModel *)giftInfo { + [self.giftQueue addObject:giftInfo]; + [self startGiftQueue]; +} + +// Helper methods +- (BOOL)shouldUseComboAnimationForSender:(NSString *)uid { + return [[GiftComboManager sharedManager] isGiftCombing] && + [uid isEqualToString:[AccountInfoStorage instance].getUid]; +} + +- (CGPoint)fallbackPointForEndPoint:(BOOL)isEndPoint { + CGFloat x = [UIScreen mainScreen].bounds.size.width / 2; + if (isEndPoint) { + x += 30; + } + return CGPointMake(x, 44 + kSafeAreaTopHeight); +} + +- (CGPoint)comboAnimationStartPoint { + if (isMSRTL()) { + return CGPointMake(kGetScaleWidth(86), + [UIScreen mainScreen].bounds.size.height - kSafeAreaTopHeight - kGetScaleWidth(140)); + } + + CGFloat x = KScreenWidth <= 375 ? + [UIScreen mainScreen].bounds.size.width - kGetScaleWidth(86) : + [UIScreen mainScreen].bounds.size.width - kGetScaleWidth(120); + + return CGPointMake(x, + [UIScreen mainScreen].bounds.size.height - kSafeAreaBottomHeight - kGetScaleWidth(140)); +} + +@end diff --git a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningBannerView.m b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningBannerView.m index bbf251c6..bf0c2773 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningBannerView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningBannerView.m @@ -13,36 +13,40 @@ #import "XPRoomViewController.h" #import "XCCurrentVCStackManager.h" +// Constants +static const CGFloat kBannerWidth = 346.5f; +static const CGFloat kBannerHeight = 82.5f; +static const CGFloat kBannerTopMargin = 80.0f; +static const CGFloat kAvatarSize = 43.0f; +static const CGFloat kAnimationDuration = 0.25f; +static const CGFloat kDisplayDuration = 2.5f; +static const CGFloat kRoomTransitionDelay = 2.0f; + @interface LuckyGiftWinningBannerViewModel : PIBaseModel -// 会命中混淆,model 单独出来 -@property (nonatomic, copy) NSString *times; -@property (nonatomic, copy) NSString *avatar; -@property (nonatomic, copy) NSString *coins; -@property (nonatomic, copy) NSDictionary *giftNameMap; -@property (nonatomic, copy) NSString *roomUid; - -- (NSString *)giftName; +@property (nonatomic, copy, readonly) NSString *times; +@property (nonatomic, copy, readonly) NSString *avatar; +@property (nonatomic, copy, readonly) NSString *coins; +@property (nonatomic, copy, readonly) NSDictionary *giftNameMap; +@property (nonatomic, copy, readonly) NSString *roomUid; +@property (nonatomic, copy, readonly) NSString *giftName; @end - @implementation LuckyGiftWinningBannerViewModel + (NSDictionary *)replacedKeyFromPropertyName { - return @{ - @"avatar":@"sender.avatar" - }; + return @{@"avatar": @"sender.avatar"}; } - (NSString *)giftName { - if ([self.giftNameMap.allKeys containsObject:@"ar"] && isMSRTL()) { + if (isMSRTL() && [self.giftNameMap[@"ar"] length]) { return self.giftNameMap[@"ar"]; - } else if ([self.giftNameMap.allKeys containsObject:@"zh"] && isMSZH()) { - return self.giftNameMap[@"zh"]; - } else { - return self.giftNameMap[@"en"]; } + if (isMSZH() && [self.giftNameMap[@"zh"] length]) { + return self.giftNameMap[@"zh"]; + } + return self.giftNameMap[@"en"] ?: @""; } @end @@ -52,13 +56,11 @@ @property (nonatomic, strong) LuckyGiftWinningBannerViewModel *model; @property (nonatomic, strong) UIImageView *backgroundImageView; @property (nonatomic, strong) UIImageView *ballImageView; -@property (nonatomic, strong) NetImageView *avatarImage; +@property (nonatomic, strong) NetImageView *avatarImageView; @property (nonatomic, strong) UILabel *giftNameLabel; @property (nonatomic, strong) UILabel *timesLabel; @property (nonatomic, strong) UILabel *coinsLabel; - @property (nonatomic, assign) NSInteger currentRoomUid; - @property (nonatomic, copy) void(^completeDisplay)(void); @property (nonatomic, copy) void(^exitCurrentRoom)(void); @@ -71,39 +73,66 @@ with:(AttachmentModel *)attachment complete:(void(^)(void))complete exitCurrentRoom:(void(^)(void))exit { - +#if DEBUG + NSParameterAssert(superView); + NSParameterAssert(attachment); +#else + if (!superView || !attachment) { + if (complete) { + complete(); + } + return; + } +#endif LuckyGiftWinningBannerViewModel *model = [LuckyGiftWinningBannerViewModel modelWithDictionary:attachment.data]; - CGFloat width = kGetScaleWidth(346.5); - CGFloat height = kGetScaleWidth(82.5); - LuckyGiftWinningBannerView *bannerView = [[LuckyGiftWinningBannerView alloc] initWithFrame:CGRectMake(KScreenWidth, 80, width, height)]; + CGFloat width = kGetScaleWidth(kBannerWidth); + CGFloat height = kGetScaleWidth(kBannerHeight); + CGRect frame = CGRectMake(KScreenWidth, kBannerTopMargin, width, height); + + LuckyGiftWinningBannerView *bannerView = [[LuckyGiftWinningBannerView alloc] initWithFrame:frame]; bannerView.model = model; bannerView.completeDisplay = complete; bannerView.exitCurrentRoom = exit; bannerView.currentRoomUid = roomUid; [superView addSubview:bannerView]; - @kWeakify(bannerView); - [UIView animateWithDuration:0.25 animations:^{ - bannerView.center = CGPointMake(superView.center.x, height/2 + 80); + [bannerView animateDisplay]; +} + +- (void)animateDisplay { + @kWeakify(self); + [UIView animateWithDuration:kAnimationDuration animations:^{ + @kStrongify(self); + self.center = CGPointMake(self.superview.center.x, self.frame.size.height/2 + kBannerTopMargin); } completion:^(BOOL finished) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ - bannerView.frame = CGRectMake(-KScreenWidth, 80, width, height); - } completion:^(BOOL finished) { - @kStrongify(bannerView); - [bannerView removeFromSuperview]; - if (bannerView.completeDisplay) { - bannerView.completeDisplay(); - } - }]; + @kStrongify(self); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDisplayDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self animateDisappear]; }); }]; } +- (void)animateDisappear { + @kWeakify(self); + [UIView animateWithDuration:kAnimationDuration + delay:0 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + @kStrongify(self); + self.frame = CGRectMake(-KScreenWidth, kBannerTopMargin, self.frame.size.width, self.frame.size.height); + } completion:^(BOOL finished) { + @kStrongify(self); + [self removeFromSuperview]; + if (self.completeDisplay) { + self.completeDisplay(); + } + }]; +} + - (void)setModel:(LuckyGiftWinningBannerViewModel *)model { _model = model; - self.avatarImage.imageUrl = model.avatar; + self.avatarImageView.imageUrl = model.avatar; self.giftNameLabel.text = [model giftName]; self.timesLabel.text = model.times; self.coinsLabel.text = [NSString formatNumberToKOrM:model.coins]; @@ -127,13 +156,13 @@ exitCurrentRoom:(void(^)(void))exit { return; } - __block NSString *targetRoomUid = self.model.roomUid; @kWeakify(self); [TTPopup alertWithMessage:YMLocalizedString(@"Combo_10") confirmHandler:^{ @kStrongify(self); if (self.exitCurrentRoom) { self.exitCurrentRoom(); } + NSString *targetRoomUid = self.model.roomUid; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [XPRoomViewController openRoom:targetRoomUid viewController:[XCCurrentVCStackManager shareManager].getCurrentVC]; @@ -153,8 +182,8 @@ exitCurrentRoom:(void(^)(void))exit { make.width.mas_equalTo(self.ballImageView.mas_height); }]; - [self addSubview:self.avatarImage]; - [self.avatarImage mas_makeConstraints:^(MASConstraintMaker *make) { + [self addSubview:self.avatarImageView]; + [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.mas_equalTo(kGetScaleWidth(8)); make.top.mas_equalTo(kGetScaleWidth(24)); make.width.height.mas_equalTo(kGetScaleWidth(43)); @@ -163,8 +192,8 @@ exitCurrentRoom:(void(^)(void))exit { UILabel *titleLabel_1 = [UILabel labelInitWithText:YMLocalizedString(@"Combo_0") font:kFontSemibold(14) textColor:[UIColor whiteColor]]; [self addSubview:titleLabel_1]; [titleLabel_1 mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.mas_equalTo(self.avatarImage).offset(2); - make.leading.mas_equalTo(self.avatarImage.mas_trailing).offset(10); + make.top.mas_equalTo(self.avatarImageView).offset(2); + make.leading.mas_equalTo(self.avatarImageView.mas_trailing).offset(10); }]; [self addSubview:self.giftNameLabel]; @@ -176,8 +205,8 @@ exitCurrentRoom:(void(^)(void))exit { UILabel *titleLabel_2 = [UILabel labelInitWithText:YMLocalizedString(@"Combo_4") font:kFontSemibold(14) textColor:[UIColor whiteColor]]; [self addSubview:titleLabel_2]; [titleLabel_2 mas_makeConstraints:^(MASConstraintMaker *make) { - make.bottom.mas_equalTo(self.avatarImage.mas_bottom).offset(-2); - make.leading.mas_equalTo(self.avatarImage.mas_trailing).offset(10); + make.bottom.mas_equalTo(self.avatarImageView.mas_bottom).offset(-2); + make.leading.mas_equalTo(self.avatarImageView.mas_trailing).offset(10); }]; [self addSubview:self.timesLabel]; @@ -226,19 +255,17 @@ exitCurrentRoom:(void(^)(void))exit { return _ballImageView; } -- (NetImageView *)avatarImage { - if (!_avatarImage) { +- (NetImageView *)avatarImageView { + if (!_avatarImageView) { NetImageConfig * config = [[NetImageConfig alloc]init]; config.placeHolder = [UIImageConstant defaultAvatarPlaceholder]; config.imageType = ImageTypeCornerAvatar; - _avatarImage = [[NetImageView alloc] initWithConfig:config]; - _avatarImage.backgroundColor = [UIColor clearColor]; - _avatarImage.contentMode = UIViewContentModeScaleAspectFill; - _avatarImage.layer.cornerRadius = kGetScaleWidth(43/2); - _avatarImage.layer.masksToBounds = YES; - _avatarImage.clipsToBounds = YES; + _avatarImageView = [[NetImageView alloc] initWithConfig:config]; + _avatarImageView.backgroundColor = [UIColor clearColor]; + _avatarImageView.contentMode = UIViewContentModeScaleAspectFill; + [_avatarImageView setCornerRadius:kGetScaleWidth(43/2)]; } - return _avatarImage; + return _avatarImageView; } - (UILabel *)giftNameLabel { diff --git a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m index 72e7f2f1..46c66ece 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/LuckyGiftWinningFlagView.m @@ -115,14 +115,6 @@ [stackView addArrangedSubview:self.coinIcon]; [stackView addArrangedSubview:self.winPriceLabel]; - [self.coinIcon mas_makeConstraints:^(MASConstraintMaker *make) { - make.size.mas_equalTo(CGSizeMake(21, 21)); - }]; - - [self.winPriceLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.height.mas_equalTo(35); - }]; - UIStackView *stackView_2 = [[UIStackView alloc] init]; stackView_2.spacing = 4; [self addSubview:stackView_2]; @@ -149,10 +141,6 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"receiveLuckGiftWinning" object:model.coins]; } -- (void)display { - -} - #pragma mark - - (UIImageView *)coinIcon { if (!_coinIcon) { diff --git a/YuMi/Modules/YMRoom/View/AnimationView/XPRoomAnimationView.m b/YuMi/Modules/YMRoom/View/AnimationView/XPRoomAnimationView.m index 10b2fa38..cde20348 100644 --- a/YuMi/Modules/YMRoom/View/AnimationView/XPRoomAnimationView.m +++ b/YuMi/Modules/YMRoom/View/AnimationView/XPRoomAnimationView.m @@ -79,6 +79,9 @@ #import "GameUniversalBannerView.h" #import "RoomHighValueGiftBannerAnimation.h" +#import "GiftAnimationHelper.h" +#import "GiftAnimationManager.h" + @interface XPRoomAnimationView ()< SVGAPlayerDelegate, NIMBroadcastManagerDelegate, @@ -90,7 +93,9 @@ XPRoomStarKitchenBannerViewDelegate, PAGViewListener, XPRoomAnchorRankBannerViewDelegate, PIRoomGiftBroadcastWindowDelegate, -PIUniversalBannerViewDelegate> +PIUniversalBannerViewDelegate, +GiftAnimationDelegate> + ///展示的不同层级 ///最底层的 @property (nonatomic,strong) XPRoomAnimationHitView * lowLevelView; @@ -116,24 +121,17 @@ PIUniversalBannerViewDelegate> ///代理 @property (nonatomic,weak) iddelegate; -///复用池 -@property (strong,nonatomic)NSMutableSet * bannerDequePool; -///可见池 -@property (strong,nonatomic)NSMutableSet * bannerVisiablePool; ///礼物定时器 6s刷新一次 @property (nonatomic,strong)dispatch_source_t giftEffectTimer; #pragma mark - 送礼物的动画的 ///礼物的队列 -@property (nonatomic,strong) NSMutableArray *giftQueue; -///定时器 -@property (nonatomic ,strong)dispatch_source_t giftTimer; -///复用池 应为送礼物的动画是一个很频繁的事情 所以可以减少内存的消耗 -@property (nonatomic,strong) NSMutableSet * giftReuseArray; -///可见池 -@property (nonatomic,strong) NSMutableSet * giftVisibleArray; - +//@property (nonatomic,strong) NSMutableArray *giftQueue; +/////定时器 +//@property (nonatomic ,strong)dispatch_source_t giftTimer; +@property(nonatomic, strong) GiftAnimationHelper *giftAnimationHelper; +@property(nonatomic, strong) GiftAnimationManager *giftAnimationManager; ///大礼物是否正在播放 @property (nonatomic,assign) BOOL isLargeGiftAnimating; #pragma mark - 进房座驾动画的 @@ -205,10 +203,10 @@ PIUniversalBannerViewDelegate> self.giftEffectTimer = nil; } - if(self.giftTimer != nil){ - dispatch_source_cancel(self.giftTimer); - self.giftTimer = nil; - } +// if(self.giftTimer != nil){ +// dispatch_source_cancel(self.giftTimer); +// self.giftTimer = nil; +// } [self.giftPagView removeListener:self]; } - (instancetype)initWithDelegate:(id)delegate { @@ -216,9 +214,14 @@ PIUniversalBannerViewDelegate> if (self) { [[NIMSDK sharedSDK].broadcastManager addDelegate:self]; self.delegate = delegate; + [self initSubViews]; [self initSubViewConstraints]; + self.giftAnimationHelper = [[GiftAnimationHelper alloc] init]; + self.giftAnimationManager = [[GiftAnimationManager alloc] initWithContainerView:self.lowLevelView]; + self.giftAnimationManager.delegate = self; + _roomEffectModelsQueueV2 = [NSMutableArray array]; _GiftDynamicEffectListPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) xpSafeObjectAtIndex:0] stringByAppendingPathComponent:@"GiftDynamicEffectList"]; @@ -336,7 +339,11 @@ PIUniversalBannerViewDelegate> receiveInfo.isBatch = (attachment.second == Custom_Message_Sub_AllBatchSend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend); receiveInfo.isComboBatch = attachment.second == Custom_Message_Sub_AllMicroSend; - [self receiveGiftHandleSendGiftAnimation:attachment]; + if (receiveInfo.gift.notifyFull && attachment.second == Custom_Message_Sub_Gift_Send) { +// return; + } else { + [self receiveGiftHandleSendGiftAnimation:attachment]; + } if (receiveInfo.isLuckyBagGift) { if (!self.delegate.getRoomInfo.hasAnimationEffect) {return;} @@ -1662,10 +1669,11 @@ PIUniversalBannerViewDelegate> ///在最外面判断是否可以拿到那个礼物 后面使用就不用判断了 receiveInfo.gift = giftInfo; receiveInfo.isLuckyBagGift = (attachment.second == Custom_Message_Sub_AllMicroLuckySend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend || attachment.second == Custom_Message_Sub_Gift_LuckySend); - [self.giftQueue addObject:receiveInfo]; - if (self.giftTimer == nil && self.giftQueue > 0) { - [self startGiftQueueTimer]; - } + [self.giftAnimationManager enqueueGift:receiveInfo]; +// [self.giftQueue addObject:receiveInfo]; +// if (self.giftTimer == nil && self.giftQueue > 0) { +// [self startGiftQueueTimer]; +// } } #pragma mark - 收到礼物展示动画 @@ -1693,260 +1701,38 @@ PIUniversalBannerViewDelegate> ///在最外面判断是否可以拿到那个礼物 后面使用就不用判断了 receiveInfo.gift = giftInfo; receiveInfo.isLuckyBagGift = (attachment.second == Custom_Message_Sub_AllMicroLuckySend || attachment.second == Custom_Message_Sub_AllBatchMicroLuckySend || attachment.second == Custom_Message_Sub_Gift_LuckySend); - [self.giftQueue addObject:receiveInfo]; - if (self.giftTimer == nil && self.giftQueue > 0) { - [self startGiftQueueTimer]; - } + [self.giftAnimationManager enqueueGift:receiveInfo]; +// [self.giftQueue addObject:receiveInfo]; +// if (self.giftTimer == nil && self.giftQueue > 0) { +// [self startGiftQueueTimer]; +// } } //扫描礼物队列 -- (void)startGiftQueueTimer { - NSTimeInterval period = 0.5; //设置时间间隔一个礼物动画的时间 - dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0)); - dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC); - dispatch_source_set_event_handler(_timer, ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.giftQueue.count) { - GiftReceiveInfoModel * receiveInfo = self.giftQueue.firstObject; - [self handoutGiftDistributeAnimation:receiveInfo]; - [self.giftQueue removeObject:receiveInfo]; - }else { - dispatch_source_cancel(_timer); - self.giftTimer = nil; - } - }); - }); - dispatch_resume(_timer); - self.giftTimer = _timer; -} +//- (void)startGiftQueueTimer { +// static NSTimeInterval period = 0.5; //设置时间间隔一个礼物动画的时间 +// dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0)); +// dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC); +// dispatch_source_set_event_handler(_timer, ^{ +// dispatch_async(dispatch_get_main_queue(), ^{ +// if (self.giftQueue.count) { +// GiftReceiveInfoModel * receiveInfo = self.giftQueue.firstObject; +//// [self handoutGiftDistributeAnimation:receiveInfo]; +// [self.giftAnimationManager enqueueGift:receiveInfo]; +// [self.giftQueue removeObject:receiveInfo]; +// }else { +// dispatch_source_cancel(_timer); +// self.giftTimer = nil; +// } +// }); +// }); +// dispatch_resume(_timer); +// self.giftTimer = _timer; +//} -///处理礼物的分发动画 -- (void)handoutGiftDistributeAnimation:(GiftReceiveInfoModel *)receiveInfo { - GiftInfoModel * giftInfo = receiveInfo.gift; - NSString * sendUid = receiveInfo.uid; - CGPoint starPoint = [self getGiftAnimationPoint:sendUid isEnd:NO]; - CGPoint endPoint; - NSArray * targetUids; - if (receiveInfo.isLuckyBagGift) { - targetUids = @[receiveInfo.targetUid]; - } else { - targetUids = receiveInfo.targetUids; - if (targetUids.count <= 0) { - if (receiveInfo.targetUsers) { - targetUids = [receiveInfo.targetUsers valueForKeyPath:@"uid"]; - } - if (targetUids.count <=0) { - targetUids = [NSString isEmpty:receiveInfo.targetUid] ? @[] : @[receiveInfo.targetUid]; - } - } - } - -#if DEBUG - for (NSInteger i=0;i<15;i++) { - endPoint = [self.delegate animationPointAtStageViewByIndex:i]; - BOOL isCombing = [[GiftComboManager sharedManager] isGiftCombing] && [sendUid isEqualToString:[AccountInfoStorage instance].getUid]; - CGFloat time = isCombing ? 0.25 : 0.5; - dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, time * NSEC_PER_SEC); - dispatch_after(timer, dispatch_get_main_queue(), ^{ - [self beginGiftAnimation:giftInfo.giftUrl - startPoint:starPoint - endPoint:endPoint - isGiftCombing:isCombing]; - }); - } -#else - for (NSString *targetUid in targetUids) { - NSString * userId = targetUid; - if ([userId isKindOfClass:[NSNumber class]]) { - userId = ((NSNumber *)userId).stringValue; - } - endPoint = [self getGiftAnimationPoint:userId isEnd:YES]; - dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC); - - BOOL isCombing = [[GiftComboManager sharedManager] isGiftCombing] && [sendUid isEqualToString:[AccountInfoStorage instance].getUid]; - - if (isCombing) { - timer = dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC); - } - dispatch_after(timer, dispatch_get_main_queue(), ^{ - [self beginGiftAnimation:giftInfo.giftUrl - startPoint:starPoint - endPoint:endPoint - isGiftCombing:isCombing]; - }); - } -#endif - - -} - -///起点 -- (CGPoint)getGiftAnimationPoint:(NSString *)uid isEnd:(BOOL)isEnd{ - CGPoint point = [self.delegate animationPointAtStageViewByUid:uid]; - //没有找到那个点 那个用户不再坑位上 - if (point.x <= 0 || point.y <= 0) { - if (isEnd) { - point = CGPointMake([UIScreen mainScreen].bounds.size.width /2 + 30, 44 + kSafeAreaTopHeight); - } else { - point = CGPointMake([UIScreen mainScreen].bounds.size.width / 2, 44 + kSafeAreaTopHeight); - } - } - - if ([[GiftComboManager sharedManager] isGiftCombing] && - [uid isEqualToString:[AccountInfoStorage instance].getUid] && - isEnd == false) { - if (isMSRTL()) { - point = CGPointMake(kGetScaleWidth(86), [UIScreen mainScreen].bounds.size.height - kSafeAreaTopHeight - kGetScaleWidth(140)); - } else { - if (KScreenWidth <= 375) { - point = CGPointMake(kGetScaleWidth([UIScreen mainScreen].bounds.size.width - kGetScaleWidth(86)), [UIScreen mainScreen].bounds.size.height - kSafeAreaBottomHeight - kGetScaleWidth(140)); - } else { - point = CGPointMake(kGetScaleWidth([UIScreen mainScreen].bounds.size.width - kGetScaleWidth(150)), [UIScreen mainScreen].bounds.size.height - kSafeAreaBottomHeight - kGetScaleWidth(140)); - } - } - } - - return point; -} - -- (void)beginGiftAnimation:(NSString *)giftUrl - startPoint:(CGPoint)startPoint - endPoint:(CGPoint)endPoint - isGiftCombing:(BOOL)isGiftCombing { - NetImageView * giftImageView = [self.giftReuseArray anyObject]; - if (giftImageView == nil) { - giftImageView = [[NetImageView alloc]initWithFrame:CGRectMake(0, 0 , 55, 55)]; - giftImageView.center = startPoint; - giftImageView.alpha = 1; - giftImageView.layer.anchorPoint = CGPointMake(0.5, 0.5); - giftImageView.imageUrl = giftUrl; - [self.giftVisibleArray addObject:giftImageView]; - }else { - [self.giftReuseArray removeObject:giftImageView]; - } - giftImageView.hidden = NO; - giftImageView.imageUrl = giftUrl; - [self.lowLevelView addSubview:giftImageView]; - - CGFloat delay = 3.2; - CAAnimationGroup *group = [self createGiftAnimationStartPoint:startPoint endPoint:endPoint]; - if (isGiftCombing) { - delay = 1.0; - group = [self createGiftComboAnimationStartPoint:startPoint endPoint:endPoint]; - } - // 确保视图在动画开始前设置到 startPoint 位置 - giftImageView.layer.position = startPoint; - [giftImageView.layer addAnimation:group forKey:@"giftDisplayViewAnimation"]; - [self performSelector:@selector(aniationDidFinish:) withObject:giftImageView afterDelay:(delay+0.25)]; -} - -- (CAAnimationGroup *)createGiftComboAnimationStartPoint:(CGPoint)startPoint - endPoint:(CGPoint)endPoint { - // 缩放动画1: 动画开始时缩放至 0.4 - CAKeyframeAnimation *scaleAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; - scaleAnimation1.duration = 0.1; - scaleAnimation1.values = @[@1.0, @0.4]; - scaleAnimation1.repeatCount = 1; - scaleAnimation1.calculationMode = kCAAnimationCubic; - scaleAnimation1.removedOnCompletion = NO; - scaleAnimation1.fillMode = kCAFillModeForwards; - - // 位移动画: 0.5秒内从起点移动到目标点 - CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; - positionAnimation.duration = 0.5; - positionAnimation.beginTime = 0.1; // 缩放结束后开始位移 - positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - positionAnimation.values = @[[NSValue valueWithCGPoint:startPoint], [NSValue valueWithCGPoint:endPoint]]; - positionAnimation.repeatCount = 1; - positionAnimation.removedOnCompletion = NO; - positionAnimation.fillMode = kCAFillModeForwards; - - // 缩放动画2: 在位移的过程中逐渐变大到 1.2 - CAKeyframeAnimation *scaleAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; - scaleAnimation2.duration = 0.5; - scaleAnimation2.beginTime = 0.1; // 同时与位移动画进行 - scaleAnimation2.values = @[@0.4, @2]; - scaleAnimation2.repeatCount = 1; - scaleAnimation2.calculationMode = kCAAnimationCubic; - scaleAnimation2.removedOnCompletion = NO; - scaleAnimation2.fillMode = kCAFillModeForwards; - - // 创建动画组 - CAAnimationGroup *group = [CAAnimationGroup animation]; - group.duration = 0.6; // 总时间为 0.1 (缩放) + 0.5 (位移与缩放) - group.animations = @[scaleAnimation1, positionAnimation, scaleAnimation2]; - group.repeatCount = 1; - group.removedOnCompletion = NO; - group.fillMode = kCAFillModeForwards; - - return group; -} - -/// 图片运动的动画组 -/// @param startPoint 开始的点 -/// @param endPoint 结束的点 -- (CAAnimationGroup *)createGiftAnimationStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint { - CGPoint centerPoint = CGPointMake(KScreenWidth / 2, KScreenHeight / 2); - CAKeyframeAnimation *animation0 = [CAKeyframeAnimation animation]; - animation0.duration = 0.8; - animation0.keyPath = @"transform.scale"; - animation0.values = @[@1.0,@1.5,@2.0,@1.5]; - animation0.repeatCount = 1; - animation0.calculationMode = kCAAnimationCubic; - animation0.removedOnCompletion = NO; - animation0.fillMode = kCAFillModeForwards; - - CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation]; - animation1.duration = 0.8; - animation1.beginTime = 0.8; - animation1.keyPath = @"transform.scale"; - animation1.values = @[@1.5,@2.0,@2.5,@3.0]; - animation1.repeatCount = 1; - animation1.calculationMode = kCAAnimationCubic; - animation1.removedOnCompletion = NO; - animation1.fillMode = kCAFillModeForwards; - - CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animation]; - animation2.duration = 0.8; - animation2.beginTime = 0.8; - animation2.keyPath = @"position"; - animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];; - animation2.values = @[[NSValue valueWithCGPoint:startPoint],[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)]]; - animation2.repeatCount = 1; - animation2.removedOnCompletion = NO; - animation2.fillMode = kCAFillModeForwards; - - CAKeyframeAnimation *animation3 = [CAKeyframeAnimation animation]; - animation3.duration = 0.8; - animation3.beginTime = 2.6;//0.8+0.8+1 - animation3.keyPath = @"transform.scale"; - animation3.values = @[@3,@2.5,@2,@1.5,@1]; - animation3.repeatCount = 1; - - CAKeyframeAnimation *animation4 = [CAKeyframeAnimation animation]; - animation4.duration = 0.8; - animation4.beginTime = 2.6; - animation4.keyPath = @"position"; - animation4.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; - animation4.values = @[[NSValue valueWithCGPoint:CGPointMake(centerPoint.x ,centerPoint.y)],[NSValue valueWithCGPoint:endPoint]]; - animation4.repeatCount = 1; - - CAAnimationGroup *group = [CAAnimationGroup animation]; - group.duration = 3.2; - group.animations = @[animation0,animation1,animation2, animation3,animation4]; - group.repeatCount = 1; - group.removedOnCompletion = NO; - group.fillMode = kCAFillModeForwards; - - return group; -} - -- (void)aniationDidFinish:(NetImageView *)giftImageView{ - [giftImageView removeFromSuperview]; - giftImageView.image = nil; - giftImageView.hidden = YES; - [self.giftVisibleArray removeObject:giftImageView]; - [self.giftReuseArray addObject:giftImageView]; +#pragma mark - GiftAnimationManagerDelegate +- (CGPoint)animationPointAtStageViewByUid:(NSString *)uid { + return [self.delegate animationPointAtStageViewByUid:uid]; } #pragma mark - 全服礼物 飘屏 @@ -1957,7 +1743,9 @@ PIUniversalBannerViewDelegate> if (giftNotifyInfo.ceremonyGift)return; // if(giftNotifyInfo.isHomeShow == YES)return; ///房间外飘屏 在tabbar中监听 // if(giftNotifyInfo.roomUid == self.delegate.getRoomInfo.uid)return; ///自己的房间跨房间飘屏不用走 - if (self.animationListA.count == 0 && self.isPlayOfB == NO && self.isPlayOfA == NO) { + if (self.animationListA.count == 0 && + self.isPlayOfB == NO && + self.isPlayOfA == NO) { [self createGiftBroadcastViewAnimation:giftNotifyInfo]; } [self.animationListA addObject:giftNotifyInfo]; @@ -2057,8 +1845,6 @@ PIUniversalBannerViewDelegate> [self.animationListB addObject:prizeModel]; } - - - (void)createBigPrizeAnimation:(PIBaseAnimationViewModel *)prizeModel { self.isPlayOfB = YES; @@ -2094,11 +1880,6 @@ PIUniversalBannerViewDelegate> [self.animationListB removeObjectAtIndex:0]; } self.isPlayOfB = NO; - // if(self.isAnimationListAFinish == YES){ - // self.isAnimationListAFinish = NO; - // [self playAnimationWithModel]; - // return; - // } [self playAnimationWithModel]; } }]; @@ -3070,19 +2851,6 @@ PIUniversalBannerViewDelegate> return _parser; } -- (NSMutableSet *)bannerDequePool { - if (!_bannerDequePool) { - _bannerDequePool = [NSMutableSet set]; - } - return _bannerDequePool; -} -- (NSMutableSet *)bannerVisiablePool { - if (!_bannerVisiablePool) { - _bannerVisiablePool = [NSMutableSet set]; - } - return _bannerVisiablePool; -} - - (XPRoomAnimationHitView *)lowLevelView { if (!_lowLevelView) { _lowLevelView = [[XPRoomAnimationHitView alloc] init]; @@ -3110,26 +2878,12 @@ PIUniversalBannerViewDelegate> return _highLevleView; } -- (NSMutableArray *)giftQueue { - if (!_giftQueue) { - _giftQueue = [NSMutableArray array]; - } - return _giftQueue; -} - -- (NSMutableSet *)giftReuseArray { - if (!_giftReuseArray) { - _giftReuseArray = [NSMutableSet set]; - } - return _giftReuseArray; -} - -- (NSMutableSet *)giftVisibleArray { - if (!_giftVisibleArray) { - _giftVisibleArray = [NSMutableSet set]; - } - return _giftVisibleArray; -} +//- (NSMutableArray *)giftQueue { +// if (!_giftQueue) { +// _giftQueue = [NSMutableArray array]; +// } +// return _giftQueue; +//} - (NSMutableArray *)carEffectQueue { if (_carEffectQueue == nil) { diff --git a/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboFlagView.m b/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboFlagView.m index 7c46959f..e3a47a42 100644 --- a/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboFlagView.m +++ b/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboFlagView.m @@ -55,30 +55,64 @@ #pragma mark - 更新 UI - (void)updateUIWithModel:(GiftReceiveInfoModel *)model { - if ([NSString isEmpty:self.cacheIcon]) { - self.cacheIcon = model.gift.giftUrl; - } else { - if ([self.cacheIcon isEqualToString:model.gift.giftUrl]) { - self.cacheIcon = model.gift.giftUrl; - self.giftIcon.imageUrl = model.gift.giftUrl; - } - } - if ([NSString isEmpty:self.cacheAvatar]) { - self.cacheIcon = model.avatar; - self.giftGiverAvatar.imageUrl = model.avatar; - } else { - if (![self.cacheAvatar isEqualToString:model.avatar]) { - self.cacheIcon = model.avatar; - self.giftGiverAvatar.imageUrl = model.avatar; - } - } + // 更新礼物图标 + @kWeakify(self); + [self updateCacheForProperty:self.cacheIcon newValue:model.gift.giftUrl updateBlock:^(NSString *value) { + @kStrongify(self); + self.giftIcon.imageUrl = value; + }]; + + // 更新头像 + [self updateCacheForProperty:self.cacheAvatar newValue:model.avatar updateBlock:^(NSString *value) { + @kStrongify(self); + self.giftGiverAvatar.imageUrl = value; + }]; + + // 更新名字 self.giftGiverNameLabel.text = model.nick; - + + // 更新接收者名称 self.giftReceiverNameLabel.text = [self receiverNameForModel:model]; - + + // 更新礼物计数 NSInteger total = model.comboCount * model.giftNum * model.receiveUserCount; NSString *countStr = [NSString stringWithFormat:@"x%ld", (long)total]; self.giftCountLabel.attributedText = [self generateAttributedStringForCount:countStr]; + +// if ([NSString isEmpty:self.cacheIcon]) { +// self.cacheIcon = model.gift.giftUrl; +// } else { +// if (![self.cacheIcon isEqualToString:model.gift.giftUrl]) { +// self.cacheIcon = model.gift.giftUrl; +// self.giftIcon.imageUrl = model.gift.giftUrl; +// } +// } +// if ([NSString isEmpty:self.cacheAvatar]) { +// self.cacheAvatar = model.avatar; +// self.giftGiverAvatar.imageUrl = model.avatar; +// } else { +// if (![self.cacheAvatar isEqualToString:model.avatar]) { +// self.cacheIcon = model.avatar; +// self.giftGiverAvatar.imageUrl = model.avatar; +// } +// } +// self.giftGiverNameLabel.text = model.nick; +// +// self.giftReceiverNameLabel.text = [self receiverNameForModel:model]; +// +// NSInteger total = model.comboCount * model.giftNum * model.receiveUserCount; +// NSString *countStr = [NSString stringWithFormat:@"x%ld", (long)total]; +// self.giftCountLabel.attributedText = [self generateAttributedStringForCount:countStr]; +} + +- (void)updateCacheForProperty:(NSString *)cacheProperty newValue:(NSString *)newValue updateBlock:(void (^)(NSString *value))updateBlock { + if ([NSString isEmpty:cacheProperty]) { + cacheProperty = newValue; + updateBlock(newValue); + } else if (![newValue isEqualToString:cacheProperty]) { + cacheProperty = newValue; + updateBlock(newValue); + } } - (NSMutableAttributedString *)generateAttributedStringForCount:(NSString *)countStr { @@ -190,7 +224,9 @@ [self addSubview:self.giftIcon]; [self.giftIcon mas_makeConstraints:^(MASConstraintMaker *make) { - make.leading.mas_equalTo(self.giftReceiverNameLabel.mas_trailing).offset(8); +// make.leading.mas_equalTo(self.giftGiverNameLabel.mas_trailing).offset(8); + make.leading.greaterThanOrEqualTo(self.giftGiverNameLabel.mas_trailing).offset(8); + make.leading.greaterThanOrEqualTo(self.giftReceiverNameLabel.mas_trailing).offset(8); make.bottom.mas_equalTo(self.backgroundImageView); make.size.mas_equalTo(CGSizeMake(kGetScaleWidth(48), kGetScaleWidth(48))); }]; diff --git a/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboView.m b/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboView.m index 5ce502c6..333c2ac6 100644 --- a/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboView.m +++ b/YuMi/Modules/YMRoom/View/SendGiftView/View/GiftComboView.m @@ -18,6 +18,7 @@ @property (nonatomic, strong) UILabel *comboCountLabel; @property(nonatomic, strong) UILabel *comboGoldLabel; @property(nonatomic, strong) UILabel *updateGoldLabel; +@property(nonatomic, strong) UILabel *winPriceGoldLabel; @property (nonatomic, strong) SVGAImageView *playImageView; @property (nonatomic, strong) SVGAVideoEntity *svgaVideoEntity; @property (nonatomic, strong) CountdownRingView *countdownRingView; @@ -25,6 +26,8 @@ @property(nonatomic, strong) NSMutableArray *updateGoldQueue; @property(nonatomic, assign) BOOL isAnimatingUpdateGold; +@property(nonatomic, strong) UIImpactFeedbackGenerator *f; + @end @implementation GiftComboView @@ -39,6 +42,7 @@ - (instancetype)init { if (self = [super init]) { + self.f = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy]; self.updateGoldQueue = @[].mutableCopy; [self setupNotification]; @@ -91,10 +95,6 @@ double price = [[dic objectForKey:@"Price"] doubleValue]; BOOL isFromWinning = [[dic objectForKey:@"isFromWinning"] boolValue]; [self updateCurrentGold:coin giftPrice:price isFromWinning:isFromWinning]; -// [[NSNotificationCenter defaultCenter] postNotificationName:@"receiveLuckGiftWinning" -// object:@{@"CurrentGold": receive.userPurse.diamonds, -// @"Price": @(receive.gift.goldPrice), -// @"isFromWinning":@(NO)}]; } }]; } @@ -175,8 +175,14 @@ [self addSubview:self.updateGoldLabel]; [self.updateGoldLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.centerX.mas_equalTo(bg); - make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(0); + make.trailing.mas_equalTo(self.comboGoldLabel); + make.bottom.mas_equalTo(self.comboGoldLabel); + }]; + + [self addSubview:self.winPriceGoldLabel]; + [self.winPriceGoldLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.trailing.mas_equalTo(self.comboGoldLabel); + make.bottom.mas_equalTo(self.comboGoldLabel); }]; } @@ -207,7 +213,9 @@ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @kStrongify(self); // 1521 连续震动 3 次 - AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +// AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +// AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)); + [self.f impactOccurred]; [[GiftComboManager sharedManager] sendGift]; [self.playImageView startAnimation]; @@ -231,39 +239,111 @@ } - (void)updateCurrentGold:(double)gold giftPrice:(double)price isFromWinning:(BOOL)isFromWinning { - if (isFromWinning) { - [self.updateGoldQueue insertObject:[NSString stringWithFormat:@"+ %@", @(gold)] atIndex:0]; - } else { - [self.updateGoldQueue addObject:@(-price).stringValue]; - self.comboGoldLabel.text = @(gold).stringValue; - } +// if (isFromWinning) { +// [self.updateGoldQueue insertObject:[NSString stringWithFormat:@"+ %@", @(gold)] atIndex:0]; +// [self processNextUpdateGold]; +// } else { +// [self.updateGoldQueue addObject:@(-price).stringValue]; +// self.comboGoldLabel.text = @(gold).stringValue; +// if (!self.isAnimatingUpdateGold) { +// [self processNextUpdateGold]; +// } +// } - if (!self.isAnimatingUpdateGold) { + NSString *goldString = isFromWinning ? [NSString stringWithFormat:@"+ %@", @(gold)] : @(-price).stringValue; + if (isFromWinning) { + [self.updateGoldQueue insertObject:goldString atIndex:0]; [self processNextUpdateGold]; + } else { + [self.updateGoldQueue addObject:goldString]; + self.comboGoldLabel.text = @(gold).stringValue; + + if (!self.isAnimatingUpdateGold) { + [self processNextUpdateGold]; + } } + } - (void)processNextUpdateGold { +// if (self.updateGoldQueue.count == 0) { +// self.isAnimatingUpdateGold = NO; +// return; +// } +// +// NSNumber *goldNum = [self.updateGoldQueue xpSafeObjectAtIndex:0]; +// [self.updateGoldQueue removeObjectAtIndex:0]; +// +// self.isAnimatingUpdateGold = YES; +// +// if (goldNum.integerValue < 0) { +// self.updateGoldLabel.text = goldNum.stringValue; +// [self showGoldUpdateAnimation]; +// } else { +// self.winPriceGoldLabel.text = goldNum.stringValue; +// [self showGoldWinPriceAnimation]; +// } + if (self.updateGoldQueue.count == 0) { self.isAnimatingUpdateGold = NO; return; } - NSNumber *goldNum = [self.updateGoldQueue xpSafeObjectAtIndex:0]; + NSString *goldString = [self.updateGoldQueue xpSafeObjectAtIndex:0]; [self.updateGoldQueue removeObjectAtIndex:0]; self.isAnimatingUpdateGold = YES; - if (goldNum.integerValue < 0) { - self.updateGoldLabel.textColor = UIColorFromRGB(0xffd54c); - } else { - self.updateGoldLabel.textColor = UIColorFromRGB(0xBC36FF); - } - self.updateGoldLabel.text = goldNum.stringValue; + BOOL isWinning = [goldString containsString:@"+"]; // 区分正负值 + UILabel *targetLabel = isWinning ? [self winPriceGoldLabel] : [self updateGoldLabel]; +// [self addSubview:targetLabel]; +// [targetLabel mas_makeConstraints:^(MASConstraintMaker *make) { +//// make.trailing.mas_equalTo(self.comboGoldLabel); +//// make.bottom.trailing.mas_equalTo(self.comboGoldLabel); +// make.leading.mas_equalTo(44); +// make.bottom.mas_equalTo(-44); +// }]; + CGFloat offset = isWinning ? -80 : -40; + + [self showGoldAnimationOnLabel:targetLabel withText:goldString offset:offset]; +} + +- (void)showGoldAnimationOnLabel:(UILabel *)label withText:(NSString *)text offset:(CGFloat)offset { + label.text = text; + label.alpha = 0; + + [UIView animateWithDuration:0.2 animations:^{ + [label mas_updateConstraints:^(MASConstraintMaker *make) { +// make.bottom.mas_equalTo(-44 + offset); + make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(offset); + }]; + label.alpha = 1; + [self layoutIfNeeded]; + } completion:^(BOOL finished) { + [UIView animateWithDuration:0.0 + delay:0.2 + options:UIViewAnimationOptionCurveLinear + animations:^{ + [label mas_updateConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(0); + }]; + label.alpha = 0; +// [self layoutIfNeeded]; + } completion:^(BOOL finished) { +// [label removeFromSuperview]; + self.isAnimatingUpdateGold = NO; + [self processNextUpdateGold]; + }]; +// self.isAnimatingUpdateGold = NO; +// [self processNextUpdateGold]; + }]; +} + +- (void)showGoldUpdateAnimation { self.updateGoldLabel.alpha = 0; [UIView animateWithDuration:0.1 animations:^{ [self.updateGoldLabel mas_updateConstraints:^(MASConstraintMaker *make) { - make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(goldNum.integerValue > 0 ? -60 : -40);; + make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(-40);; }]; self.updateGoldLabel.alpha = 1; [self layoutIfNeeded]; @@ -284,7 +364,31 @@ }]; } -#pragma mark - +- (void)showGoldWinPriceAnimation { + self.winPriceGoldLabel.alpha = 0; + [UIView animateWithDuration:0.1 animations:^{ + [self.winPriceGoldLabel mas_updateConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(-60); + }]; + self.winPriceGoldLabel.alpha = 1; + [self layoutIfNeeded]; + } completion:^(BOOL finished) { + [UIView animateWithDuration:0.0 + delay:0.15 + options:UIViewAnimationOptionCurveLinear + animations:^{ + [self.winPriceGoldLabel mas_updateConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(self.comboGoldLabel.mas_bottom).offset(0); + }]; + self.winPriceGoldLabel.alpha = 0; + [self layoutIfNeeded]; + } completion:^(BOOL finished) { + self.isAnimatingUpdateGold = NO; + [self processNextUpdateGold]; + }]; + }]; +} + #pragma mark - - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { // 如果触摸点在 UITextView 或 UICollectionView 内,则不触发手势 @@ -323,12 +427,21 @@ } - (UILabel *)updateGoldLabel { +// return [UILabel labelInitWithText:@"" font:kFontSemibold(24) textColor:UIColorFromRGB(0xffd54c)];; if (!_updateGoldLabel) { _updateGoldLabel = [UILabel labelInitWithText:@"" font:kFontSemibold(24) textColor:UIColorFromRGB(0xffd54c)]; } return _updateGoldLabel; } +- (UILabel *)winPriceGoldLabel { +// return [UILabel labelInitWithText:@"" font:kFontSemibold(24) textColor:UIColorFromRGB(0xBC36FF)]; + if (!_winPriceGoldLabel) { + _winPriceGoldLabel = [UILabel labelInitWithText:@"" font:kFontSemibold(24) textColor:UIColorFromRGB(0xBC36FF)]; + } + return _winPriceGoldLabel; +} + - (SVGAImageView *)playImageView { if (_playImageView == nil) { _playImageView = [[SVGAImageView alloc]init]; @@ -344,8 +457,14 @@ _countdownRingView = [[CountdownRingView alloc] initWithFrame:CGRectMake(0, 0, kGetScaleWidth(90), kGetScaleWidth(90)) duration:5]; _countdownRingView.userInteractionEnabled = YES; +//#if DEBUG +// UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; +// longPress.minimumPressDuration = 0.2; +// [_countdownRingView addGestureRecognizer:longPress]; +//#else UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; [_countdownRingView addGestureRecognizer:tap]; +//#endif } return _countdownRingView; }