Files
peko-ios/YuMi/Modules/YMRoom/View/AnimationView/GameUniversalBannerView.m

408 lines
15 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// GameUniversalBannerView.m
// YuMi
//
// Created by P on 2024/10/15.
//
#import "GameUniversalBannerView.h"
#import <POP.h>
#import <SVGA.h>
#import "AttachmentModel.h"
#import "PIUniversalBannerModel.h"
@interface GameUniversalBannerView ()
@property (nonatomic, strong) NetImageView *bgImageView;
@property (nonatomic, strong) SVGAImageView *bgSVGA;
@property (nonatomic, strong) NetImageView *gameIcon;
@property (nonatomic, strong) NetImageView *avatarIcon;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *goButton;
@property (nonatomic, assign) NSString *gameID;
@property (nonatomic, strong) SVGAParser *parser;
@property(nonatomic, assign) bool alreadyCancel;
@property (nonatomic, copy) void(^completeDisplay)(void);
@property (nonatomic, copy) void(^didTapGo)(NSInteger gameID);
@end
@implementation GameUniversalBannerView
+ (void)display:(UIView *)superView
with:(AttachmentModel *)attachment
complete:(void(^)(void))complete
goToGame:(void(^)(NSInteger gameID))go {
CGFloat width = KScreenWidth;
CGFloat height = kGetScaleWidth(70);
PIUniversalBannerModel *model = [PIUniversalBannerModel modelWithDictionary:attachment.data];
GameUniversalBannerView *bannerView = [[GameUniversalBannerView alloc] initWithFrame:CGRectMake(KScreenWidth, 0, width, height)];
bannerView.model = model;
bannerView.completeDisplay = complete;
bannerView.didTapGo = go;
bannerView.gameID = model.skipContent;
[superView addSubview:bannerView];
@kWeakify(bannerView);
[bannerView popEnterAnimation:^(BOOL finished) {
@kStrongify(bannerView);
[bannerView addNotification];
if (finished && bannerView.alreadyCancel == NO) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[bannerView popLeaveAnimation:^(bool finished) {
if (bannerView.completeDisplay) {
bannerView.completeDisplay();
}
[bannerView removeNotification];
[bannerView removeFromSuperview];
}];
});
}
}];
}
- (void)handleSwipeNotification {
[self dismissBanner];
}
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
NSLog(@"🔄 GameUniversalBannerView: 接收到点击点 %@ (bannerContainer坐标系)", NSStringFromCGPoint(point));
// 将 bannerContainer 坐标系中的点转换为 GameUniversalBannerView 坐标系中的点
CGPoint bannerPoint = [self convertPoint:point fromView:self.superview];
NSLog(@"🔄 GameUniversalBannerView: 转换为 banner 坐标系 %@", NSStringFromCGPoint(bannerPoint));
NSLog(@"%@", CGRectContainsPoint(self.goButton.frame, bannerPoint) ? @"YES" : @"NO");
// 检查点击是否与 go 按钮重合
CGPoint goButtonPoint = [self.goButton convertPoint:bannerPoint fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:nil]) {
NSLog(@"🎯 GameUniversalBannerView: tap 点与 go 按钮重合,触发游戏跳转事件");
[self handleTapGo];
} else {
CGPoint screenPoint = [self convertPoint:point toView:nil];
// 发送通知给 FunctionContainer 处理,传递屏幕坐标
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dismissBanner {
NSLog(@"🚨 GameUniversalBannerView dismissBanner 被调用");
self.alreadyCancel = YES;
[self pop_removeAllAnimations]; // 停止所有动画
[self popLeaveAnimation:^(bool finished) {
NSLog(@"🚨 GameUniversalBannerView 动画完成,调用回调");
if (self.completeDisplay) {
self.completeDisplay();
} else {
NSLog(@"🚨 警告: completeDisplay 回调为空");
}
[self removeFromSuperview];
}];
}
- (void)popEnterAnimation:(void(^)(BOOL finished))finish {
NSInteger height = kGetScaleWidth(70);
POPSpringAnimation *enterAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPViewFrame];
enterAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, KScreenWidth, height)];
enterAnimation.springBounciness = 10; // 弹性系数
enterAnimation.springSpeed = 12; // 动画速度
enterAnimation.completionBlock = ^(POPAnimation *anim, BOOL finished) {
if (finish) {
finish(finished);
}
};
[self pop_addAnimation:enterAnimation forKey:@"enterAnimation"];
}
- (void)popLeaveAnimation:(void(^)(bool finished))finish {
NSInteger height = kGetScaleWidth(70);
POPBasicAnimation *exitAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewFrame];
exitAnimation.toValue = [NSValue valueWithCGRect:CGRectMake(-KScreenWidth, 0, KScreenWidth, height)];
exitAnimation.duration = 0.25; // 动画持续时间
exitAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
exitAnimation.completionBlock = ^(POPAnimation *anim, BOOL finished) {
if (finish) {
finish(finished);
}
};
[self pop_addAnimation:exitAnimation forKey:@"exitAnimation"];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.bgSVGA];
[self.bgSVGA mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
[self addSubview:self.bgImageView];
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self);
}];
[self addSubview:self.gameIcon];
[self.gameIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(44);
make.height.mas_equalTo(44);
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(35);
}];
[self addSubview:self.avatarIcon];
[self.avatarIcon mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(28);
make.height.mas_equalTo(28);
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(self.gameIcon.mas_trailing).offset(5);
}];
[self addSubview:self.goButton];
[self.goButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self);
make.trailing.mas_equalTo(-36);
make.size.mas_equalTo(CGSizeMake(35, 20));
}];
[self addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(117);
make.trailing.mas_equalTo(-80);
make.centerY.mas_equalTo(self.goButton);
make.height.mas_equalTo(70);
}];
}
return self;
}
- (void)setEntity:(SVGAVideoEntity *)entity {
_entity = entity;
self.bgSVGA.videoItem = entity;
[self.bgSVGA autoPlay];
}
- (void)setModel:(PIUniversalBannerModel *)model {
_model = model;
NSDictionary *textDic = model.template;
if(textDic.allKeys.count == 0) {
return ;
}
NSString *key = [NSBundle uploadLanguageText];
NSString *title = textDic[key] == nil ? textDic[textDic.allKeys.firstObject] : textDic[key];
if(title.length == 0) {
return;
}
[title stringByReplacingOccurrencesOfString:@"\n" withString:@""];
if (_model.fontSize <= 0){
_model.fontSize = 12;
}
CGFloat font = _model.fontSize;
NSMutableAttributedString * attribute = [[NSMutableAttributedString alloc] initWithString:title
attributes:@{
NSFontAttributeName:[UIFont systemFontOfSize:font],
NSForegroundColorAttributeName:[DJDKMIMOMColor colorWithHexString:_model.textColor]}];
for (PIUniversalBannerItemModel *model in _model.contents) {
if([model.type isEqualToString:@"TEXT"]){
NSDictionary *subTextDic = model.text;
if(subTextDic.allKeys.count > 0){
NSString *subText = subTextDic[key] == nil ? subTextDic[subTextDic.allKeys.firstObject] : subTextDic[key];
NSAttributedString *attText = [[NSAttributedString alloc]initWithString:subText attributes:@{NSForegroundColorAttributeName:[DJDKMIMOMColor colorWithHexString:model.textColor],NSFontAttributeName:[UIFont systemFontOfSize:font]}];
if ([attribute.string containsString:[NSString stringWithFormat:@"{%@}",model.key]]){
[attribute replaceCharactersInRange:[attribute.string rangeOfString:[NSString stringWithFormat:@"{%@}",model.key]] withAttributedString:attText];
}
}
} else if ([model.type isEqualToString:@"IMAGE"]) {
if ([model.key isEqualToString:@"avatar"]) {
[self.avatarIcon loadImageWithUrl:model.image completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) {
if (!image) {
[self.avatarIcon mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
} else {
self.avatarIcon.image = image;
}
}];
} else if ([model.key isEqualToString:@"gameIcon"]) {
[self.gameIcon loadImageWithUrl:model.image completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) {
if (!image) {
[self.gameIcon mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
} else {
self.gameIcon.image = image;
}
}];
}
if ([attribute.string containsString:[NSString stringWithFormat:@"{%@}",model.key]]){
[attribute replaceCharactersInRange:[attribute.string rangeOfString:[NSString stringWithFormat:@"{%@}",model.key]] withAttributedString:[NSAttributedString new]];
}
}
}
self.titleLabel.attributedText = attribute;
BOOL isSvga = [model.resourceType.uppercaseString isEqualToString:@"SVGA"];
@kWeakify(self);
if (isSvga == YES) {
self.bgSVGA.hidden = NO;
self.bgImageView.hidden = YES;
if (!_parser) {
_parser = [SVGAParser new];
}
[self.parser parseWithURL:[NSURL URLWithString:model.resourceContent] completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
@kStrongify(self);
self.bgSVGA.videoItem = videoItem;
} failureBlock:^(NSError * _Nonnull error) {
@kStrongify(self);
[self loadLocalSVGA];
}];
}else{
self.bgSVGA.hidden = YES;
self.bgImageView.hidden = NO;
@kWeakify(self);
[self.bgImageView loadImageWithUrl:model.resourceContent completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) {
@kStrongify(self);
if (image) {
self.bgImageView.image = image;
} else {
[self loadLocalSVGA];
}
}];
}
}
- (void)loadLocalSVGA {
self.bgSVGA.hidden = NO;
self.bgImageView.hidden = YES;
@kWeakify(self);
[self.parser parseWithNamed:@"game_floating_bg"
inBundle:[NSBundle mainBundle]
completionBlock:^(SVGAVideoEntity * _Nonnull videoItem) {
@kStrongify(self);
self.bgSVGA.videoItem = videoItem;
} failureBlock:^(NSError * _Nonnull error) {
}];
}
- (void)setupNotification {
}
- (void)handleTapGo {
if (self.didTapGo) {
self.didTapGo(self.gameID.integerValue);
}
}
#pragma mark -
- (NetImageView *)bgImageView {
if (!_bgImageView) {
_bgImageView = [[NetImageView alloc] init];
_bgImageView.layer.cornerRadius = 2;
_bgImageView.layer.masksToBounds = YES;
}
return _bgImageView;
}
- (SVGAImageView *)bgSVGA {
if (!_bgSVGA) {
_bgSVGA = [[SVGAImageView alloc] init];
_bgSVGA.autoPlay = YES;
_bgSVGA.loops = -1;
}
return _bgSVGA;
}
- (NetImageConfig *)avatarConfig {
NetImageConfig * config = [[NetImageConfig alloc]init];
config.imageType = ImageTypeUserIcon;
config.placeHolder = [UIImageConstant defaultAvatarPlaceholder];
return config;
}
- (NetImageView *)gameIcon {
if (!_gameIcon) {
_gameIcon = [[NetImageView alloc] initWithConfig:[self avatarConfig]];
_gameIcon.layer.cornerRadius = 2;
_gameIcon.layer.masksToBounds = YES;
}
return _gameIcon;
}
- (NetImageView *)avatarIcon {
if (!_avatarIcon) {
_avatarIcon = [[NetImageView alloc] initWithConfig:[self avatarConfig]];
_avatarIcon.layer.cornerRadius = 28/2;
_avatarIcon.layer.masksToBounds = YES;
}
return _avatarIcon;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.numberOfLines = 0;
_titleLabel.adjustsFontSizeToFitWidth = YES;
_titleLabel.minimumScaleFactor = 0.5;
}
return _titleLabel;
}
- (UIButton *)goButton {
if (!_goButton) {
_goButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_goButton setImage:kImage(@"game_banner_go")
forState:UIControlStateNormal];
[_goButton addTarget:self
action:@selector(handleTapGo)
forControlEvents:UIControlEventTouchUpInside];
}
return _goButton;
}
// 在主实现体内添加事件穿透逻辑
//- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {
// return nil;
// }
// CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self];
// if ([self.goButton pointInside:goButtonPoint withEvent:event]) {
// return self.goButton;
// }
// // 其他区域返回self允许触摸事件被处理包括手势识别器
// return self;
//}
@end