
- Removed YuMi/Library/ (138 MB, not tracked) - Removed YuMi/Resources/ (23 MB, not tracked) - Removed old version assets (566 files, not tracked) - Excluded Pods/, xcuserdata/ and other build artifacts - Clean repository optimized for company server deployment
535 lines
19 KiB
Objective-C
535 lines
19 KiB
Objective-C
//
|
|
// MyDressingViewController.m
|
|
// YuMi
|
|
//
|
|
// Created by P on 2024/11/13.
|
|
//
|
|
|
|
#import "MyDressingViewController.h"
|
|
#import <SVGA.h>
|
|
#import <QGVAPWrapView.h>
|
|
#import "UserInfoModel.h"
|
|
#import "MyDressingDataPresent.h"
|
|
#import "ShoppingMallItemPreview.h"
|
|
#import "XPSkillCardPlayerManager.h"
|
|
#import "XPRoomGiftAnimationParser.h"
|
|
#import "ShoppingMallCategoryListView.h"
|
|
|
|
static NSArray<NSDictionary<NSString *, NSNumber *> *> *MyDressUpOptions(void) {
|
|
return @[
|
|
@{YMLocalizedString(@"XPMineDressUpViewController2") : @1}, // 座驾
|
|
@{YMLocalizedString(@"XPMineDressUpViewController1") : @0}, // 头饰
|
|
@{YMLocalizedString(@"XPMineDressUpViewController3") : @2}, // 铭牌
|
|
@{YMLocalizedString(@"XPMineDressUpViewController4") : @3}, // 资料卡
|
|
@{YMLocalizedString(@"XPMineDressUpViewController5") : @4}, // 聊天气泡
|
|
@{YMLocalizedString(@"1.0.30_text_9") : @5} // 個人頁裝飾
|
|
];
|
|
}
|
|
|
|
@interface MyDressingViewController ()
|
|
|
|
@property(nonatomic, strong) UserInfoModel *userInfo;
|
|
|
|
@property(nonatomic, strong) NetImageView *avatarView;
|
|
@property(nonatomic, strong) NetImageView *avatarHeadViewImageView;
|
|
@property(nonatomic, strong) SVGAImageView *avatarHeadWearSVGA;
|
|
|
|
@property (nonatomic, strong) UIView *playEffectMask;
|
|
@property (nonatomic, strong) VAPView *mp4Effect;
|
|
@property(nonatomic, strong) XPRoomGiftAnimationParser *mp4Parser;
|
|
@property (nonatomic, strong) SVGAImageView *svgaEffect;
|
|
@property(nonatomic, strong) UIActivityIndicatorView *loading;
|
|
|
|
@property(nonatomic, strong) MyDressingDataModel *selectedModel;
|
|
|
|
@property(nonatomic, strong) ShoppingMallCategoryListView *listView;
|
|
|
|
@end
|
|
|
|
@implementation MyDressingViewController
|
|
|
|
- (MyDressingDataPresent *)createPresenter {
|
|
return [[MyDressingDataPresent alloc] init];
|
|
}
|
|
|
|
- (BOOL)isHiddenNavBar {
|
|
return YES;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
self.userInfo = [XPSkillCardPlayerManager shareInstance].userInfoModel;
|
|
|
|
[self setupUI];
|
|
[self updateAvatar];
|
|
}
|
|
|
|
- (void)setupUI {
|
|
[self setupBackground];
|
|
[self setupNavigationBar];
|
|
[self setupAvatar];
|
|
[self setupContentList];
|
|
}
|
|
|
|
- (void)setupBackground {
|
|
self.view.backgroundColor = UIColorFromRGB(0x02061D);
|
|
UIImageView *topBG = [[UIImageView alloc] initWithImage:kImage(@"mall_top_bg")];
|
|
[self.view addSubview:topBG];
|
|
[topBG mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.top.leading.trailing.mas_equalTo(self.view);
|
|
make.height.mas_equalTo(kGetScaleWidth(200));
|
|
}];
|
|
}
|
|
|
|
- (void)setupNavigationBar {
|
|
UILabel *titleLabel = [self titleLabel];
|
|
UIButton *backButton = [self backButton];
|
|
[self.view addSubview:titleLabel];
|
|
[self.view addSubview:backButton];
|
|
|
|
[titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.centerX.mas_equalTo(self.view);
|
|
make.top.mas_equalTo(self.view).offset(44 + 11);
|
|
make.height.mas_equalTo(22);
|
|
}];
|
|
|
|
[backButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.leading.mas_equalTo(self.view).offset(16);
|
|
make.centerY.mas_equalTo(titleLabel);
|
|
make.size.mas_equalTo(CGSizeMake(22, 22));
|
|
}];
|
|
}
|
|
|
|
- (void)setupAvatar {
|
|
[self.view addSubview:self.avatarView];
|
|
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.top.mas_equalTo(100);
|
|
make.centerX.mas_equalTo(self.view);
|
|
make.size.mas_equalTo(CGSizeMake(kGetScaleWidth(73), kGetScaleWidth(73)));
|
|
}];
|
|
|
|
[self.view addSubview:self.avatarHeadViewImageView];
|
|
[self.avatarHeadViewImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.center.mas_equalTo(self.avatarView);
|
|
make.size.mas_equalTo(self.avatarView).multipliedBy(1.3);
|
|
}];
|
|
|
|
[self.view addSubview:self.avatarHeadWearSVGA];
|
|
[self.avatarHeadWearSVGA mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.center.mas_equalTo(self.avatarView);
|
|
make.size.mas_equalTo(self.avatarView).multipliedBy(1.3);
|
|
}];
|
|
}
|
|
|
|
- (void)setupContentList {
|
|
NSInteger bottom = kSafeAreaBottomHeight;
|
|
// 内部列表使用 frame 布局,这里要计算 height
|
|
ShoppingMallCategoryListView *listView = [[ShoppingMallCategoryListView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight - 100 - 73)];
|
|
listView.isForMyDressingPage = YES;
|
|
listView.bottomSpace = bottom;
|
|
[self.view addSubview:listView];
|
|
[listView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.top.mas_equalTo(self.avatarView.mas_bottom).offset(16);
|
|
make.leading.trailing.bottom.mas_equalTo(self.view);
|
|
}];
|
|
@kWeakify(self);
|
|
listView.fetchDataForMyDressingPage = ^(NSInteger pageIndex, NSInteger pageNum, FetchDataForMyDressingCompletion _Nonnull completion) {
|
|
@kStrongify(self);
|
|
[self fetchDataForPage:pageIndex
|
|
pageNum:pageNum
|
|
completion:completion];
|
|
};
|
|
listView.didTapItemPlay = ^(NSString * _Nonnull resourcePath, NSInteger effectType, NSInteger type) {
|
|
@kStrongify(self);
|
|
CGSize size = CGSizeZero;
|
|
switch (type) {
|
|
case 0: // 头饰
|
|
size = CGSizeMake(kGetScaleWidth(183), kGetScaleWidth(183));
|
|
break;
|
|
case 5:
|
|
size = CGSizeMake(KScreenWidth - 20, KScreenWidth - 20);
|
|
break;
|
|
default:
|
|
size = CGSizeMake(KScreenWidth, KScreenHeight);
|
|
break;
|
|
}
|
|
[self playItemEffect:resourcePath effectType:effectType size:size];
|
|
};
|
|
listView.returnMyDressingSelectedModel = ^(MyDressingDataModel * _Nonnull model, NSInteger type, NSIndexPath *indexPath) {
|
|
@kStrongify(self);
|
|
[self handTapItem:model type:type indexPath:indexPath];
|
|
};
|
|
|
|
listView.items = MyDressUpOptions();
|
|
self.listView = listView;
|
|
}
|
|
|
|
- (void)updateAvatar {
|
|
self.avatarView.imageUrl = self.userInfo.avatar;
|
|
if ([NSString isEmpty:self.userInfo.headwearEffect]) {
|
|
[self.avatarHeadWearSVGA stopAnimation];
|
|
self.avatarHeadWearSVGA.hidden = YES;
|
|
} else {
|
|
if ([self.userInfo.headwearEffect.lowercaseString hasSuffix:@"svga"]) {
|
|
self.avatarHeadWearSVGA.hidden = NO;
|
|
self.avatarHeadViewImageView.hidden = YES;
|
|
SVGAParser *p = [[SVGAParser alloc] init];
|
|
[p parseWithURL:[NSURL URLWithString:self.userInfo.headwearEffect]
|
|
completionBlock:^(SVGAVideoEntity * _Nullable videoItem) {
|
|
self.avatarHeadWearSVGA.videoItem = videoItem;
|
|
[self.avatarHeadWearSVGA startAnimation];
|
|
self.avatarHeadWearSVGA.hidden = NO;
|
|
} failureBlock:^(NSError * _Nullable error) {}];
|
|
} else {
|
|
self.avatarHeadWearSVGA.hidden = YES;
|
|
self.avatarHeadViewImageView.hidden = NO;
|
|
self.avatarHeadViewImageView.imageUrl = self.userInfo.headwearEffect;
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
- (void)fetchDataForPage:(NSInteger)pageIndex
|
|
pageNum:(NSInteger)pageNum
|
|
completion:(FetchDataForMyDressingCompletion)completion {
|
|
switch (pageIndex) {
|
|
case 0: {
|
|
[self.presenter loadMyDressingItems:0
|
|
page:pageNum
|
|
success:^(NSArray <MyDressingDataModel *>* _Nonnull array, NSInteger type) {
|
|
if (completion) {
|
|
completion(array);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) {
|
|
|
|
}];
|
|
}
|
|
break;
|
|
case 1: {
|
|
[self.presenter loadVehicles:^(NSArray <CarModel *>* _Nonnull array) {
|
|
if (completion) {
|
|
NSMutableArray *transArray = @[].mutableCopy;
|
|
for (CarModel *car in array) {
|
|
MyDressingDataModel *model = [MyDressingDataModel modelFromVehicle:car];
|
|
[transArray addObject:model];
|
|
}
|
|
completion(transArray);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) { }];
|
|
}
|
|
break;
|
|
case 2: {
|
|
[self.presenter loadNameplate:pageNum
|
|
success:^(NSArray<NameplateModel *> * _Nonnull array) {
|
|
if (completion) {
|
|
NSMutableArray *transArray = @[].mutableCopy;
|
|
for (NameplateModel *nameplate in array) {
|
|
MyDressingDataModel *model = [MyDressingDataModel modelFromNameplate:nameplate];
|
|
[transArray addObject:model];
|
|
}
|
|
completion(transArray);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) {
|
|
|
|
}];
|
|
}
|
|
break;
|
|
case 3: {
|
|
[self.presenter loadRoomCard:pageNum
|
|
success:^(NSArray<NobleCardModel *> * _Nonnull array) {
|
|
if (completion) {
|
|
NSMutableArray *transArray = @[].mutableCopy;
|
|
for (NobleCardModel *nobleCard in array) {
|
|
MyDressingDataModel *model = [MyDressingDataModel modelFromNobelCard:nobleCard];
|
|
[transArray addObject:model];
|
|
}
|
|
completion(transArray);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) {
|
|
|
|
}];
|
|
}
|
|
break;
|
|
case 4: {
|
|
[self.presenter loadBubble:pageNum
|
|
success:^(NSArray<ChatBubbleModel *> * _Nonnull array) {
|
|
if (completion) {
|
|
NSMutableArray *transArray = @[].mutableCopy;
|
|
for (ChatBubbleModel *bubble in array) {
|
|
MyDressingDataModel *model = [MyDressingDataModel modelFromChatBubble:bubble];
|
|
[transArray addObject:model];
|
|
}
|
|
completion(transArray);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) {
|
|
|
|
}];
|
|
}
|
|
break;
|
|
case 5: {
|
|
[self.presenter loadMyDressingItems:5
|
|
page:pageNum
|
|
success:^(NSArray <MyDressingDataModel *>* _Nonnull array, NSInteger type) {
|
|
if (completion) {
|
|
completion(array);
|
|
}
|
|
} failure:^(NSError * _Nonnull error) {
|
|
|
|
}];
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void)handTapItem:(MyDressingDataModel *)model
|
|
type:(NSInteger)type
|
|
indexPath:(NSIndexPath *)indexPath {
|
|
|
|
if (model.hasExpired) {
|
|
// 弹窗购买
|
|
BOOL isVaild = model.obtainWay<=1;
|
|
if (isVaild) {
|
|
isVaild = ![NSString isEmpty:model.dressShopId] && model.dressShopId.integerValue > 0;
|
|
}
|
|
|
|
ShoppingMallItemPreview *preview = [[ShoppingMallItemPreview alloc] initWithMyDress:model
|
|
isVaild:isVaild];
|
|
preview.frame = CGRectMake(0, 0, 284, 353);
|
|
[TTPopup popupView:preview style:TTPopupStyleAlert];
|
|
|
|
@kWeakify(self);
|
|
[preview setRebuyWith:^(MyDressingDataModel * _Nonnull model) {
|
|
@kStrongify(self);
|
|
@kWeakify(self)
|
|
[self.presenter rebuyItem:model.dressShopId
|
|
success:^(id _Nonnull obj) {
|
|
@kStrongify(self);
|
|
[TTPopup dismiss];
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
[self showSuccessToast:YMLocalizedString(@"XPDressUpShopListViewController0")];
|
|
[self.listView updateSelectedItem:indexPath];
|
|
});
|
|
} failure:^(NSError * _Nonnull error) {
|
|
@kStrongify(self);
|
|
[self showErrorToast:error.localizedDescription];
|
|
}];
|
|
}];
|
|
} else {
|
|
// 直接使用
|
|
NSInteger dressID = 0;
|
|
if (model) {
|
|
if (type == 1 || type == 2) {
|
|
dressID = model.id;
|
|
} else {
|
|
dressID = model.dressId.integerValue;
|
|
}
|
|
}
|
|
|
|
@kWeakify(self);
|
|
[self.presenter useDressBy:type
|
|
dressID:dressID
|
|
success:^(UserInfoModel *userInfo){
|
|
@kStrongify(self);
|
|
if (userInfo) {
|
|
self.userInfo = userInfo;
|
|
[XPSkillCardPlayerManager shareInstance].userInfoModel = userInfo;
|
|
[self updateAvatar];
|
|
}
|
|
[self.listView updateSelectedItem:indexPath];
|
|
} failure:^(NSError * _Nonnull error) {
|
|
// NSLog(@"%@", error);
|
|
}];
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
- (void)didTapBack {
|
|
[self.navigationController popViewControllerAnimated:YES];
|
|
}
|
|
|
|
#pragma mark -
|
|
- (void)playItemEffect:(NSString *)path
|
|
effectType:(NSInteger)effectType
|
|
size:(CGSize)size {
|
|
if (!_playEffectMask) {
|
|
[self.view addSubview:self.playEffectMask];
|
|
[self.playEffectMask mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.edges.mas_equalTo(self.view);
|
|
}];
|
|
|
|
[self.playEffectMask addSubview:self.svgaEffect];
|
|
[self.playEffectMask addSubview:self.mp4Effect];
|
|
}
|
|
|
|
[self.svgaEffect mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
make.center.mas_equalTo(self.playEffectMask);
|
|
make.size.mas_equalTo(size);
|
|
}];
|
|
|
|
[self.mp4Effect mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
make.center.mas_equalTo(self.playEffectMask);
|
|
make.size.mas_equalTo(size);
|
|
}];
|
|
|
|
self.playEffectMask.hidden = NO;
|
|
[self.loading startAnimating];
|
|
|
|
if ([path.lowercaseString hasSuffix:@"mp4"] || effectType == 1) {
|
|
[self playMP4:path];
|
|
} else if ([path.lowercaseString hasSuffix:@"svga"] || effectType == 2) {
|
|
[self playSVGA:path];
|
|
} else {
|
|
[self playSVGA:path];
|
|
}
|
|
}
|
|
|
|
- (void)playMP4:(NSString *)path {
|
|
@kWeakify(self);
|
|
[self.mp4Parser parseWithURL:path
|
|
completionBlock:^(NSString * _Nullable videoUrl) {
|
|
@kStrongify(self);
|
|
[self.loading stopAnimating];
|
|
self.mp4Effect.hidden = NO;
|
|
[self.mp4Effect playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
|
|
} failureBlock:^(NSError * _Nullable error) {
|
|
@kStrongify(self);
|
|
[self stopItemEffect];
|
|
}];
|
|
}
|
|
|
|
- (void)playSVGA:(NSString *)path {
|
|
@kWeakify(self);
|
|
SVGAParser *p = [[SVGAParser alloc] init];
|
|
[p parseWithURL:[NSURL URLWithString:path]
|
|
completionBlock:^(SVGAVideoEntity * _Nullable videoItem) {
|
|
@kStrongify(self);
|
|
[self.loading stopAnimating];
|
|
self.svgaEffect.hidden = NO;
|
|
self.svgaEffect.videoItem = videoItem;
|
|
[self.svgaEffect startAnimation];
|
|
} failureBlock:^(NSError * _Nullable error) {
|
|
@kStrongify(self);
|
|
[self stopItemEffect];
|
|
}];
|
|
}
|
|
|
|
- (void)stopItemEffect {
|
|
[self.loading stopAnimating];
|
|
[self.svgaEffect stopAnimation];
|
|
self.svgaEffect.hidden = YES;
|
|
[self.mp4Effect stopHWDMP4];
|
|
self.mp4Effect.hidden = YES;
|
|
self.playEffectMask.hidden = YES;
|
|
}
|
|
|
|
#pragma mark -
|
|
- (UIButton *)backButton {
|
|
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
[b setImage:[kImage(@"common_nav_back_white") ms_SetImageForRTL]
|
|
forState:UIControlStateNormal];
|
|
[b addTarget:self
|
|
action:@selector(didTapBack)
|
|
forControlEvents:UIControlEventTouchUpInside];
|
|
return b;
|
|
}
|
|
|
|
- (UILabel *)titleLabel {
|
|
UILabel *label = [[UILabel alloc] init];
|
|
label.textAlignment = NSTextAlignmentCenter;
|
|
label.font = kFontMedium(17);
|
|
label.text = YMLocalizedString(@"1.0.30_text_8");
|
|
label.textColor = UIColorFromRGB(0xD9E7F7);
|
|
return label;
|
|
}
|
|
|
|
- (UIView *)playEffectMask {
|
|
if (!_playEffectMask) {
|
|
_playEffectMask = [[UIView alloc] initWithFrame:self.view.bounds];
|
|
_playEffectMask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7f];
|
|
[_playEffectMask addSubview:self.loading];
|
|
[self.loading mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
make.center.mas_equalTo(_playEffectMask);
|
|
}];
|
|
|
|
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stopItemEffect)];
|
|
[_playEffectMask addGestureRecognizer:tap];
|
|
}
|
|
return _playEffectMask;
|
|
}
|
|
|
|
- (SVGAImageView *)svgaEffect {
|
|
if (!_svgaEffect) {
|
|
_svgaEffect = [[SVGAImageView alloc] init];
|
|
_svgaEffect.contentMode = UIViewContentModeScaleAspectFit;
|
|
_svgaEffect.autoPlay = YES;
|
|
_svgaEffect.loops = 0;
|
|
_svgaEffect.clearsAfterStop = YES;
|
|
_svgaEffect.hidden = YES;
|
|
_svgaEffect.userInteractionEnabled = YES;
|
|
}
|
|
return _svgaEffect;
|
|
}
|
|
|
|
- (VAPView *)mp4Effect {
|
|
if (!_mp4Effect) {
|
|
_mp4Effect = [[VAPView alloc] init];
|
|
_mp4Effect.contentMode = UIViewContentModeScaleAspectFit;
|
|
}
|
|
return _mp4Effect;
|
|
}
|
|
|
|
- (XPRoomGiftAnimationParser *)mp4Parser {
|
|
if (!_mp4Parser) {
|
|
_mp4Parser = [[XPRoomGiftAnimationParser alloc] init];
|
|
}
|
|
return _mp4Parser;
|
|
}
|
|
|
|
- (UIActivityIndicatorView *)loading {
|
|
if (!_loading) {
|
|
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
|
indicator.translatesAutoresizingMaskIntoConstraints = NO;
|
|
indicator.hidesWhenStopped = YES;
|
|
_loading = indicator;
|
|
}
|
|
return _loading;
|
|
}
|
|
|
|
- (NetImageView *)avatarHeadViewImageView {
|
|
if (!_avatarHeadViewImageView) {
|
|
_avatarHeadViewImageView = [[NetImageView alloc] init];
|
|
_avatarHeadViewImageView.contentMode = UIViewContentModeScaleAspectFit;
|
|
}
|
|
return _avatarHeadViewImageView;
|
|
}
|
|
|
|
- (NetImageView *)avatarView {
|
|
if (!_avatarView) {
|
|
NetImageConfig * config = [[NetImageConfig alloc]init];
|
|
config.imageType = ImageTypeUserIcon;
|
|
config.placeHolder = [UIImageConstant defaultAvatarPlaceholder];
|
|
_avatarView = [[NetImageView alloc] initWithConfig:config];
|
|
[_avatarView setCornerRadius:kGetScaleWidth(73)/2
|
|
corners:kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner
|
|
borderWidth:2
|
|
borderColor:UIColorFromRGB(0xFBD99A)];
|
|
}
|
|
return _avatarView;
|
|
}
|
|
|
|
- (SVGAImageView *)avatarHeadWearSVGA {
|
|
if (!_avatarHeadWearSVGA) {
|
|
_avatarHeadWearSVGA = [[SVGAImageView alloc] init];
|
|
_avatarHeadWearSVGA.contentMode = UIViewContentModeScaleAspectFit;
|
|
_avatarHeadWearSVGA.autoPlay = YES;
|
|
_avatarHeadWearSVGA.loops = 0;
|
|
_avatarHeadWearSVGA.clearsAfterStop = YES;
|
|
}
|
|
return _avatarHeadWearSVGA;
|
|
}
|
|
|
|
@end
|