Commit 75fc8c51 by suolong

开始表演表演api

parent 654c3e0e
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
00F7FF722FAF000100AAAA1B /* FUSLiveShowTimeTicketContributionPopView.h in Headers */ = {isa = PBXBuildFile; fileRef = 00F7FF702FAF000100AAAA19 /* FUSLiveShowTimeTicketContributionPopView.h */; };
00F7FF732FAF000100AAAA1C /* FUSLiveShowTimeTicketContributionPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F7FF712FAF000100AAAA1A /* FUSLiveShowTimeTicketContributionPopView.m */; };
004772D72F5AE86A00E46A79 /* PK_Cover_Animation_8.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772B62F5AE86A00E46A79 /* PK_Cover_Animation_8.png */; }; 004772D72F5AE86A00E46A79 /* PK_Cover_Animation_8.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772B62F5AE86A00E46A79 /* PK_Cover_Animation_8.png */; };
004772D82F5AE86A00E46A79 /* PK_Cover_Animation_29.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772CB2F5AE86A00E46A79 /* PK_Cover_Animation_29.png */; }; 004772D82F5AE86A00E46A79 /* PK_Cover_Animation_29.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772CB2F5AE86A00E46A79 /* PK_Cover_Animation_29.png */; };
004772D92F5AE86A00E46A79 /* PK_Cover_Animation_20.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772C22F5AE86A00E46A79 /* PK_Cover_Animation_20.png */; }; 004772D92F5AE86A00E46A79 /* PK_Cover_Animation_20.png in Resources */ = {isa = PBXBuildFile; fileRef = 004772C22F5AE86A00E46A79 /* PK_Cover_Animation_20.png */; };
...@@ -3884,6 +3886,8 @@ ...@@ -3884,6 +3886,8 @@
00F7FF152F9A100100AAAA06 /* FUSLiveShowTimeTicketContributionListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveShowTimeTicketContributionListView.m; sourceTree = "<group>"; }; 00F7FF152F9A100100AAAA06 /* FUSLiveShowTimeTicketContributionListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveShowTimeTicketContributionListView.m; sourceTree = "<group>"; };
00F7FF162F9A100100AAAA07 /* FUSLiveShowTimeTicketNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveShowTimeTicketNoticeView.h; sourceTree = "<group>"; }; 00F7FF162F9A100100AAAA07 /* FUSLiveShowTimeTicketNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveShowTimeTicketNoticeView.h; sourceTree = "<group>"; };
00F7FF172F9A100100AAAA08 /* FUSLiveShowTimeTicketNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveShowTimeTicketNoticeView.m; sourceTree = "<group>"; }; 00F7FF172F9A100100AAAA08 /* FUSLiveShowTimeTicketNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveShowTimeTicketNoticeView.m; sourceTree = "<group>"; };
00F7FF702FAF000100AAAA19 /* FUSLiveShowTimeTicketContributionPopView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveShowTimeTicketContributionPopView.h; sourceTree = "<group>"; };
00F7FF712FAF000100AAAA1A /* FUSLiveShowTimeTicketContributionPopView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveShowTimeTicketContributionPopView.m; sourceTree = "<group>"; };
BED655F22C5B745D00668116 /* FUSLiveBottomToolView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveBottomToolView.h; sourceTree = "<group>"; }; BED655F22C5B745D00668116 /* FUSLiveBottomToolView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveBottomToolView.h; sourceTree = "<group>"; };
BED655F32C5B745D00668116 /* FUSLiveBottomToolView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveBottomToolView.m; sourceTree = "<group>"; }; BED655F32C5B745D00668116 /* FUSLiveBottomToolView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUSLiveBottomToolView.m; sourceTree = "<group>"; };
BED655F42C5B745D00668116 /* FUSLiveChatFastInputCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveChatFastInputCell.h; sourceTree = "<group>"; }; BED655F42C5B745D00668116 /* FUSLiveChatFastInputCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUSLiveChatFastInputCell.h; sourceTree = "<group>"; };
...@@ -6921,6 +6925,8 @@ ...@@ -6921,6 +6925,8 @@
00E6CB0C2F4D523000B63797 /* FUSLiveGameListViewHeader.swift */, 00E6CB0C2F4D523000B63797 /* FUSLiveGameListViewHeader.swift */,
00E6CE1F2F56F5F200B63797 /* FUSLiveBottomToolWebpButton.swift */, 00E6CE1F2F56F5F200B63797 /* FUSLiveBottomToolWebpButton.swift */,
00F7FF302F9A100100AAAA01 /* FUSLiveShowTimeTicketActionPop */, 00F7FF302F9A100100AAAA01 /* FUSLiveShowTimeTicketActionPop */,
00F7FF702FAF000100AAAA19 /* FUSLiveShowTimeTicketContributionPopView.h */,
00F7FF712FAF000100AAAA1A /* FUSLiveShowTimeTicketContributionPopView.m */,
); );
path = ChatInputView; path = ChatInputView;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -8766,6 +8772,7 @@ ...@@ -8766,6 +8772,7 @@
00F7FF222F9A100100AAAA03 /* FUSLiveShowTimeTicketContributionCell.h in Headers */, 00F7FF222F9A100100AAAA03 /* FUSLiveShowTimeTicketContributionCell.h in Headers */,
00F7FF242F9A100100AAAA05 /* FUSLiveShowTimeTicketContributionListView.h in Headers */, 00F7FF242F9A100100AAAA05 /* FUSLiveShowTimeTicketContributionListView.h in Headers */,
00F7FF262F9A100100AAAA07 /* FUSLiveShowTimeTicketNoticeView.h in Headers */, 00F7FF262F9A100100AAAA07 /* FUSLiveShowTimeTicketNoticeView.h in Headers */,
00F7FF722FAF000100AAAA1B /* FUSLiveShowTimeTicketContributionPopView.h in Headers */,
00B28CBA2D2FC4E10008476B /* FUSLiveEndLiveAssesModel.h in Headers */, 00B28CBA2D2FC4E10008476B /* FUSLiveEndLiveAssesModel.h in Headers */,
BE189DDF2C733B460008418B /* FSRRoomManagerViewController.h in Headers */, BE189DDF2C733B460008418B /* FSRRoomManagerViewController.h in Headers */,
BED65A442C5B745F00668116 /* FUSLiveBoxDetailSettingView.h in Headers */, BED65A442C5B745F00668116 /* FUSLiveBoxDetailSettingView.h in Headers */,
...@@ -10850,6 +10857,7 @@ ...@@ -10850,6 +10857,7 @@
00F7FF232F9A100100AAAA04 /* FUSLiveShowTimeTicketContributionCell.m in Sources */, 00F7FF232F9A100100AAAA04 /* FUSLiveShowTimeTicketContributionCell.m in Sources */,
00F7FF252F9A100100AAAA06 /* FUSLiveShowTimeTicketContributionListView.m in Sources */, 00F7FF252F9A100100AAAA06 /* FUSLiveShowTimeTicketContributionListView.m in Sources */,
00F7FF272F9A100100AAAA08 /* FUSLiveShowTimeTicketNoticeView.m in Sources */, 00F7FF272F9A100100AAAA08 /* FUSLiveShowTimeTicketNoticeView.m in Sources */,
00F7FF732FAF000100AAAA1C /* FUSLiveShowTimeTicketContributionPopView.m in Sources */,
BED6599E2C5B745F00668116 /* FUSLinkMicItemView.m in Sources */, BED6599E2C5B745F00668116 /* FUSLinkMicItemView.m in Sources */,
BED658802C5B745E00668116 /* FUSLivePropsModel.m in Sources */, BED658802C5B745E00668116 /* FUSLivePropsModel.m in Sources */,
BE189DB82C733B460008418B /* FSRRoomManagerShowRoomModel.m in Sources */, BE189DB82C733B460008418B /* FSRRoomManagerShowRoomModel.m in Sources */,
...@@ -281,10 +281,13 @@ ...@@ -281,10 +281,13 @@
}); });
return; return;
} }
if (FUSLiveHelper.shareInstance.liveType == FUSLiveTypeAnchor) { // 观众端只处理 dataType=2 以外的推送
if (dataType != 4) { if (FUSLiveHelper.shareInstance.liveType == FUSLiveTypeAudience && dataType == 2) {
return; return;
} }
// 主播端只处理 dataType=4 的推送
if (FUSLiveHelper.shareInstance.liveType == FUSLiveTypeAnchor && dataType != 4) {
return;
} }
// 复用 toggle 接口返回模型:字段与 socket 推送结构保持一致(targetTicketNum/finalTicketNum/stageData/mvpInfo 等) // 复用 toggle 接口返回模型:字段与 socket 推送结构保持一致(targetTicketNum/finalTicketNum/stageData/mvpInfo 等)
...@@ -503,7 +506,13 @@ ...@@ -503,7 +506,13 @@
} }
NSInteger remaining = MAX(0, target - finalNum); NSInteger remaining = MAX(0, target - finalNum);
CGFloat progress = (target > 0 ? ((CGFloat)finalNum / (CGFloat)target) : 0); CGFloat progress = (target > 0 ? ((CGFloat)finalNum / (CGFloat)target) : 0);
NSString *remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining]; NSString *remainingText = nil;
if (stageStatus == 2 || stageStatus == 1) {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"已集票数 %zd 张!"], (NSInteger)finalNum];
progress = 1.0;
} else {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
}
NSString *themeText = (model.showTheme.length > 0 ? model.showTheme : [NSString fus_localString:@"限时表演"]); NSString *themeText = (model.showTheme.length > 0 ? model.showTheme : [NSString fus_localString:@"限时表演"]);
NSInteger newTicketCount = MAX(0, model.buyNum); NSInteger newTicketCount = MAX(0, model.buyNum);
...@@ -537,14 +546,18 @@ ...@@ -537,14 +546,18 @@
} }
giftUser = (giftUserDict ? [FUSOnlineUserModel fus_modelWithDict:giftUserDict] : nil); giftUser = (giftUserDict ? [FUSOnlineUserModel fus_modelWithDict:giftUserDict] : nil);
} }
if (dataType == 4) {
[bar fus_updateTopTicketInfoWithGiftUserModel:giftUser newTicketCount:newTicketCount];
}
FUSLiveShowTimeCollectFrostedState showState = (newTicketCount > 0 ? FUSLiveShowTimeCollectFrostedStateNewTicket : FUSLiveShowTimeCollectFrostedStateStarted); FUSLiveShowTimeCollectFrostedState showState = (stageStatus == 0 && newTicketCount > 0 ? FUSLiveShowTimeCollectFrostedStateNewTicket : FUSLiveShowTimeCollectFrostedStateStarted);
// MVP 用户信息用于右侧头像展示(无则不展示) // MVP 用户信息用于右侧头像展示(无则不展示)
FUSOnlineUserModel *mvpUser = model.mvpInfo.mvpUserInfo; FUSOnlineUserModel *mvpUser = model.mvpInfo.mvpUserInfo;
if (bar.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) { if (bar.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime); NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime);
NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime); NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime);
NSInteger totalSeconds = (durationMs > 0 ? (NSInteger)llround(((NSTimeInterval)durationMs) / 1000.0) : -1);
NSInteger remainSeconds = -1; NSInteger remainSeconds = -1;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) { if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0); remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0);
...@@ -554,6 +567,7 @@ ...@@ -554,6 +567,7 @@
themeText:themeText themeText:themeText
progress:progress progress:progress
remainingText:remainingText remainingText:remainingText
countdownTotalSeconds:totalSeconds
countdownRemainingSeconds:remainSeconds countdownRemainingSeconds:remainSeconds
newTicketCount:newTicketCount newTicketCount:newTicketCount
giftUserModel:giftUser giftUserModel:giftUser
...@@ -1332,7 +1346,7 @@ ...@@ -1332,7 +1346,7 @@
return; return;
} }
if (roomInfoModel.collectTicket != 1) { if (roomInfoModel.collectTicket != 1 || roomInfoModel.liveScope != 5) {
[self fus_destroyShowTimeFrostedIfNeeded]; [self fus_destroyShowTimeFrostedIfNeeded];
return; return;
} }
...@@ -1423,7 +1437,13 @@ ...@@ -1423,7 +1437,13 @@
NSInteger remaining = MAX(0, target - finalNum); NSInteger remaining = MAX(0, target - finalNum);
CGFloat progress = MIN(1.0, MAX(0.0, (CGFloat)finalNum / (CGFloat)target)); CGFloat progress = MIN(1.0, MAX(0.0, (CGFloat)finalNum / (CGFloat)target));
NSString *remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining]; NSString *remainingText = nil;
if (stageStatus == 2 || stageStatus == 1) {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"已集票数 %zd 张!"], (NSInteger)finalNum];
progress = 1.0;
} else {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
}
NSString *finalThemeText = nil; NSString *finalThemeText = nil;
if (model.showTheme.length > 0) { if (model.showTheme.length > 0) {
...@@ -1439,17 +1459,19 @@ ...@@ -1439,17 +1459,19 @@
NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime); NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime);
NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime); NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime);
NSInteger totalSeconds = (durationMs > 0 ? (NSInteger)llround(((NSTimeInterval)durationMs) / 1000.0) : -1);
NSInteger remainSeconds = -1; NSInteger remainSeconds = -1;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) { if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0); remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0);
} }
strongSelf.showTimeCountdownRemainSeconds = remainSeconds; strongSelf.showTimeCountdownRemainSeconds = remainSeconds;
FUSLiveShowTimeCollectFrostedState showState = (finalNum >= target ? FUSLiveShowTimeCollectFrostedStateCompleted : FUSLiveShowTimeCollectFrostedStateStarted); FUSLiveShowTimeCollectFrostedState showState = FUSLiveShowTimeCollectFrostedStateStarted;
[currentBar fus_updateAudienceWithState:showState [currentBar fus_updateAudienceWithState:showState
themeText:finalThemeText themeText:finalThemeText
progress:progress progress:progress
remainingText:remainingText remainingText:remainingText
countdownTotalSeconds:totalSeconds
countdownRemainingSeconds:remainSeconds countdownRemainingSeconds:remainSeconds
newTicketCount:0 newTicketCount:0
giftUserModel:model.buyUser giftUserModel:model.buyUser
...@@ -1523,8 +1545,64 @@ ...@@ -1523,8 +1545,64 @@
strongSelf.showTimeAnchorPopupRemainingSeconds = -1; strongSelf.showTimeAnchorPopupRemainingSeconds = -1;
NSInteger stageStatus = MAX(0, (startModel.stageData ? startModel.stageData.showStatus : startModel.showStatus)); NSInteger stageStatus = MAX(0, (startModel.stageData ? startModel.stageData.showStatus : startModel.showStatus));
strongSelf.showTimeStageStatus = stageStatus; strongSelf.showTimeStageStatus = stageStatus;
if (strongSelf.showTimeFrostedView) { FUSLiveShowTimeCollectFrostedView *bar = strongSelf.showTimeFrostedView;
strongSelf.showTimeFrostedView.stageStatus = stageStatus; if (bar) {
bar.stageStatus = stageStatus;
NSInteger target = MAX(0, startModel.targetTicketNum);
NSInteger finalNum = MAX(0, startModel.finalTicketNum);
if (target > 0) {
NSInteger remaining = MAX(0, target - finalNum);
CGFloat progress = MIN(1.0, MAX(0.0, (CGFloat)finalNum / (CGFloat)target));
NSString *remainingText = nil;
if (stageStatus == 2 || stageStatus == 1) {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"已集票数 %zd 张!"], (NSInteger)finalNum];
progress = 1.0;
} else {
remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
}
FUSRoomInfoModel *roomInfoModel = FUSLiveHelper.shareInstance.roomInfoModel;
NSString *themeText = nil;
if (startModel.showTheme.length > 0) {
themeText = startModel.showTheme;
} else if (![NSString isNull:roomInfoModel.introduce]) {
themeText = roomInfoModel.introduce;
} else {
themeText = [NSString fus_localString:@"限时表演"];
}
NSInteger durationMs = (startModel.stageData ? startModel.stageData.showStatusTime : startModel.showStatusTime);
NSInteger remainMs = (startModel.stageData ? startModel.stageData.remaintime : startModel.remaintime);
NSInteger totalSeconds = (durationMs > 0 ? (NSInteger)llround(((NSTimeInterval)durationMs) / 1000.0) : -1);
NSInteger remainSeconds = -1;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0);
}
strongSelf.showTimeCountdownRemainSeconds = remainSeconds;
if (bar.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
FUSLiveShowTimeCollectFrostedState showState = FUSLiveShowTimeCollectFrostedStateStarted;
[bar fus_updateAudienceWithState:showState
themeText:themeText
progress:progress
remainingText:remainingText
countdownTotalSeconds:totalSeconds
countdownRemainingSeconds:remainSeconds
newTicketCount:0
giftUserModel:startModel.buyUser
mvpUserModel:startModel.mvpInfo.mvpUserInfo];
} else {
FUSLiveShowTimeCollectFrostedState showState = FUSLiveShowTimeCollectFrostedStateStarted;
[bar fus_updateWithState:showState
themeText:themeText
progress:progress
remainingText:remainingText
newTicketCount:0
giftUserModel:startModel.buyUser
mvpUserModel:startModel.mvpInfo.mvpUserInfo];
}
}
} }
[FUSDialogView fus_showDialog:[NSString fus_localString:@"已开始表演"]]; [FUSDialogView fus_showDialog:[NSString fus_localString:@"已开始表演"]];
}); });
......
...@@ -55,16 +55,21 @@ typedef NS_ENUM(NSInteger, FUSLiveShowTimeCollectFrostedDisplayMode) { ...@@ -55,16 +55,21 @@ typedef NS_ENUM(NSInteger, FUSLiveShowTimeCollectFrostedDisplayMode) {
mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel; mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel;
/// 用户端更新展示内容(用于用户中途进房时同步倒计时) /// 用户端更新展示内容(用于用户中途进房时同步倒计时)
/// @param countdownRemainingSeconds 距离结束剩余秒数;<0 表示未知,内部按 10 分钟从当前开始计时 /// @param countdownTotalSeconds 当前阶段总时长(秒);<=0 表示未知,内部按 10 分钟兜底
/// @param countdownRemainingSeconds 距离结束剩余秒数;<0 表示未知,内部按总时长从当前开始计时
- (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state - (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText themeText:(NSString *)themeText
progress:(CGFloat)progress progress:(CGFloat)progress
remainingText:(NSString *)remainingText remainingText:(NSString *)remainingText
countdownTotalSeconds:(NSInteger)countdownTotalSeconds
countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds
newTicketCount:(NSInteger)newTicketCount newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel * _Nullable)giftUserModel giftUserModel:(FUSOnlineUserModel * _Nullable)giftUserModel
mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel; mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel;
- (void)fus_updateTopTicketInfoWithGiftUserModel:(FUSOnlineUserModel * _Nullable)giftUserModel
newTicketCount:(NSInteger)newTicketCount;
/// 取消按钮点击回调(预留服务端入口) /// 取消按钮点击回调(预留服务端入口)
@property (nonatomic, copy, nullable) void (^cancelHandler)(void); @property (nonatomic, copy, nullable) void (^cancelHandler)(void);
......
...@@ -46,6 +46,9 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -46,6 +46,9 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 记录上一次进度圈尺寸,避免重复计算 path /// 记录上一次进度圈尺寸,避免重复计算 path
@property (nonatomic, assign) CGFloat lastProgressCircleSize; @property (nonatomic, assign) CGFloat lastProgressCircleSize;
/// 左上角上票信息容器(头像/昵称/等级/VIP/票图标/+N)
@property (nonatomic, strong) UIView *topTicketInfoContainerView;
/// 顶部用户头像 /// 顶部用户头像
@property (nonatomic, strong) UIImageView *avatarView; @property (nonatomic, strong) UIImageView *avatarView;
/// 顶部昵称 /// 顶部昵称
...@@ -58,6 +61,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -58,6 +61,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@property (nonatomic, strong) UIImageView *ticketIconView; @property (nonatomic, strong) UIImageView *ticketIconView;
/// 新票数提示(+N) /// 新票数提示(+N)
@property (nonatomic, strong) UILabel *showTimeNewTicketLabel; @property (nonatomic, strong) UILabel *showTimeNewTicketLabel;
/// 记录最近一次新票数,用于无新票推送时保持展示
@property (nonatomic, assign) NSInteger lastNewTicketCount;
/// 分割线以下内容容器(用于垂直居中进度圈) /// 分割线以下内容容器(用于垂直居中进度圈)
@property (nonatomic, strong) UIView *bottomContentView; @property (nonatomic, strong) UIView *bottomContentView;
...@@ -67,6 +72,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -67,6 +72,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@property (nonatomic, strong) UILabel *statusTagLabel; @property (nonatomic, strong) UILabel *statusTagLabel;
/// 倒计时图标 /// 倒计时图标
@property (nonatomic, strong) UIImageView *countdownIconView; @property (nonatomic, strong) UIImageView *countdownIconView;
/// 表演中:倒计时标题(“表演结束倒计时”)
@property (nonatomic, strong) UILabel *performanceCountdownTitleLabel;
/// 还差票数提示 /// 还差票数提示
@property (nonatomic, strong) UILabel *remainingLabel; @property (nonatomic, strong) UILabel *remainingLabel;
/// 倒计时文本 /// 倒计时文本
...@@ -88,6 +95,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -88,6 +95,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@property (nonatomic, assign) NSTimeInterval startTimestamp; @property (nonatomic, assign) NSTimeInterval startTimestamp;
/// 倒计时基准对应的阶段状态(避免上票推送导致 startTimestamp 反复被覆盖) /// 倒计时基准对应的阶段状态(避免上票推送导致 startTimestamp 反复被覆盖)
@property (nonatomic, assign) NSInteger countdownStageStatus; @property (nonatomic, assign) NSInteger countdownStageStatus;
/// 当前阶段倒计时总时长(秒)
@property (nonatomic, assign) NSInteger countdownTotalSeconds;
/// 倒计时刷新计时器 /// 倒计时刷新计时器
@property (nonatomic, strong) dispatch_source_t countdownTimer; @property (nonatomic, strong) dispatch_source_t countdownTimer;
...@@ -96,6 +105,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -96,6 +105,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@implementation FUSLiveShowTimeCollectFrostedView @implementation FUSLiveShowTimeCollectFrostedView
+ (instancetype)fus_showOnView:(UIView *)onView { + (instancetype)fus_showOnView:(UIView *)onView {
// 功能:在指定父视图底部创建并展示磨砂条;若已存在则复用已存在实例
FUSLiveShowTimeCollectFrostedView *existView = [onView viewWithTag:8817201]; FUSLiveShowTimeCollectFrostedView *existView = [onView viewWithTag:8817201];
if ([existView isKindOfClass:FUSLiveShowTimeCollectFrostedView.class]) { if ([existView isKindOfClass:FUSLiveShowTimeCollectFrostedView.class]) {
return existView; return existView;
...@@ -117,9 +127,11 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -117,9 +127,11 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
- (instancetype)initWithFrame:(CGRect)frame { - (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self) { if (self) {
// 功能:初始化子视图、默认状态与基础布局
self.backgroundColor = UIColor.clearColor; self.backgroundColor = UIColor.clearColor;
_displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAnchor; _displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAnchor;
_countdownStageStatus = -1; _countdownStageStatus = -1;
_countdownTotalSeconds = kFUSShowTimeCollectSeconds;
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
self.blurView = [[UIVisualEffectView alloc] initWithEffect:effect]; self.blurView = [[UIVisualEffectView alloc] initWithEffect:effect];
...@@ -129,11 +141,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -129,11 +141,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
[self.blurView.contentView addSubview:self.progressContainer]; [self.blurView.contentView addSubview:self.progressContainer];
[self fus_setupProgressLayers]; [self fus_setupProgressLayers];
self.topTicketInfoContainerView = [[UIView alloc] init];
self.topTicketInfoContainerView.hidden = YES;
[self.blurView.contentView addSubview:self.topTicketInfoContainerView];
self.avatarView = [[UIImageView alloc] init]; self.avatarView = [[UIImageView alloc] init];
self.avatarView.layer.cornerRadius = 11; self.avatarView.layer.cornerRadius = 11;
self.avatarView.layer.masksToBounds = YES; self.avatarView.layer.masksToBounds = YES;
self.avatarView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.2]; self.avatarView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.2];
[self.blurView.contentView addSubview:self.avatarView]; [self.topTicketInfoContainerView addSubview:self.avatarView];
self.topTitleLabel = [[UILabel alloc] init]; self.topTitleLabel = [[UILabel alloc] init];
self.topTitleLabel.font = [UIFont fus_themeFont:12]; self.topTitleLabel.font = [UIFont fus_themeFont:12];
...@@ -141,7 +157,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -141,7 +157,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.topTitleLabel.text = [NSString fus_localString:@"我是昵称"]; self.topTitleLabel.text = [NSString fus_localString:@"我是昵称"];
self.topTitleLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.topTitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.topTitleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; [self.topTitleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
[self.blurView.contentView addSubview:self.topTitleLabel]; [self.topTicketInfoContainerView addSubview:self.topTitleLabel];
FUSShowTimePaddingLabel *levLabel = [[FUSShowTimePaddingLabel alloc] init]; FUSShowTimePaddingLabel *levLabel = [[FUSShowTimePaddingLabel alloc] init];
levLabel.textInsets = UIEdgeInsetsMake(0, 6, 0, 6); levLabel.textInsets = UIEdgeInsetsMake(0, 6, 0, 6);
...@@ -155,23 +171,24 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -155,23 +171,24 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.levLabel.text = @"0"; self.levLabel.text = @"0";
[self.levLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [self.levLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.levLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [self.levLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.blurView.contentView addSubview:self.levLabel]; [self.topTicketInfoContainerView addSubview:self.levLabel];
self.vipIconView = [[UIImageView alloc] init]; self.vipIconView = [[UIImageView alloc] init];
self.vipIconView.contentMode = UIViewContentModeScaleAspectFit; self.vipIconView.contentMode = UIViewContentModeScaleAspectFit;
self.vipIconView.hidden = YES; self.vipIconView.hidden = YES;
[self.blurView.contentView addSubview:self.vipIconView]; [self.topTicketInfoContainerView addSubview:self.vipIconView];
self.ticketIconView = [[UIImageView alloc] init]; self.ticketIconView = [[UIImageView alloc] init];
self.ticketIconView.image = [FUSShowRoomCenterBunble imageNamed:@"home_list_ticket"]; self.ticketIconView.image = [FUSShowRoomCenterBunble imageNamed:@"home_list_ticket"];
self.ticketIconView.hidden = YES; self.ticketIconView.hidden = YES;
[self.blurView.contentView addSubview:self.ticketIconView]; [self.topTicketInfoContainerView addSubview:self.ticketIconView];
self.showTimeNewTicketLabel = [[UILabel alloc] init]; self.showTimeNewTicketLabel = [[UILabel alloc] init];
self.showTimeNewTicketLabel.font = [UIFont fus_themeBoldFont:12]; self.showTimeNewTicketLabel.font = [UIFont fus_themeBoldFont:12];
self.showTimeNewTicketLabel.textColor = [UIColor colorWithWhite:1 alpha:0.9]; self.showTimeNewTicketLabel.textColor = [UIColor colorWithWhite:1 alpha:0.9];
self.showTimeNewTicketLabel.hidden = YES; self.showTimeNewTicketLabel.hidden = YES;
[self.blurView.contentView addSubview:self.showTimeNewTicketLabel]; [self.topTicketInfoContainerView addSubview:self.showTimeNewTicketLabel];
self.lastNewTicketCount = 0;
self.cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; self.cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.cancelBtn.layer.cornerRadius = 13; self.cancelBtn.layer.cornerRadius = 13;
...@@ -222,6 +239,13 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -222,6 +239,13 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.countdownIconView.image = [FUSShowRoomCenterBunble imageNamed:@"Live_bottom_countdown"]; self.countdownIconView.image = [FUSShowRoomCenterBunble imageNamed:@"Live_bottom_countdown"];
[self.blurView.contentView addSubview:self.countdownIconView]; [self.blurView.contentView addSubview:self.countdownIconView];
self.performanceCountdownTitleLabel = [[UILabel alloc] init];
self.performanceCountdownTitleLabel.hidden = YES;
self.performanceCountdownTitleLabel.font = [UIFont fus_themeFont:12];
self.performanceCountdownTitleLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85];
self.performanceCountdownTitleLabel.text = @"";
[self.blurView.contentView addSubview:self.performanceCountdownTitleLabel];
self.countdownLabel = [[UILabel alloc] init]; self.countdownLabel = [[UILabel alloc] init];
self.countdownLabel.font = [UIFont fus_themeFont:12]; self.countdownLabel.font = [UIFont fus_themeFont:12];
self.countdownLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85]; self.countdownLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85];
...@@ -255,23 +279,6 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -255,23 +279,6 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.edges.equalTo(self); make.edges.equalTo(self);
}]; }];
[self.statusTagLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.progressContainer.mas_right).offset(12);
make.right.equalTo(self.themeLabel.mas_left).offset(-6);
make.centerY.equalTo(self.themeLabel);
make.height.mas_equalTo(16);
}];
[self.statusTagLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.statusTagLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.remainingLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.themeLabel);
make.top.equalTo(self.themeLabel.mas_bottom).offset(6);
make.height.mas_equalTo(18);
}];
[self.remainingLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.remainingLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.mvpTagLabel mas_makeConstraints:^(MASConstraintMaker *make) { [self.mvpTagLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.mvpAvatarView); make.centerX.equalTo(self.mvpAvatarView);
make.bottom.equalTo(self.mvpAvatarView).offset(3); make.bottom.equalTo(self.mvpAvatarView).offset(3);
...@@ -285,9 +292,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -285,9 +292,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.top.greaterThanOrEqualTo(self.bottomContentView).offset(8); make.top.greaterThanOrEqualTo(self.bottomContentView).offset(8);
}]; }];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { [self.topTicketInfoContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.blurView.contentView).offset(12); make.left.equalTo(self.blurView.contentView).offset(12);
make.top.equalTo(self.blurView.contentView).offset(10); make.top.equalTo(self.blurView.contentView).offset(10);
make.height.mas_equalTo(22);
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-8);
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(self.topTicketInfoContainerView);
make.size.mas_equalTo(CGSizeMake(22, 22)); make.size.mas_equalTo(CGSizeMake(22, 22));
}]; }];
...@@ -312,32 +325,17 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -312,32 +325,17 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
[self.ticketIconView mas_makeConstraints:^(MASConstraintMaker *make) { [self.ticketIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.vipIconView.mas_right).offset(6); make.left.equalTo(self.vipIconView.mas_right).offset(6);
make.centerY.equalTo(self.avatarView).offset(-0.5); make.centerY.equalTo(self.topTitleLabel);
make.size.mas_equalTo(CGSizeMake(23, 13.5)); make.size.mas_equalTo(CGSizeMake(23, 13.5));
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-4); make.right.lessThanOrEqualTo(self.topTicketInfoContainerView);
}]; }];
[self.showTimeNewTicketLabel mas_makeConstraints:^(MASConstraintMaker *make) { [self.showTimeNewTicketLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.ticketIconView.mas_right).offset(4); make.left.equalTo(self.ticketIconView.mas_right).offset(4);
make.centerY.equalTo(self.avatarView).offset(-0.5); make.centerY.equalTo(self.topTitleLabel);
make.height.mas_equalTo(18);
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-8);
}];
[self.countdownIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.remainingLabel.mas_right).offset(8);
make.centerY.equalTo(self.remainingLabel);
make.size.mas_equalTo(CGSizeMake(16, 16));
}];
[self.countdownLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.countdownIconView.mas_right).offset(4);
make.centerY.equalTo(self.remainingLabel);
make.height.mas_equalTo(18); make.height.mas_equalTo(18);
make.width.mas_equalTo(44); make.right.lessThanOrEqualTo(self.topTicketInfoContainerView);
}]; }];
[self.countdownLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.countdownLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self fus_applyLayoutForDisplayMode]; [self fus_applyLayoutForDisplayMode];
...@@ -358,11 +356,13 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -358,11 +356,13 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
- (void)layoutSubviews { - (void)layoutSubviews {
[super layoutSubviews]; [super layoutSubviews];
// 功能:视图尺寸变化时重新设置进度圈路径与图层
[self fus_setupProgressLayers]; [self fus_setupProgressLayers];
} }
/// 配置左侧进度圈(用于展示集票进度) /// 配置左侧进度圈(用于展示集票进度)
- (void)fus_setupProgressLayers { - (void)fus_setupProgressLayers {
// 功能:创建/更新进度圈底轨、进度层与居中文本;根据容器尺寸计算圆弧路径
CGFloat size = MIN(CGRectGetWidth(self.progressContainer.bounds), CGRectGetHeight(self.progressContainer.bounds)); CGFloat size = MIN(CGRectGetWidth(self.progressContainer.bounds), CGRectGetHeight(self.progressContainer.bounds));
CGFloat lineW = 4.0; CGFloat lineW = 4.0;
...@@ -430,6 +430,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -430,6 +430,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
remainingText:(NSString *)remainingText remainingText:(NSString *)remainingText
newTicketCount:(NSInteger)newTicketCount newTicketCount:(NSInteger)newTicketCount
userModel:(FUSOnlineUserModel *)userModel { userModel:(FUSOnlineUserModel *)userModel {
// 功能:兼容旧调用入口,转调到带 gift/mvp 的新方法
[self fus_updateWithState:state [self fus_updateWithState:state
themeText:themeText themeText:themeText
progress:progress progress:progress
...@@ -446,6 +447,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -446,6 +447,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
newTicketCount:(NSInteger)newTicketCount newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel *)giftUserModel giftUserModel:(FUSOnlineUserModel *)giftUserModel
mvpUserModel:(FUSOnlineUserModel *)mvpUserModel { mvpUserModel:(FUSOnlineUserModel *)mvpUserModel {
// 功能:刷新主题、MVP/赠票用户展示、集票进度与剩余文案,并维护倒计时状态
NSString *decodedThemeText = nil; NSString *decodedThemeText = nil;
if (themeText.length > 0) { if (themeText.length > 0) {
decodedThemeText = themeText.stringByRemovingPercentEncoding ?: themeText; decodedThemeText = themeText.stringByRemovingPercentEncoding ?: themeText;
...@@ -470,13 +472,63 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -470,13 +472,63 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.size.mas_equalTo(self.mvpAvatarView.hidden ? CGSizeZero : CGSizeMake(42, 42)); make.size.mas_equalTo(self.mvpAvatarView.hidden ? CGSizeZero : CGSizeMake(42, 42));
}]; }];
if (giftUserModel) { CGFloat clamped = MIN(1.0, MAX(0.0, progress));
self.avatarView.hidden = NO; self.progressLayer.strokeEnd = clamped;
self.topTitleLabel.hidden = NO; if (self.stageStatus == 2) {
self.levLabel.hidden = NO; self.progressLabel.text = [NSString fus_localString:@"表演中"];
} else {
self.progressLabel.text = [NSString stringWithFormat:@"%zd%%", (NSInteger)llround(clamped * 100)];
}
self.remainingLabel.text = (remainingText.length > 0 ? remainingText : @"");
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
self.startTimestamp = 0;
self.countdownStageStatus = -1;
} else {
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
if (self.startTimestamp <= 0) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
} else if (self.countdownStageStatus != self.stageStatus) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
}
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
[self fus_stopCountdownTimer];
} else {
[self fus_startCountdownTimerIfNeeded];
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
self.remainingLabel.text = [NSString fus_localString:@"已集票满 50 张!"];
self.progressLayer.strokeEnd = 1.0;
self.progressLabel.text = @"100%";
self.countdownLabel.text = @"00:00";
}
}
- (void)fus_updateTopTicketInfoWithGiftUserModel:(FUSOnlineUserModel *)giftUserModel
newTicketCount:(NSInteger)newTicketCount {
BOOL hasGiftUser = (giftUserModel != nil);
self.topTicketInfoContainerView.hidden = !hasGiftUser;
if (!hasGiftUser) {
self.topTitleLabel.text = [NSString fus_localString:@""];
self.levLabel.text = @"0";
self.vipIconView.image = nil;
self.vipIconView.hidden = YES;
[self.vipIconView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
self.lastNewTicketCount = 0;
self.ticketIconView.hidden = YES;
self.showTimeNewTicketLabel.hidden = YES;
return;
}
NSString *nickname = (giftUserModel.nickname.length > 0 ? giftUserModel.nickname : giftUserModel.uid); NSString *nickname = (giftUserModel.nickname.length > 0 ? giftUserModel.nickname : giftUserModel.uid);
self.topTitleLabel.text = (nickname.length > 0 ? nickname : [NSString fus_localString:@""]); NSString *decodedNickname = (nickname.length > 0 ? (nickname.stringByRemovingPercentEncoding ?: nickname) : @"");
self.topTitleLabel.text = (decodedNickname.length > 0 ? decodedNickname : [NSString fus_localString:@""]);
NSString *levText = (giftUserModel.lev.length > 0 ? giftUserModel.lev : @"0"); NSString *levText = (giftUserModel.lev.length > 0 ? giftUserModel.lev : @"0");
self.levLabel.text = levText; self.levLabel.text = levText;
...@@ -510,66 +562,23 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -510,66 +562,23 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
if (giftUserModel.face.length > 0) { if (giftUserModel.face.length > 0) {
[self.avatarView setWebImageWithSubURLString:giftUserModel.face placeholder:nil]; [self.avatarView setWebImageWithSubURLString:giftUserModel.face placeholder:nil];
} }
} else {
self.avatarView.hidden = YES;
self.topTitleLabel.hidden = YES;
self.levLabel.hidden = YES;
self.vipIconView.image = nil;
self.vipIconView.hidden = YES;
[self.vipIconView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
}
CGFloat clamped = MIN(1.0, MAX(0.0, progress)); self.lastNewTicketCount = MAX(0, (NSInteger)newTicketCount);
self.progressLayer.strokeEnd = clamped;
self.progressLabel.text = [NSString stringWithFormat:@"%zd%%", (NSInteger)llround(clamped * 100)];
self.remainingLabel.text = (remainingText.length > 0 ? remainingText : @"");
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
self.startTimestamp = 0;
self.countdownStageStatus = -1;
} else {
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
if (self.startTimestamp <= 0) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
} else if (self.countdownStageStatus != self.stageStatus) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
}
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
[self fus_stopCountdownTimer];
} else {
[self fus_startCountdownTimerIfNeeded];
}
if (giftUserModel && newTicketCount > 0) {
self.ticketIconView.hidden = NO; self.ticketIconView.hidden = NO;
self.showTimeNewTicketLabel.hidden = NO; self.showTimeNewTicketLabel.hidden = NO;
self.showTimeNewTicketLabel.text = [NSString stringWithFormat:@"x%zd", (NSInteger)newTicketCount]; self.showTimeNewTicketLabel.text = [NSString stringWithFormat:@"+%zd", (NSInteger)MAX(0, self.lastNewTicketCount)];
} else {
self.ticketIconView.hidden = YES;
self.showTimeNewTicketLabel.hidden = YES;
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
self.remainingLabel.text = [NSString fus_localString:@"已集票满 50 张!"];
self.progressLayer.strokeEnd = 1.0;
self.progressLabel.text = @"100%";
self.countdownLabel.text = @"00:00";
}
} }
- (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state - (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText themeText:(NSString *)themeText
progress:(CGFloat)progress progress:(CGFloat)progress
remainingText:(NSString *)remainingText remainingText:(NSString *)remainingText
countdownTotalSeconds:(NSInteger)countdownTotalSeconds
countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds
newTicketCount:(NSInteger)newTicketCount newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel *)giftUserModel giftUserModel:(FUSOnlineUserModel *)giftUserModel
mvpUserModel:(FUSOnlineUserModel *)mvpUserModel { mvpUserModel:(FUSOnlineUserModel *)mvpUserModel {
// 功能:用户端同步倒计时基准;使用服务端给定的当前阶段总时长与剩余秒数
[self fus_updateWithState:state [self fus_updateWithState:state
themeText:themeText themeText:themeText
progress:progress progress:progress
...@@ -588,9 +597,11 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -588,9 +597,11 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
return; return;
} }
NSInteger clampedRemain = MAX(0, MIN(kFUSShowTimeCollectSeconds, countdownRemainingSeconds)); NSInteger totalSeconds = (countdownTotalSeconds > 0 ? countdownTotalSeconds : kFUSShowTimeCollectSeconds);
self.countdownTotalSeconds = totalSeconds;
NSInteger clampedRemain = MAX(0, MIN(totalSeconds, countdownRemainingSeconds));
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970]; NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
NSTimeInterval base = nowSec - MAX(0, (NSTimeInterval)kFUSShowTimeCollectSeconds - (NSTimeInterval)clampedRemain); NSTimeInterval base = nowSec - MAX(0, (NSTimeInterval)totalSeconds - (NSTimeInterval)clampedRemain);
if (self.startTimestamp <= 0) { if (self.startTimestamp <= 0) {
self.startTimestamp = base; self.startTimestamp = base;
...@@ -609,22 +620,26 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -609,22 +620,26 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
- (void)setCancelHandler:(void (^)(void))cancelHandler { - (void)setCancelHandler:(void (^)(void))cancelHandler {
_cancelHandler = [cancelHandler copy]; _cancelHandler = [cancelHandler copy];
// 功能:设置取消回调后刷新按钮显隐
[self fus_refreshActionButtonVisibility]; [self fus_refreshActionButtonVisibility];
} }
- (void)setDisplayMode:(FUSLiveShowTimeCollectFrostedDisplayMode)displayMode { - (void)setDisplayMode:(FUSLiveShowTimeCollectFrostedDisplayMode)displayMode {
_displayMode = displayMode; _displayMode = displayMode;
// 功能:切换展示模式并重新应用布局与按钮显隐
[self fus_applyLayoutForDisplayMode]; [self fus_applyLayoutForDisplayMode];
[self fus_refreshActionButtonVisibility]; [self fus_refreshActionButtonVisibility];
} }
- (void)setActionHandler:(void (^)(void))actionHandler { - (void)setActionHandler:(void (^)(void))actionHandler {
_actionHandler = [actionHandler copy]; _actionHandler = [actionHandler copy];
// 功能:设置用户端按钮点击回调后刷新按钮显隐
[self fus_refreshActionButtonVisibility]; [self fus_refreshActionButtonVisibility];
} }
- (void)setActionTitle:(NSString *)actionTitle { - (void)setActionTitle:(NSString *)actionTitle {
_actionTitle = [actionTitle copy]; _actionTitle = [actionTitle copy];
// 功能:设置用户端按钮文案(购票支持/抢当MVP)
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience && actionTitle.length > 0) { if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience && actionTitle.length > 0) {
[self.cancelBtn setTitle:actionTitle forState:UIControlStateNormal]; [self.cancelBtn setTitle:actionTitle forState:UIControlStateNormal];
} }
...@@ -633,23 +648,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -633,23 +648,15 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
- (void)setStageStatus:(NSInteger)stageStatus { - (void)setStageStatus:(NSInteger)stageStatus {
_stageStatus = stageStatus; _stageStatus = stageStatus;
if (self.displayMode != FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
self.statusTagLabel.text = @""; self.statusTagLabel.text = @"";
self.statusTagLabel.hidden = YES; self.statusTagLabel.hidden = YES;
return; [self fus_applyBottomInfoLayout];
} [self fus_refreshActionButtonVisibility];
NSString *text = @"";
if (stageStatus == 2) {
text = [NSString fus_localString:@"表演中"];
} else if (stageStatus == 1) {
text = [NSString fus_localString:@"待表演"];
}
self.statusTagLabel.text = text;
self.statusTagLabel.hidden = (text.length == 0);
} }
/// 开始倒计时刷新(每秒刷新显示,触底自动停止) /// 开始倒计时刷新(每秒刷新显示,触底自动停止)
- (void)fus_startCountdownTimerIfNeeded { - (void)fus_startCountdownTimerIfNeeded {
// 功能:启动倒计时定时器(主线程每秒刷新),无有效起始时间则停止
if (self.startTimestamp <= 0) { if (self.startTimestamp <= 0) {
[self fus_stopCountdownTimer]; [self fus_stopCountdownTimer];
self.countdownLabel.text = @""; self.countdownLabel.text = @"";
...@@ -674,6 +681,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -674,6 +681,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 停止倒计时刷新 /// 停止倒计时刷新
- (void)fus_stopCountdownTimer { - (void)fus_stopCountdownTimer {
// 功能:停止倒计时定时器并释放资源
if (self.countdownTimer) { if (self.countdownTimer) {
dispatch_source_cancel(self.countdownTimer); dispatch_source_cancel(self.countdownTimer);
self.countdownTimer = nil; self.countdownTimer = nil;
...@@ -682,6 +690,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -682,6 +690,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 刷新倒计时显示 /// 刷新倒计时显示
- (void)fus_refreshCountdownText { - (void)fus_refreshCountdownText {
// 功能:根据总时长与已用时计算剩余时间;触底后显示 00:00 并停止定时器
if (self.startTimestamp <= 0) { if (self.startTimestamp <= 0) {
self.countdownLabel.text = @""; self.countdownLabel.text = @"";
[self fus_stopCountdownTimer]; [self fus_stopCountdownTimer];
...@@ -690,7 +699,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -690,7 +699,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
NSTimeInterval elapsed = MAX(0, now - self.startTimestamp); NSTimeInterval elapsed = MAX(0, now - self.startTimestamp);
NSInteger remain = (NSInteger)llround(MAX(0, (NSTimeInterval)kFUSShowTimeCollectSeconds - elapsed)); NSInteger totalSeconds = (self.countdownTotalSeconds > 0 ? self.countdownTotalSeconds : kFUSShowTimeCollectSeconds);
NSInteger remain = (NSInteger)llround(MAX(0, (NSTimeInterval)totalSeconds - elapsed));
if (remain <= 0) { if (remain <= 0) {
self.countdownLabel.text = @"00:00"; self.countdownLabel.text = @"00:00";
[self fus_stopCountdownTimer]; [self fus_stopCountdownTimer];
...@@ -704,6 +714,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -704,6 +714,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 点击取消:预留给服务端取消接口 /// 点击取消:预留给服务端取消接口
- (void)fus_onClickCancel { - (void)fus_onClickCancel {
// 功能:处理用户端按钮点击或主播端“取消集票”,未接好后端时给出提示
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) { if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
if (self.actionHandler) { if (self.actionHandler) {
self.actionHandler(); self.actionHandler();
...@@ -722,6 +733,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -722,6 +733,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 根据展示模式调整布局(主播端/用户端差异) /// 根据展示模式调整布局(主播端/用户端差异)
- (void)fus_applyLayoutForDisplayMode { - (void)fus_applyLayoutForDisplayMode {
// 功能:应用顶部操作区与底部容器的通用布局,随后按模式/阶段细分底部信息布局
self.topDividerView.hidden = NO; self.topDividerView.hidden = NO;
[self.cancelBtn mas_remakeConstraints:^(MASConstraintMaker *make) { [self.cancelBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
...@@ -747,29 +759,75 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -747,29 +759,75 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.size.mas_equalTo(CGSizeMake(42, 42)); make.size.mas_equalTo(CGSizeMake(42, 42));
}]; }];
[self.themeLabel mas_remakeConstraints:^(MASConstraintMaker *make) { [self.mvpIconView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeZero);
make.top.left.equalTo(self.blurView.contentView);
}];
[self fus_applyBottomInfoLayout];
[self setNeedsLayout];
[self layoutIfNeeded];
[self.themeLabel refreshLabels];
}
- (void)fus_applyBottomInfoLayout {
// 功能:统一主播/观众底部信息排版,仅保留按钮显隐差异
BOOL isPerformance = NO;
self.performanceCountdownTitleLabel.hidden = YES;
self.themeLabel.hidden = NO;
self.performanceCountdownTitleLabel.text = @"";
self.countdownIconView.hidden = NO;
UIView *topLineView = self.themeLabel;
[self.statusTagLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.progressContainer.mas_right).offset(12); make.left.equalTo(self.progressContainer.mas_right).offset(12);
make.centerY.equalTo(topLineView);
make.height.mas_equalTo(16);
}];
[self.statusTagLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.statusTagLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
BOOL hasStatusTag = NO;
[topLineView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(hasStatusTag ? self.statusTagLabel.mas_right : self.progressContainer.mas_right).offset(hasStatusTag ? 6 : 12);
make.top.equalTo(self.bottomContentView).offset(8); make.top.equalTo(self.bottomContentView).offset(8);
make.height.mas_equalTo(22);
make.right.equalTo(self.mvpAvatarView.mas_left).offset(-12); make.right.equalTo(self.mvpAvatarView.mas_left).offset(-12);
make.height.mas_equalTo(22);
}]; }];
[self.countdownLabel mas_updateConstraints:^(MASConstraintMaker *make) { [self.remainingLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.lessThanOrEqualTo(self.mvpAvatarView.mas_left).offset(-8); make.left.equalTo(self.themeLabel);
make.top.equalTo(topLineView.mas_bottom).offset(6);
make.height.mas_equalTo(18);
make.right.lessThanOrEqualTo(self.mvpAvatarView.mas_left).offset(-12);
}]; }];
[self.remainingLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.remainingLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.mvpIconView mas_remakeConstraints:^(MASConstraintMaker *make) { [self.countdownIconView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeZero); make.left.equalTo(self.remainingLabel.mas_right).offset(8);
make.top.left.equalTo(self.blurView.contentView); make.centerY.equalTo(self.remainingLabel);
make.size.mas_equalTo(CGSizeMake(16, 16));
}]; }];
[self.countdownLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.countdownIconView.mas_right).offset(4);
make.centerY.equalTo(self.remainingLabel);
make.height.mas_equalTo(18);
make.width.mas_equalTo(44);
make.right.lessThanOrEqualTo(self.mvpAvatarView.mas_left).offset(-8);
}];
[self.countdownLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.countdownLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self setNeedsLayout]; [self setNeedsLayout];
[self layoutIfNeeded];
[self.themeLabel refreshLabels];
} }
/// 刷新按钮显隐与文案(避免用户端 cancelHandler=nil 时按钮被隐藏) /// 刷新按钮显隐与文案(避免用户端 cancelHandler=nil 时按钮被隐藏)
- (void)fus_refreshActionButtonVisibility { - (void)fus_refreshActionButtonVisibility {
// 功能:根据展示模式与是否设置回调/文案决定按钮显隐与文案
BOOL showBtn = NO; BOOL showBtn = NO;
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) { if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
showBtn = (self.actionHandler != nil || self.actionTitle.length > 0); showBtn = (self.actionHandler != nil || self.actionTitle.length > 0);
...@@ -782,15 +840,25 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60; ...@@ -782,15 +840,25 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
showBtn = (self.cancelHandler != nil); showBtn = (self.cancelHandler != nil);
[self.cancelBtn setTitle:[NSString fus_localString:@"取消集票"] forState:UIControlStateNormal]; [self.cancelBtn setTitle:[NSString fus_localString:@"取消集票"] forState:UIControlStateNormal];
} }
if (self.stageStatus == 2) {
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAnchor) {
showBtn = NO;
} else {
// 观众端表演中保留入口(购票支持/抢当MVP)
showBtn = (self.actionHandler != nil || self.actionTitle.length > 0);
}
}
self.cancelBtn.hidden = !showBtn; self.cancelBtn.hidden = !showBtn;
} }
- (void)fus_dismiss { - (void)fus_dismiss {
// 功能:移除磨砂条前停止倒计时
[self fus_stopCountdownTimer]; [self fus_stopCountdownTimer];
[self removeFromSuperview]; [self removeFromSuperview];
} }
- (void)dealloc { - (void)dealloc {
// 功能:对象释放时确保停止倒计时,避免计时器泄漏
[self fus_stopCountdownTimer]; [self fus_stopCountdownTimer];
} }
......
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class FUSShowRoomUserContributeModel;
/**
弹窗视图:票的贡献列表底部上滑弹窗
设计目标:
- 与现有底部弹窗交互一致(半透明遮罩、白底圆角、右上角关闭)
- 内部复用既有贡献列表样式与cell,保持一致的排版与风格
使用方式:
- 调用 fus_showOnView: 展示到指定父视图
- 通过 fus_updateTotalCount: 更新标题上的贡献总数
- 通过 fus_updateContributionList: 更新列表数据
- 调用 fus_dismiss 主动关闭
*/
@interface FUSLiveShowTimeTicketContributionPopView : UIView
/// 展示到指定父视图(已做同类弹窗复用处理)
/// - Parameter onView: 作为承载容器的父视图
/// - Returns: 弹窗实例(可能是复用已有实例)
+ (instancetype)fus_showOnView:(UIView *)onView;
/// 更新标题右侧的贡献总数展示(当 totalCount <= 0 时只显示“票的贡献”)
/// - Parameter totalCount: 贡献总数
- (void)fus_updateTotalCount:(NSInteger)totalCount;
/// 更新列表数据(内部复用现有 FUSLiveShowTimeTicketContributionListView)
/// - Parameter contributionList: 贡献用户数据列表
- (void)fus_updateContributionList:(NSArray<FUSShowRoomUserContributeModel *> *)contributionList;
/// 关闭弹窗(含下滑动画与移除)
- (void)fus_dismiss;
@end
NS_ASSUME_NONNULL_END
#import "FUSLiveShowTimeTicketContributionPopView.h"
#import <Masonry/Masonry.h>
#import <FUSCommon/FUSCommon.h>
#import <FUSFoundation/FUSFoundation.h>
#import "FUSShowRoomCenterBunble.h"
#import "FUSLiveShowTimeTicketContributionListView.h"
static const NSInteger kFUSShowTimeTicketContributionPopViewTag = 8817316;
@interface FUSLiveShowTimeTicketContributionPopView ()
/// 背景遮罩按钮(点击关闭)
@property (nonatomic, strong) UIButton *bgBtn;
/// 底部内容容器(白底圆角)
@property (nonatomic, strong) UIView *contentView;
/// 内容容器底约束(用于控制上/下滑动画)
@property (nonatomic, strong) MASConstraint *contentBottomConstraint;
/// 标题标签(显示“票的贡献”与总数)
@property (nonatomic, strong) UILabel *titleLabel;
/// 关闭按钮(右上角 X)
@property (nonatomic, strong) UIButton *closeBtn;
/// 顶部分割线(标题与列表的分隔)
@property (nonatomic, strong) UIView *headerDividerView;
/// 贡献列表视图(复用既有列表与cell)
@property (nonatomic, strong) FUSLiveShowTimeTicketContributionListView *listView;
/// 当前总贡献数(用于标题文案)
@property (nonatomic, assign) NSInteger totalCount;
@end
@implementation FUSLiveShowTimeTicketContributionPopView
+ (instancetype)fus_showOnView:(UIView *)onView {
if (!onView) {
return nil;
}
FUSLiveShowTimeTicketContributionPopView *existView = [onView viewWithTag:kFUSShowTimeTicketContributionPopViewTag];
if ([existView isKindOfClass:FUSLiveShowTimeTicketContributionPopView.class]) {
[onView bringSubviewToFront:existView];
[existView fus_show];
return existView;
}
FUSLiveShowTimeTicketContributionPopView *view = [[FUSLiveShowTimeTicketContributionPopView alloc] initWithFrame:CGRectZero];
view.tag = kFUSShowTimeTicketContributionPopViewTag;
[onView addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(onView);
}];
[view fus_show];
return view;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) {
return nil;
}
self.totalCount = 0;
// 遮罩与点击关闭
self.bgBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.bgBtn.backgroundColor = [UIColor colorWithWhite:0 alpha:0.35];
[self.bgBtn addTarget:self action:@selector(fus_dismiss) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.bgBtn];
// 计算内容高度:最大 520 或屏高 * 0.85,再加安全区底部
CGFloat safeBottom = UIView.fus_SafeBottom;
CGFloat maxContentH = MIN(520, UIView.fus_screenH * 0.85);
CGFloat contentH = maxContentH + safeBottom;
// 底部内容容器:白底、仅顶部圆角,保持与全局弹窗一致
self.contentView = [[UIView alloc] initWithFrame:CGRectZero];
self.contentView.backgroundColor = UIColor.whiteColor;
self.contentView.layer.cornerRadius = 16;
self.contentView.layer.masksToBounds = YES;
if (@available(iOS 11.0, *)) {
self.contentView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
}
[self addSubview:self.contentView];
// 标题与关闭按钮
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.titleLabel.font = [UIFont fus_themeBoldFont:16];
self.titleLabel.textColor = [UIColor colorWithHex:@"#333333"];
self.titleLabel.text = [NSString fus_localString:@"票的贡献"];
[self.contentView addSubview:self.titleLabel];
self.closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.closeBtn setImage:[FUSShowRoomCenterBunble imageNamed:@"live_board_close"] forState:UIControlStateNormal];
[self.closeBtn addTarget:self action:@selector(fus_dismiss) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.closeBtn];
// 顶部分割线
self.headerDividerView = [[UIView alloc] initWithFrame:CGRectZero];
self.headerDividerView.backgroundColor = [UIColor colorWithHex:@"#F2F3F5"];
[self.contentView addSubview:self.headerDividerView];
// 列表(复用现有 ListView 与 Cell,确保样式一致)
self.listView = [[FUSLiveShowTimeTicketContributionListView alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:self.listView];
// Masonry 约束:遮罩覆盖全屏
[self.bgBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
// Masonry 约束:内容容器初始位于屏幕下方,通过 bottom 约束控制动画
[self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self);
make.height.mas_equalTo(contentH);
self.contentBottomConstraint = make.bottom.equalTo(self).offset(contentH);
}];
// Masonry 约束:右上角关闭按钮固定 44x44,标题居中对齐关闭按钮
[self.closeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.contentView);
make.top.equalTo(self.contentView);
make.width.height.mas_equalTo(44);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.centerY.equalTo(self.closeBtn);
make.right.lessThanOrEqualTo(self.closeBtn.mas_left).offset(-8);
make.height.mas_equalTo(22);
}];
// Masonry 约束:分割线位于标题下方
[self.headerDividerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.contentView);
make.top.equalTo(self.closeBtn.mas_bottom);
make.height.mas_equalTo(0.5);
}];
// Masonry 约束:列表填充剩余区域,底部让出安全区
[self.listView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.contentView);
make.top.equalTo(self.headerDividerView.mas_bottom);
make.bottom.equalTo(self.contentView).offset(-safeBottom);
}];
return self;
}
- (void)fus_updateTotalCount:(NSInteger)totalCount {
self.totalCount = MAX(0, totalCount);
if (self.totalCount > 0) {
self.titleLabel.text = [NSString stringWithFormat:@"%@ %zd", [NSString fus_localString:@"票的贡献"], (NSInteger)self.totalCount];
} else {
self.titleLabel.text = [NSString fus_localString:@"票的贡献"];
}
}
- (void)fus_updateContributionList:(NSArray<FUSShowRoomUserContributeModel *> *)contributionList {
self.listView.contributionList = (contributionList ?: @[]);
}
- (void)fus_show {
// 上滑动画:将 bottom 约束置 0,使内容容器进入可视区域
[self.superview layoutIfNeeded];
[self.contentBottomConstraint setOffset:0];
[UIView animateWithDuration:0.3 animations:^{
[self.superview layoutIfNeeded];
}];
}
- (void)fus_dismiss {
// 下滑动画:恢复初始 bottom 偏移,结束后移除
CGFloat safeBottom = UIView.fus_SafeBottom;
CGFloat maxContentH = MIN(520, UIView.fus_screenH * 0.85);
CGFloat contentH = maxContentH + safeBottom;
[self.contentBottomConstraint setOffset:contentH];
[UIView animateWithDuration:0.25 animations:^{
self.bgBtn.alpha = 0;
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment