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

369 lines
13 KiB
Objective-C

//
// 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];
[bannerView addNotification];
@kWeakify(bannerView);
[bannerView popEnterAnimation:^(BOOL finished) {
if (finished && bannerView.alreadyCancel == NO) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(bannerView);
[bannerView popLeaveAnimation:^(bool finished) {
if (bannerView.completeDisplay) {
bannerView.completeDisplay();
}
[bannerView removeFromSuperview];
}];
});
}
}];
}
- (void)addNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner];
}];
}
- (void)dismissBanner {
self.alreadyCancel = YES;
[self pop_removeAllAnimations]; // 停止所有动画
[self popLeaveAnimation:^(bool finished) {
if (self.completeDisplay) {
self.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;
}
return nil;
}
@end