Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
galaxy-iOS
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mobile-group
galaxy-iOS
Commits
613ab8a6
Commit
613ab8a6
authored
Nov 30, 2024
by
Alex朱枝文
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
直播、录播、banner等播放器管理类
parent
2a71c649
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1707 additions
and
451 deletions
+1707
-451
project.pbxproj
galaxy/galaxy.xcodeproj/project.pbxproj
+20
-8
YHBasePlayerViewController.swift
.../LivestreamSales(直播销售)/C/YHBasePlayerViewController.swift
+165
-145
YHFloatingWindow.swift
...es/Modules/LivestreamSales(直播销售)/C/YHFloatingWindow.swift
+9
-0
YHLivePlayerViewController.swift
.../LivestreamSales(直播销售)/C/YHLivePlayerViewController.swift
+194
-135
YHPlayer.swift
...xy/Classes/Modules/LivestreamSales(直播销售)/C/YHPlayer.swift
+79
-0
YHPlayerManager.swift
...ses/Modules/LivestreamSales(直播销售)/C/YHPlayerManager.swift
+424
-0
YHPlayerTransitionAnimator.swift
.../LivestreamSales(直播销售)/C/YHPlayerTransitionAnimator.swift
+279
-0
YHVODPlayerViewController.swift
...s/LivestreamSales(直播销售)/C/YHVODPlayerViewController.swift
+75
-137
YHIMHelper.swift
...ses/Modules/LivestreamSales(直播销售)/IM(环信)/YHIMHelper.swift
+1
-1
YHFloatingWindow.swift
...es/Modules/LivestreamSales(直播销售)/V/YHFloatingWindow.swift
+383
-0
YHPlayerControlView.swift
...Modules/LivestreamSales(直播销售)/V/YHPlayerControlView.swift
+78
-25
live_room_test_bg.png
...ses/Modules/LivestreamSales(直播销售)/V/live_room_test_bg.png
+0
-0
No files found.
galaxy/galaxy.xcodeproj/project.pbxproj
View file @
613ab8a6
...
@@ -302,7 +302,6 @@
...
@@ -302,7 +302,6 @@
04564D532CF38FE2004456E4
/* YHVODPlayerViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
;
};
04564D532CF38FE2004456E4
/* YHVODPlayerViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
;
};
04564D562CF4467B004456E4
/* YHPlayerTopBarView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
;
};
04564D562CF4467B004456E4
/* YHPlayerTopBarView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
;
};
04564D592CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D582CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift */
;
};
04564D592CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D582CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift */
;
};
04564D5D2CF49F0A004456E4
/* live_room_test_bg.png in Resources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D5C2CF49F0A004456E4
/* live_room_test_bg.png */
;
};
04564D5F2CF565C7004456E4
/* YHInputBottomBar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
;
};
04564D5F2CF565C7004456E4
/* YHInputBottomBar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
;
};
04564D612CF59835004456E4
/* YHMessageInputViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
;
};
04564D612CF59835004456E4
/* YHMessageInputViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
;
};
04564D632CF60222004456E4
/* YHGradientView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D622CF60222004456E4
/* YHGradientView.swift */
;
};
04564D632CF60222004456E4
/* YHGradientView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D622CF60222004456E4
/* YHGradientView.swift */
;
};
...
@@ -313,6 +312,10 @@
...
@@ -313,6 +312,10 @@
04564D6E2CF6EB3D004456E4
/* YHHuanXinUserModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D6D2CF6EB3D004456E4
/* YHHuanXinUserModel.swift */
;
};
04564D6E2CF6EB3D004456E4
/* YHHuanXinUserModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D6D2CF6EB3D004456E4
/* YHHuanXinUserModel.swift */
;
};
04564D702CF6EC8A004456E4
/* YHRecordedDetailModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D6F2CF6EC8A004456E4
/* YHRecordedDetailModel.swift */
;
};
04564D702CF6EC8A004456E4
/* YHRecordedDetailModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D6F2CF6EC8A004456E4
/* YHRecordedDetailModel.swift */
;
};
04564D722CF6F18A004456E4
/* YHIMHelper.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D712CF6F18A004456E4
/* YHIMHelper.swift */
;
};
04564D722CF6F18A004456E4
/* YHIMHelper.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D712CF6F18A004456E4
/* YHIMHelper.swift */
;
};
04564D7A2CF8CEF0004456E4
/* YHPlayerManager.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D792CF8CEF0004456E4
/* YHPlayerManager.swift */
;
};
04564D7C2CF8CF6D004456E4
/* YHPlayer.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D7B2CF8CF6D004456E4
/* YHPlayer.swift */
;
};
04564D7E2CF8D03D004456E4
/* YHFloatingWindow.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D7D2CF8D03D004456E4
/* YHFloatingWindow.swift */
;
};
04564D802CF8E16C004456E4
/* YHPlayerTransitionAnimator.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
04564D7F2CF8E16C004456E4
/* YHPlayerTransitionAnimator.swift */
;
};
0457920B2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920A2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift */
;
};
0457920B2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920A2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift */
;
};
0457920D2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920C2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift */
;
};
0457920D2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920C2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift */
;
};
0457920F2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920E2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift */
;
};
0457920F2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
0457920E2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift */
;
};
...
@@ -1367,7 +1370,6 @@
...
@@ -1367,7 +1370,6 @@
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHVODPlayerViewController.swift
;
sourceTree
=
"<group>"
;
};
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHVODPlayerViewController.swift
;
sourceTree
=
"<group>"
;
};
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHPlayerTopBarView.swift
;
sourceTree
=
"<group>"
;
};
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHPlayerTopBarView.swift
;
sourceTree
=
"<group>"
;
};
04564D582CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHIncomeRecordCompanyTipsCell.swift
;
sourceTree
=
"<group>"
;
};
04564D582CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHIncomeRecordCompanyTipsCell.swift
;
sourceTree
=
"<group>"
;
};
04564D5C2CF49F0A004456E4
/* live_room_test_bg.png */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
image.png
;
path
=
live_room_test_bg.png
;
sourceTree
=
"<group>"
;
};
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHInputBottomBar.swift
;
sourceTree
=
"<group>"
;
};
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHInputBottomBar.swift
;
sourceTree
=
"<group>"
;
};
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHMessageInputViewController.swift
;
sourceTree
=
"<group>"
;
};
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHMessageInputViewController.swift
;
sourceTree
=
"<group>"
;
};
04564D622CF60222004456E4
/* YHGradientView.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHGradientView.swift
;
sourceTree
=
"<group>"
;
};
04564D622CF60222004456E4
/* YHGradientView.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHGradientView.swift
;
sourceTree
=
"<group>"
;
};
...
@@ -1378,6 +1380,10 @@
...
@@ -1378,6 +1380,10 @@
04564D6D2CF6EB3D004456E4
/* YHHuanXinUserModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHHuanXinUserModel.swift
;
sourceTree
=
"<group>"
;
};
04564D6D2CF6EB3D004456E4
/* YHHuanXinUserModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHHuanXinUserModel.swift
;
sourceTree
=
"<group>"
;
};
04564D6F2CF6EC8A004456E4
/* YHRecordedDetailModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHRecordedDetailModel.swift
;
sourceTree
=
"<group>"
;
};
04564D6F2CF6EC8A004456E4
/* YHRecordedDetailModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHRecordedDetailModel.swift
;
sourceTree
=
"<group>"
;
};
04564D712CF6F18A004456E4
/* YHIMHelper.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHIMHelper.swift
;
sourceTree
=
"<group>"
;
};
04564D712CF6F18A004456E4
/* YHIMHelper.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHIMHelper.swift
;
sourceTree
=
"<group>"
;
};
04564D792CF8CEF0004456E4
/* YHPlayerManager.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHPlayerManager.swift
;
sourceTree
=
"<group>"
;
};
04564D7B2CF8CF6D004456E4
/* YHPlayer.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHPlayer.swift
;
sourceTree
=
"<group>"
;
};
04564D7D2CF8D03D004456E4
/* YHFloatingWindow.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHFloatingWindow.swift
;
sourceTree
=
"<group>"
;
};
04564D7F2CF8E16C004456E4
/* YHPlayerTransitionAnimator.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHPlayerTransitionAnimator.swift
;
sourceTree
=
"<group>"
;
};
0457920A2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardViewModel.swift
;
sourceTree
=
"<group>"
;
};
0457920A2CBCE7B200EBD99B
/* YHResignUploadTravelCardViewModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardViewModel.swift
;
sourceTree
=
"<group>"
;
};
0457920C2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardListModel.swift
;
sourceTree
=
"<group>"
;
};
0457920C2CBCE8A800EBD99B
/* YHResignUploadTravelCardListModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardListModel.swift
;
sourceTree
=
"<group>"
;
};
0457920E2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardListTableViewCell.swift
;
sourceTree
=
"<group>"
;
};
0457920E2CBCE9D000EBD99B
/* YHResignUploadTravelCardListTableViewCell.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
YHResignUploadTravelCardListTableViewCell.swift
;
sourceTree
=
"<group>"
;
};
...
@@ -2962,11 +2968,14 @@
...
@@ -2962,11 +2968,14 @@
04564D402CF1BE99004456E4
/* C */
=
{
04564D402CF1BE99004456E4
/* C */
=
{
isa
=
PBXGroup
;
isa
=
PBXGroup
;
children
=
(
children
=
(
04564D792CF8CEF0004456E4
/* YHPlayerManager.swift */
,
04564D482CF385D9004456E4
/* YHBasePlayerViewController.swift */
,
04564D482CF385D9004456E4
/* YHBasePlayerViewController.swift */
,
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
,
04564D4A2CF389DD004456E4
/* YHLivePlayerViewController.swift */
,
04564D4A2CF389DD004456E4
/* YHLivePlayerViewController.swift */
,
04564D6B2CF6C414004456E4
/* YHLivePlayerViewController+Api.swift */
,
04564D6B2CF6C414004456E4
/* YHLivePlayerViewController+Api.swift */
,
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
,
04564D522CF38FE2004456E4
/* YHVODPlayerViewController.swift */
,
04564D7F2CF8E16C004456E4
/* YHPlayerTransitionAnimator.swift */
,
04564D7B2CF8CF6D004456E4
/* YHPlayer.swift */
,
04564D602CF59835004456E4
/* YHMessageInputViewController.swift */
,
);
);
path
=
C
;
path
=
C
;
sourceTree
=
"<group>"
;
sourceTree
=
"<group>"
;
...
@@ -2974,7 +2983,6 @@
...
@@ -2974,7 +2983,6 @@
04564D412CF1BEA2004456E4
/* V */
=
{
04564D412CF1BEA2004456E4
/* V */
=
{
isa
=
PBXGroup
;
isa
=
PBXGroup
;
children
=
(
children
=
(
04564D5C2CF49F0A004456E4
/* live_room_test_bg.png */
,
04564D462CF3851D004456E4
/* YHPlayerControlView.swift */
,
04564D462CF3851D004456E4
/* YHPlayerControlView.swift */
,
04564D4C2CF38D16004456E4
/* YHLiveMessageCell.swift */
,
04564D4C2CF38D16004456E4
/* YHLiveMessageCell.swift */
,
04564D4E2CF38E20004456E4
/* YHLiveMessageListView.swift */
,
04564D4E2CF38E20004456E4
/* YHLiveMessageListView.swift */
,
...
@@ -2982,6 +2990,7 @@
...
@@ -2982,6 +2990,7 @@
04564D642CF6065D004456E4
/* YHFadeView.swift */
,
04564D642CF6065D004456E4
/* YHFadeView.swift */
,
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
,
04564D552CF4467B004456E4
/* YHPlayerTopBarView.swift */
,
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
,
04564D5E2CF565C7004456E4
/* YHInputBottomBar.swift */
,
04564D7D2CF8D03D004456E4
/* YHFloatingWindow.swift */
,
);
);
path
=
V
;
path
=
V
;
sourceTree
=
"<group>"
;
sourceTree
=
"<group>"
;
...
@@ -5741,7 +5750,6 @@
...
@@ -5741,7 +5750,6 @@
A5EE42012C216C78005BBA5D
/* img_0.png in Resources */
,
A5EE42012C216C78005BBA5D
/* img_0.png in Resources */
,
04F0ABF32C364F9400518C30
/* home.json in Resources */
,
04F0ABF32C364F9400518C30
/* home.json in Resources */
,
04013E3E2CF87F3A001A8E40
/* zhibo.json in Resources */
,
04013E3E2CF87F3A001A8E40
/* zhibo.json in Resources */
,
04564D5D2CF49F0A004456E4
/* live_room_test_bg.png in Resources */
,
04943BE82CF0A0B500BF2255
/* submit_page_scroll.gif in Resources */
,
04943BE82CF0A0B500BF2255
/* submit_page_scroll.gif in Resources */
,
A5573EDB2B317C0000D98EC0
/* Assets.xcassets in Resources */
,
A5573EDB2B317C0000D98EC0
/* Assets.xcassets in Resources */
,
04F0ABF52C364F9400518C30
/* community.json in Resources */
,
04F0ABF52C364F9400518C30
/* community.json in Resources */
,
...
@@ -6345,6 +6353,7 @@
...
@@ -6345,6 +6353,7 @@
0430E65A2C7436CD000511E2
/* YHAdopterNewPeopleViewModel.swift in Sources */
,
0430E65A2C7436CD000511E2
/* YHAdopterNewPeopleViewModel.swift in Sources */
,
04256E1D2C75C74200A37BA4
/* YHAppointHKResultModel.swift in Sources */
,
04256E1D2C75C74200A37BA4
/* YHAppointHKResultModel.swift in Sources */
,
04564D592CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift in Sources */
,
04564D592CF470B2004456E4
/* YHIncomeRecordCompanyTipsCell.swift in Sources */
,
04564D7C2CF8CF6D004456E4
/* YHPlayer.swift in Sources */
,
047F3DE62CE83A0F001B2A6D
/* YHHKRequiredItemView.swift in Sources */
,
047F3DE62CE83A0F001B2A6D
/* YHHKRequiredItemView.swift in Sources */
,
04CE1ADB2C2AD91F001CB80A
/* YHActivityTitleItemView.swift in Sources */
,
04CE1ADB2C2AD91F001CB80A
/* YHActivityTitleItemView.swift in Sources */
,
042B20E32CEC92C400655093
/* YHMajorNameCell.swift in Sources */
,
042B20E32CEC92C400655093
/* YHMajorNameCell.swift in Sources */
,
...
@@ -6407,6 +6416,7 @@
...
@@ -6407,6 +6416,7 @@
04564D652CF6065D004456E4
/* YHFadeView.swift in Sources */
,
04564D652CF6065D004456E4
/* YHFadeView.swift in Sources */
,
04213B272C48C95E00797900
/* YHHomeIdentityCell.swift in Sources */
,
04213B272C48C95E00797900
/* YHHomeIdentityCell.swift in Sources */
,
041892262C91BDF500B9FB94
/* YHResignDocumentHeaderCell.swift in Sources */
,
041892262C91BDF500B9FB94
/* YHResignDocumentHeaderCell.swift in Sources */
,
04564D802CF8E16C004456E4
/* YHPlayerTransitionAnimator.swift in Sources */
,
0449EEE92C8EEB1E00A397FD
/* YHResinMaterialManageContainerVC.swift in Sources */
,
0449EEE92C8EEB1E00A397FD
/* YHResinMaterialManageContainerVC.swift in Sources */
,
0430E6542C73400A000511E2
/* YHAdopterUploadTableViewCell.swift in Sources */
,
0430E6542C73400A000511E2
/* YHAdopterUploadTableViewCell.swift in Sources */
,
04CE1AD32C2AD91F001CB80A
/* YHTravelModel.swift in Sources */
,
04CE1AD32C2AD91F001CB80A
/* YHTravelModel.swift in Sources */
,
...
@@ -6804,6 +6814,7 @@
...
@@ -6804,6 +6814,7 @@
04F243612C9D488200DF2C74
/* YHHKRecordsPersonnelSelectCell.swift in Sources */
,
04F243612C9D488200DF2C74
/* YHHKRecordsPersonnelSelectCell.swift in Sources */
,
A5FD63CB2B63D6C300D1D9DA
/* YHInformationFillTipsCell.swift in Sources */
,
A5FD63CB2B63D6C300D1D9DA
/* YHInformationFillTipsCell.swift in Sources */
,
04F9574B2C2032D8003C631C
/* YHMyFriendsCell.swift in Sources */
,
04F9574B2C2032D8003C631C
/* YHMyFriendsCell.swift in Sources */
,
04564D7A2CF8CEF0004456E4
/* YHPlayerManager.swift in Sources */
,
0430E65E2C74624E000511E2
/* YHAdopterCardTableViewCell.swift in Sources */
,
0430E65E2C74624E000511E2
/* YHAdopterCardTableViewCell.swift in Sources */
,
045EEF1F2B9F171A0022A143
/* YHDatePickView.swift in Sources */
,
045EEF1F2B9F171A0022A143
/* YHDatePickView.swift in Sources */
,
04B401E42CE76B10005C61A9
/* YHIncomeInputMoneyCell.swift in Sources */
,
04B401E42CE76B10005C61A9
/* YHIncomeInputMoneyCell.swift in Sources */
,
...
@@ -6812,6 +6823,7 @@
...
@@ -6812,6 +6823,7 @@
045EEE972B9F171A0022A143
/* YHPreviewInfoWorkExpView.swift in Sources */
,
045EEE972B9F171A0022A143
/* YHPreviewInfoWorkExpView.swift in Sources */
,
04B401EC2CE84CBD005C61A9
/* YHIncomeTypePopViewSelectCell.swift in Sources */
,
04B401EC2CE84CBD005C61A9
/* YHIncomeTypePopViewSelectCell.swift in Sources */
,
04B401F42CEB1C51005C61A9
/* YHIncomeCompanyDetailModel.swift in Sources */
,
04B401F42CEB1C51005C61A9
/* YHIncomeCompanyDetailModel.swift in Sources */
,
04564D7E2CF8D03D004456E4
/* YHFloatingWindow.swift in Sources */
,
044BACC72BCFA58E00184C64
/* YHNoDataTipsView.swift in Sources */
,
044BACC72BCFA58E00184C64
/* YHNoDataTipsView.swift in Sources */
,
04D5C5662B8ED92600190021
/* YHBaseModel.swift in Sources */
,
04D5C5662B8ED92600190021
/* YHBaseModel.swift in Sources */
,
A5F8AC082B9F414000A21EFA
/* YHCustomTextView.swift in Sources */
,
A5F8AC082B9F414000A21EFA
/* YHCustomTextView.swift in Sources */
,
...
@@ -6942,7 +6954,7 @@
...
@@ -6942,7 +6954,7 @@
);
);
GENERATE_INFOPLIST_FILE
=
YES
;
GENERATE_INFOPLIST_FILE
=
YES
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活
-Test
"
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
...
@@ -7084,7 +7096,7 @@
...
@@ -7084,7 +7096,7 @@
);
);
GENERATE_INFOPLIST_FILE
=
YES
;
GENERATE_INFOPLIST_FILE
=
YES
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活
-Uat
"
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
...
@@ -7289,7 +7301,7 @@
...
@@ -7289,7 +7301,7 @@
);
);
GENERATE_INFOPLIST_FILE
=
YES
;
GENERATE_INFOPLIST_FILE
=
YES
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_FILE
=
galaxy/Res/Info.plist
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活
-Dev
"
;
INFOPLIST_KEY_CFBundleDisplayName
=
"银河港生活"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSApplicationCategoryType
=
"public.app-category.business"
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace
=
YES
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
INFOPLIST_KEY_NSCameraUsageDescription
=
"APP需要您的同意,才能使用相机进行照片拍摄来完成信息填写,如禁止将无法拍摄照片,会影响资料提交效率。"
;
...
...
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHBasePlayerViewController.swift
View file @
613ab8a6
...
@@ -7,147 +7,164 @@
...
@@ -7,147 +7,164 @@
//
//
import
AgoraRtcKit
import
AgoraRtcKit
import
AVKit
import
UIKit
import
UIKit
class
YHBasePlayerViewController
:
YHBaseViewController
{
class
YHBasePlayerViewController
:
YHBaseViewController
{
// MARK: - Properties
// MARK: - Properties
weak
var
player
:
YHPlayer
?
var
playerKit
:
AgoraRtcMediaPlayerProtocol
!
var
playbackInfo
:
YHPlayerManager
.
PlaybackInfo
?
var
agoraKit
:
AgoraRtcEngineKit
!
// 播放器设置
var
currentPlayingURL
:
String
?
var
currentVideoTitle
:
String
?
// MARK: - UI Components
// MARK: - UI Components
lazy
var
containerView
:
UIView
=
{
lazy
var
containerView
:
UIView
=
{
let
view
=
UIView
()
let
view
=
UIView
()
view
.
backgroundColor
=
.
black
view
.
backgroundColor
=
.
black
return
view
return
view
}()
}()
lazy
var
bgIcon
:
UIImageView
=
{
let
imageView
=
UIImageView
()
if
let
path
=
Bundle
.
main
.
path
(
forResource
:
"live_room_test_bg"
,
ofType
:
"png"
)
{
imageView
.
image
=
UIImage
(
contentsOfFile
:
path
)
}
return
imageView
}()
lazy
var
playerView
:
UIView
=
{
lazy
var
playerView
:
UIView
=
{
let
view
=
UIView
()
let
view
=
UIView
()
view
.
backgroundColor
=
.
clear
view
.
backgroundColor
=
.
clear
return
view
return
view
}()
}()
private
lazy
var
controlView
:
YHPlayerControlView
=
{
private
(set)
lazy
var
controlView
:
YHPlayerControlView
=
{
let
view
=
YHPlayerControlView
()
let
view
=
YHPlayerControlView
()
view
.
delegate
=
self
view
.
delegate
=
self
return
view
return
view
}()
}()
lazy
var
topBarView
:
YHPlayerTopBarView
=
{
lazy
var
topBarView
:
YHPlayerTopBarView
=
{
let
view
=
YHPlayerTopBarView
(
frame
:
CGRect
.
init
(
x
:
0
,
y
:
0
,
width
:
KScreenWidth
,
height
:
k_Height_NavigationtBarAndStatuBar
))
let
view
=
YHPlayerTopBarView
(
frame
:
CGRect
(
x
:
0
,
y
:
0
,
width
:
KScreenWidth
,
height
:
k_Height_NavigationtBarAndStatuBar
))
return
view
return
view
}()
}()
// 控制状态
private
var
isControlsVisible
=
true
private
var
controlsAutoHideTimer
:
Timer
?
// MARK: - Lifecycle
// MARK: - Lifecycle
override
func
viewDidLoad
()
{
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
super
.
viewDidLoad
()
setupUI
()
setupUI
()
setupAgoraKit
()
setupGestures
()
setupNotifications
()
}
}
override
func
viewWillAppear
(
_
animated
:
Bool
)
{
super
.
viewWillAppear
(
animated
)
navigationController
?
.
setNavigationBarHidden
(
true
,
animated
:
animated
)
}
override
func
viewWillDisappear
(
_
animated
:
Bool
)
{
super
.
viewWillDisappear
(
animated
)
navigationController
?
.
setNavigationBarHidden
(
false
,
animated
:
animated
)
controlsAutoHideTimer
?
.
invalidate
()
controlsAutoHideTimer
=
nil
}
deinit
{
NotificationCenter
.
default
.
removeObserver
(
self
)
}
// MARK: - Setup
// MARK: - Setup
private
func
setupUI
()
{
private
func
setupUI
()
{
gk_navBarAlpha
=
0
gk_navBarAlpha
=
0
gk_navigationBar
.
isHidden
=
true
gk_navigationBar
.
isHidden
=
true
view
.
backgroundColor
=
.
black
view
.
backgroundColor
=
.
black
view
.
addSubview
(
containerView
)
view
.
addSubview
(
containerView
)
//containerView.addSubview(bgIcon)
containerView
.
addSubview
(
playerView
)
containerView
.
addSubview
(
playerView
)
containerView
.
addSubview
(
controlView
)
containerView
.
addSubview
(
topBarView
)
containerView
.
addSubview
(
topBarView
)
setupConstraints
()
setupConstraints
()
}
}
private
func
setupConstraints
()
{
private
func
setupConstraints
()
{
containerView
.
snp
.
makeConstraints
{
make
in
containerView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
make
.
edges
.
equalToSuperview
()
}
}
// bgIcon.snp.makeConstraints { make in
// make.edges.equalToSuperview()
// }
playerView
.
snp
.
makeConstraints
{
make
in
playerView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
make
.
edges
.
equalToSuperview
()
}
}
controlView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
topBarView
.
snp
.
makeConstraints
{
make
in
topBarView
.
snp
.
makeConstraints
{
make
in
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
height
.
equalTo
(
k_Height_NavigationtBarAndStatuBar
)
make
.
height
.
equalTo
(
k_Height_NavigationtBarAndStatuBar
)
}
}
// controlView.snp.makeConstraints { make in
// make.edges.equalToSuperview()
// }
}
}
private
func
setupAgoraKit
()
{
private
func
setupGestures
()
{
let
config
=
AgoraRtcEngineConfig
()
let
tap
=
UITapGestureRecognizer
(
target
:
self
,
action
:
#selector(
handleTap
)
)
config
.
appId
=
YhConstant
.
AgoraRtcKit
.
appId
containerView
.
addGestureRecognizer
(
tap
)
config
.
areaCode
=
.
global
}
config
.
channelProfile
=
.
liveBroadcasting
agoraKit
=
AgoraRtcEngineKit
.
sharedEngine
(
with
:
config
,
delegate
:
self
)
// MARK: - Controls Visibility
@objc
private
func
handleTap
()
{
playerKit
=
agoraKit
.
createMediaPlayer
(
with
:
self
)
toggleControls
()
playerKit
.
setView
(
playerView
)
}
playerKit
.
setRenderMode
(
.
fit
)
private
func
toggleControls
()
{
isControlsVisible
.
toggle
()
controlView
.
showControls
(
isControlsVisible
)
resetControlsAutoHideTimer
()
}
private
func
resetControlsAutoHideTimer
()
{
controlsAutoHideTimer
?
.
invalidate
()
if
isControlsVisible
{
controlsAutoHideTimer
=
Timer
.
scheduledTimer
(
withTimeInterval
:
5.0
,
repeats
:
false
)
{
[
weak
self
]
_
in
self
?
.
hideControls
()
}
}
}
private
func
hideControls
()
{
isControlsVisible
=
false
controlView
.
showControls
(
false
)
}
}
}
}
extension
YHBasePlayerViewController
:
YHMediaPlayerViewDelegate
{
// MARK: - YHPlayerControlViewDelegate
// MARK: - MediaPlayerViewDelegate
extension
YHBasePlayerViewController
:
YHPlayerControlViewDelegate
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapBack
button
:
UIButton
)
{
func
didTapPlayButton
()
{
if
let
navigationController
=
navigationController
{
if
playerKit
.
getPlayerState
()
==
.
playing
{
navigationController
.
popViewController
(
animated
:
true
)
playerKit
.
pause
()
}
else
{
}
else
{
playerKit
.
play
(
)
dismiss
(
animated
:
true
)
}
}
}
}
func
didTapStopButton
()
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapPlay
button
:
UIButton
)
{
playerKit
.
stop
()
if
player
?
.
getPlayState
()
==
.
playing
{
YHPlayerManager
.
shared
.
pause
()
}
else
{
YHPlayerManager
.
shared
.
resume
()
}
}
}
func
didSeekToPosition
(
_
position
:
Float
)
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didSeekTo
position
:
Float
)
{
let
duration
=
playerKit
.
getDuration
()
guard
let
player
=
player
else
{
return
}
let
seekPosition
=
Int64
(
Float
(
duration
)
*
position
)
let
duration
=
player
.
playerKit
.
getDuration
()
playerKit
.
seek
(
toPosition
:
Int
(
seekPosition
))
let
targetPosition
=
Int
(
Float
(
duration
)
*
position
)
player
.
playerKit
.
seek
(
toPosition
:
targetPosition
)
}
}
func
didChangeQuality
(
_
quality
:
YHVideoQuality
)
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didChangeQuality
quality
:
YHVideoQuality
)
{
// 根据不同清晰度切换播放源
// 由子类实现
switch
quality
{
case
.
auto
:
// 自动码率逻辑
break
case
.
sd
:
// 切换到标清源
break
case
.
hd
:
// 切换到高清源
break
case
.
fhd
:
// 切换到超清源
break
}
}
}
func
didToggleFullscreen
()
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapFullscreen
button
:
UIButton
)
{
// 切换全屏
if
UIDevice
.
current
.
orientation
.
isPortrait
{
if
UIDevice
.
current
.
orientation
.
isPortrait
{
let
value
=
UIInterfaceOrientation
.
landscapeRight
.
rawValue
let
value
=
UIInterfaceOrientation
.
landscapeRight
.
rawValue
UIDevice
.
current
.
setValue
(
value
,
forKey
:
"orientation"
)
UIDevice
.
current
.
setValue
(
value
,
forKey
:
"orientation"
)
...
@@ -158,88 +175,91 @@ extension YHBasePlayerViewController: YHMediaPlayerViewDelegate {
...
@@ -158,88 +175,91 @@ extension YHBasePlayerViewController: YHMediaPlayerViewDelegate {
}
}
}
}
extension
YHBasePlayerViewController
:
AgoraRtcMediaPlayerDelegate
{
// MARK: - Notifications
// MARK: - AgoraRtcMediaPlayerDelegate
extension
YHBasePlayerViewController
{
private
func
setupNotifications
()
{
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
NotificationCenter
.
default
.
addObserver
(
didChangedTo
state
:
AgoraMediaPlayerState
,
self
,
reason
:
AgoraMediaPlayerReason
)
{
selector
:
#selector(
handleAppDidEnterBackground
)
,
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
name
:
UIApplication
.
didEnterBackgroundNotification
,
guard
let
self
=
self
else
{
return
}
object
:
nil
)
switch
state
{
case
.
opening
:
NotificationCenter
.
default
.
addObserver
(
print
(
"正在打开媒体源"
)
self
,
selector
:
#selector(
handleAppWillEnterForeground
)
,
case
.
playing
:
name
:
UIApplication
.
willEnterForegroundNotification
,
self
.
controlView
.
updatePlayButton
(
isPlaying
:
true
)
object
:
nil
print
(
"正在播放"
)
)
case
.
paused
:
NotificationCenter
.
default
.
addObserver
(
self
.
controlView
.
updatePlayButton
(
isPlaying
:
false
)
self
,
print
(
"已暂停"
)
selector
:
#selector(
handleAudioSessionInterruption
)
,
name
:
AVAudioSession
.
interruptionNotification
,
case
.
stopped
:
object
:
nil
self
.
controlView
.
updatePlayButton
(
isPlaying
:
false
)
)
print
(
"已停止"
)
case
.
failed
:
print
(
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
self
.
showAlert
(
message
:
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
default
:
break
}
}
}
}
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
@objc
private
func
handleAppDidEnterBackground
()
{
didChangedTo
position
:
Int
)
{
YHPlayerManager
.
shared
.
pause
()
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
let
duration
=
self
.
playerKit
.
getDuration
()
guard
duration
>
0
else
{
return
}
let
progress
=
Float
(
position
)
/
Float
(
duration
)
let
currentTime
=
self
.
formatTime
(
position
)
let
totalTime
=
self
.
formatTime
(
duration
)
self
.
controlView
.
updateProgress
(
progress
,
currentTime
:
currentTime
,
totalTime
:
totalTime
)
}
}
}
}
@objc
private
func
handleAppWillEnterForeground
()
{
extension
YHBasePlayerViewController
:
AgoraRtcEngineDelegate
{
YHPlayerManager
.
shared
.
resume
()
// MARK: - AgoraRtcEngineDelegate
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurWarning
warningCode
:
AgoraWarningCode
)
{
print
(
"Warning:
\(
warningCode
.
rawValue
)
"
)
}
}
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurError
errorCode
:
AgoraErrorCode
)
{
@objc
private
func
handleAudioSessionInterruption
(
_
notification
:
Notification
)
{
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
guard
let
userInfo
=
notification
.
userInfo
,
self
?
.
showAlert
(
message
:
"RTC错误:
\(
errorCode
.
rawValue
)
"
)
let
typeValue
=
userInfo
[
AVAudioSessionInterruptionTypeKey
]
as?
UInt
,
let
type
=
AVAudioSession
.
InterruptionType
(
rawValue
:
typeValue
)
else
{
return
}
switch
type
{
case
.
began
:
YHPlayerManager
.
shared
.
pause
()
case
.
ended
:
if
let
optionsValue
=
userInfo
[
AVAudioSessionInterruptionOptionKey
]
as?
UInt
{
let
options
=
AVAudioSession
.
InterruptionOptions
(
rawValue
:
optionsValue
)
if
options
.
contains
(
.
shouldResume
)
{
YHPlayerManager
.
shared
.
resume
()
}
}
@unknown
default
:
break
}
}
}
}
}
}
// MARK: - Helper Methods
extension
YHBasePlayerViewController
{
extension
YHBasePlayerViewController
{
// MARK: - Helper Methods
func
formatTime
(
_
timeInMilliseconds
:
Int
)
->
String
{
private
func
formatTime
(
_
timeInMilliseconds
:
Int
)
->
String
{
let
totalSeconds
=
timeInMilliseconds
/
1000
let
totalSeconds
=
timeInMilliseconds
/
1000
let
minutes
=
totalSeconds
/
60
let
minutes
=
totalSeconds
/
60
let
seconds
=
totalSeconds
%
60
let
seconds
=
totalSeconds
%
60
return
String
(
format
:
"%02d:%02d"
,
minutes
,
seconds
)
return
String
(
format
:
"%02d:%02d"
,
minutes
,
seconds
)
}
}
func
showAlert
(
message
:
String
)
{
func
showAlert
(
message
:
String
)
{
let
alert
=
UIAlertController
(
title
:
"提示"
,
let
alert
=
UIAlertController
(
title
:
"提示"
,
message
:
message
,
message
:
message
,
preferredStyle
:
.
alert
)
preferredStyle
:
.
alert
)
alert
.
addAction
(
UIAlertAction
(
title
:
"确定"
,
style
:
.
default
))
alert
.
addAction
(
UIAlertAction
(
title
:
"确定"
,
style
:
.
default
))
present
(
alert
,
animated
:
true
)
present
(
alert
,
animated
:
true
)
}
}
}
}
// MARK: - Orientation Support
extension
YHBasePlayerViewController
{
override
var
shouldAutorotate
:
Bool
{
return
true
}
override
var
supportedInterfaceOrientations
:
UIInterfaceOrientationMask
{
return
.
allButUpsideDown
}
override
var
preferredInterfaceOrientationForPresentation
:
UIInterfaceOrientation
{
return
.
portrait
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHFloatingWindow.swift
0 → 100644
View file @
613ab8a6
//
// YHFloatingWindow.swift
// galaxy
//
// Created by alexzzw on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
Foundation
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHLivePlayerViewController.swift
View file @
613ab8a6
...
@@ -12,74 +12,51 @@ import UIKit
...
@@ -12,74 +12,51 @@ import UIKit
class
YHLivePlayerViewController
:
YHBasePlayerViewController
{
class
YHLivePlayerViewController
:
YHBasePlayerViewController
{
// MARK: - Properties
// MARK: - Properties
private
let
liveId
:
Int
private
var
roomId
:
String
?
private
let
messageQueue
=
DispatchQueue
(
label
:
"com.livePlayerRoom.messageQueue"
)
private
let
messageQueue
=
DispatchQueue
(
label
:
"com.livePlayerRoom.messageQueue"
)
let
viewModel
=
YHLiveSalesViewModel
()
private
var
listMaxWidth
:
CGFloat
{
private
var
listMaxWidth
:
CGFloat
{
return
KScreenWidth
*
248.0
/
375.0
return
KScreenWidth
*
248.0
/
375.0
}
}
// MARK: - UI Components
private
lazy
var
bottomInputBar
:
YHInputBottomBar
=
{
private
lazy
var
bottomInputBar
:
YHInputBottomBar
=
{
let
view
=
YHInputBottomBar
()
let
view
=
YHInputBottomBar
()
view
.
textViewTappedEvent
=
{
[
weak
self
]
in
view
.
textViewTappedEvent
=
{
[
weak
self
]
in
guard
let
self
=
self
else
{
self
?
.
present
(
self
?
.
inputVC
??
UIViewController
(),
animated
:
false
)
return
}
self
.
present
(
self
.
inputVC
,
animated
:
false
)
}
}
return
view
return
view
}()
}()
private
lazy
var
inputVC
:
YHMessageInputViewController
=
{
private
lazy
var
inputVC
:
YHMessageInputViewController
=
{
let
ctl
=
YHMessageInputViewController
()
let
ctl
=
YHMessageInputViewController
()
ctl
.
closeCallback
=
{
//
}
ctl
.
inputCallback
=
{
[
weak
self
]
controller
,
text
in
ctl
.
inputCallback
=
{
[
weak
self
]
controller
,
text
in
guard
let
self
=
self
else
{
self
?
.
handleMessageInput
(
text
:
text
,
controller
:
controller
)
return
}
if
!
checkLogin
()
{
return
}
guard
let
roomId
=
self
.
viewModel
.
liveDetailModel
?
.
roomId
else
{
return
}
YHIMHelper
.
shared
.
sendMessage
(
roomID
:
roomId
,
sendText
:
text
)
{
[
weak
self
]
message
,
error
in
guard
let
self
=
self
else
{
return
}
guard
let
message
=
message
else
{
if
let
error
=
error
{
// 被拉黑
if
error
.
code
==
.
moderationFailed
{
YHHUD
.
flash
(
message
:
"发送了敏感信息"
)
}
else
if
error
.
code
==
.
userPermissionDenied
{
YHHUD
.
flash
(
message
:
"您已被拉黑"
)
}
else
{
YHHUD
.
flash
(
message
:
"发送失败"
)
}
}
return
}
controller
.
updateText
(
""
)
controller
.
closeKeyboard
(
nil
)
self
.
appendHistoryMessages
([
message
])
}
}
}
return
ctl
return
ctl
}()
}()
private
lazy
var
messageListView
:
YHLiveMessageListView
=
{
private
lazy
var
messageListView
:
YHLiveMessageListView
=
{
let
view
=
YHLiveMessageListView
(
frame
:
CGRect
.
init
(
x
:
0
,
y
:
0
,
width
:
listMaxWidth
,
height
:
listMaxWidth
))
let
view
=
YHLiveMessageListView
(
frame
:
CGRect
(
x
:
0
,
y
:
0
,
width
:
listMaxWidth
,
height
:
listMaxWidth
))
return
view
return
view
}()
}()
let
viewModel
=
YHLiveSalesViewModel
()
// MARK: - Initialization
// MARK: - Initialization
private
let
liveId
:
Int
init
(
id
:
Int
,
url
:
String
?
=
nil
,
title
:
String
?
=
nil
,
roomId
:
String
?
=
nil
)
{
init
(
id
:
Int
)
{
self
.
liveId
=
id
self
.
liveId
=
id
self
.
roomId
=
roomId
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
// 隐藏播控UI
controlView
.
isHidden
=
true
player
?
.
delegate
=
self
// 设置播放器代理
if
let
url
=
url
{
play
(
url
:
url
,
title
:
title
)
}
if
let
roomId
=
roomId
{
setupChatRoom
(
roomId
:
roomId
)
}
}
}
required
init
?(
coder
:
NSCoder
)
{
required
init
?(
coder
:
NSCoder
)
{
...
@@ -89,31 +66,25 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
...
@@ -89,31 +66,25 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
// MARK: - Lifecycle
// MARK: - Lifecycle
override
func
viewDidLoad
()
{
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
super
.
viewDidLoad
()
setupUI
()
setupLiveUI
()
setupLiveNotifications
()
setupData
()
setupData
()
setupNoti
()
}
}
deinit
{
deinit
{
NotificationCenter
.
default
.
removeObserver
(
self
)
NotificationCenter
.
default
.
removeObserver
(
self
)
}
/// 是否可以返回,包括点击返回和手势返回,默认YES
override
func
navigationShouldPop
()
->
Bool
{
quitChatRoom
()
quitChatRoom
()
navigationController
?
.
popViewController
(
animated
:
true
)
return
false
}
}
// MARK: - Setup
// MARK: - Setup
private
func
setupLiveUI
()
{
private
func
setupUI
()
{
containerView
.
addSubview
(
bottomInputBar
)
containerView
.
addSubview
(
bottomInputBar
)
containerView
.
addSubview
(
messageListView
)
containerView
.
addSubview
(
messageListView
)
bottomInputBar
.
snp
.
makeConstraints
{
make
in
bottomInputBar
.
snp
.
makeConstraints
{
make
in
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
top
.
equalTo
(
view
.
safeAreaLayoutGuide
.
snp
.
bottom
)
.
offset
(
-
YHInputBottomBar
.
inputActionHeight
)
make
.
top
.
equalTo
(
view
.
safeAreaLayoutGuide
.
snp
.
bottom
)
.
offset
(
-
YHInputBottomBar
.
inputActionHeight
)
}
}
messageListView
.
snp
.
makeConstraints
{
make
in
messageListView
.
snp
.
makeConstraints
{
make
in
...
@@ -123,71 +94,121 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
...
@@ -123,71 +94,121 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
}
}
topBarView
.
closeButtonClickEvent
=
{
[
weak
self
]
in
topBarView
.
closeButtonClickEvent
=
{
[
weak
self
]
in
guard
let
self
=
self
else
{
self
?
.
quitChatRoom
()
return
YHPlayerManager
.
shared
.
stop
(
type
:
.
main
)
if
let
navigationController
=
self
?
.
navigationController
{
navigationController
.
popViewController
(
animated
:
true
)
}
else
{
self
?
.
dismiss
(
animated
:
true
)
}
}
self
.
quitChatRoom
()
}
self
.
navigationController
?
.
popViewController
(
animated
:
true
)
topBarView
.
zoomButtonClickEvent
=
{
[
weak
self
]
in
self
?
.
enterFloating
()
}
}
}
}
private
func
setupData
()
{
private
func
setupData
()
{
requestData
(
id
:
liveId
)
{
[
weak
self
]
liveDetail
,
error
in
viewModel
.
getLiveDetail
(
id
:
liveId
)
{
[
weak
self
]
liveDetail
,
error
in
guard
let
self
=
self
else
{
guard
let
self
=
self
else
{
return
}
return
}
if
let
liveDetail
=
liveDetail
{
guard
let
liveDetail
=
liveDetail
else
{
self
.
handleLiveDetailSuccess
(
liveDetail
)
printLog
(
"YHLivePlayerViewController: 请求失败"
)
}
else
{
if
let
errorMsg
=
error
?
.
errorMsg
,
errorMsg
.
count
>
0
{
printLog
(
"YHLivePlayerViewController: 请求失败"
)
if
let
errorMsg
=
error
?
.
errorMsg
,
!
errorMsg
.
isEmpty
{
YHHUD
.
flash
(
message
:
errorMsg
)
YHHUD
.
flash
(
message
:
errorMsg
)
}
}
return
return
}
}
self
.
topBarView
.
setupTopBarView
(
headUrl
:
liveDetail
.
avatar
,
nickname
:
liveDetail
.
hxNickname
,
count
:
liveDetail
.
access_num
)
self
.
play
(
with
:
liveDetail
.
pullUrl
)
YHIMHelper
.
shared
.
joinChatRoom
(
roomID
:
liveDetail
.
roomId
,
leaveOtherRooms
:
true
)
{
[
weak
self
]
error
in
guard
let
self
=
self
else
{
return
}
if
let
error
=
error
{
printLog
(
"joinChatRoom:
\(
error
)
"
)
}
else
{
printLog
(
"joinChatRoom: success"
)
YHIMHelper
.
shared
.
fetchHistoryMessage
(
roomID
:
liveDetail
.
roomId
)
{
[
weak
self
]
list
,
error
in
guard
let
self
=
self
else
{
return
}
guard
let
list
=
list
else
{
return
}
self
.
messageListView
.
clearMessages
()
self
.
appendHistoryMessages
(
list
)
}
}
}
}
}
}
}
private
func
setupNoti
()
{
private
func
handleLiveDetailSuccess
(
_
liveDetail
:
YHLiveDetailModel
)
{
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didChatManagerReceiveMessages(_:)
)
,
name
:
YHIMHelper
.
didChatManagerReceiveMessages
,
object
:
nil
)
// 更新顶部栏信息
topBarView
.
setupTopBarView
(
headUrl
:
liveDetail
.
avatar
,
nickname
:
liveDetail
.
hxNickname
,
count
:
liveDetail
.
access_num
)
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didLoginEaseIMSuccess
)
,
name
:
YHIMHelper
.
didLoginEaseIMSuccess
,
object
:
nil
)
// 如果没有预设URL,使用接口返回的URL播放
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didLogOutEaseIM
)
,
name
:
YHIMHelper
.
didLogOutEaseIM
,
object
:
nil
)
if
currentPlayingURL
==
nil
{
play
(
url
:
liveDetail
.
pullUrl
)
}
// 如果没有预设roomId,使用接口返回的roomId
if
roomId
==
nil
{
roomId
=
liveDetail
.
roomId
setupChatRoom
(
roomId
:
liveDetail
.
roomId
)
}
}
}
private
func
checkLogin
()
->
Bool
{
private
func
setupChatRoom
(
roomId
:
String
)
{
if
YHLoginManager
.
shared
.
isLogin
()
==
false
{
// 初始化聊天室
YHOneKeyLoginManager
.
shared
.
oneKeyLogin
()
joinChatRoom
(
roomId
:
roomId
)
return
false
}
return
true
}
}
// MARK: - IM Action
private
func
quitChatRoom
()
{
private
func
setupLiveNotifications
()
{
guard
let
roomId
=
self
.
viewModel
.
liveDetailModel
?
.
roomId
else
{
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didChatManagerReceiveMessages(_:)
)
,
name
:
YHIMHelper
.
didChatManagerReceiveMessages
,
object
:
nil
)
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didLoginEaseIMSuccess
)
,
name
:
YHIMHelper
.
didLoginEaseIMSuccess
,
object
:
nil
)
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
didLogOutEaseIM
)
,
name
:
YHIMHelper
.
didLogOutEaseIM
,
object
:
nil
)
}
// MARK: - Public Methods
func
play
(
url
:
String
,
title
:
String
?
=
nil
)
{
currentPlayingURL
=
url
currentVideoTitle
=
title
controlView
.
setTitle
(
title
??
""
)
YHPlayerManager
.
shared
.
play
(
url
:
url
,
inView
:
playerView
,
title
:
title
)
}
func
enterFloating
()
{
guard
let
playbackInfo
=
playbackInfo
else
{
return
return
}
}
YHPlayerManager
.
shared
.
enterFloating
(
from
:
self
,
playbackInfo
:
playbackInfo
)
}
// MARK: - Chat Room Methods
private
func
joinChatRoom
(
roomId
:
String
)
{
YHIMHelper
.
shared
.
joinChatRoom
(
roomID
:
roomId
,
leaveOtherRooms
:
true
)
{
[
weak
self
]
error
in
guard
let
self
=
self
else
{
return
}
if
let
error
=
error
{
printLog
(
"joinChatRoom:
\(
error
)
"
)
}
else
{
self
.
loadHistoryMessages
(
roomId
:
roomId
)
}
}
}
private
func
loadHistoryMessages
(
roomId
:
String
)
{
YHIMHelper
.
shared
.
fetchHistoryMessage
(
roomID
:
roomId
)
{
[
weak
self
]
list
,
error
in
guard
let
self
=
self
,
let
messages
=
list
else
{
return
}
self
.
messageListView
.
clearMessages
()
self
.
appendHistoryMessages
(
messages
)
}
}
private
func
quitChatRoom
()
{
guard
let
roomId
=
roomId
else
{
return
}
YHIMHelper
.
shared
.
quitChatRoom
(
roomID
:
roomId
)
{
error
in
YHIMHelper
.
shared
.
quitChatRoom
(
roomID
:
roomId
)
{
error
in
if
let
error
=
error
{
if
let
error
=
error
{
...
@@ -198,6 +219,31 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
...
@@ -198,6 +219,31 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
}
}
}
}
// MARK: - Message Handling
private
func
handleMessageInput
(
text
:
String
,
controller
:
YHMessageInputViewController
)
{
guard
checkLogin
(),
let
roomId
=
roomId
else
{
return
}
YHIMHelper
.
shared
.
sendMessage
(
roomID
:
roomId
,
sendText
:
text
)
{
[
weak
self
]
message
,
error
in
guard
let
self
=
self
else
{
return
}
if
let
message
=
message
{
controller
.
updateText
(
""
)
controller
.
closeKeyboard
(
nil
)
self
.
appendHistoryMessages
([
message
])
}
else
if
let
error
=
error
{
switch
error
.
code
{
case
.
moderationFailed
:
YHHUD
.
flash
(
message
:
"发送了敏感信息"
)
case
.
userPermissionDenied
:
YHHUD
.
flash
(
message
:
"您已被拉黑"
)
default
:
YHHUD
.
flash
(
message
:
"发送失败"
)
}
}
}
}
private
func
appendHistoryMessages
(
_
newMessages
:
[
EMChatMessage
])
{
private
func
appendHistoryMessages
(
_
newMessages
:
[
EMChatMessage
])
{
messageQueue
.
async
{
messageQueue
.
async
{
let
filterMessages
=
newMessages
.
filter
{
message
in
let
filterMessages
=
newMessages
.
filter
{
message
in
...
@@ -211,22 +257,28 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
...
@@ -211,22 +257,28 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
}
}
}
}
}
}
private
func
checkLogin
()
->
Bool
{
if
!
YHLoginManager
.
shared
.
isLogin
()
{
YHOneKeyLoginManager
.
shared
.
oneKeyLogin
()
return
false
}
return
true
}
// MARK: - Notification Handlers
@objc
private
func
didChatManagerReceiveMessages
(
_
note
:
Notification
)
{
@objc
private
func
didChatManagerReceiveMessages
(
_
note
:
Notification
)
{
guard
let
messages
=
note
.
object
as?
[
EMChatMessage
],
let
message
=
messages
.
first
,
message
.
conversationId
==
viewModel
.
liveDetailModel
?
.
roomId
else
{
guard
let
messages
=
note
.
object
as?
[
EMChatMessage
],
let
message
=
messages
.
first
,
message
.
conversationId
==
roomId
else
{
return
return
}
}
appendHistoryMessages
(
messages
)
appendHistoryMessages
(
messages
)
}
}
@objc
private
func
didLoginEaseIMSuccess
()
{
@objc
private
func
didLoginEaseIMSuccess
()
{
guard
let
roomId
=
self
.
viewModel
.
liveDetailModel
?
.
roomId
else
{
if
let
roomId
=
roomId
{
return
joinChatRoom
(
roomId
:
roomId
)
}
YHIMHelper
.
shared
.
joinChatRoom
(
roomID
:
roomId
,
leaveOtherRooms
:
true
)
{
[
weak
self
]
error
in
guard
let
self
=
self
else
{
return
}
}
}
}
}
...
@@ -235,36 +287,43 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
...
@@ -235,36 +287,43 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
}
}
}
}
extension
YHLivePlayerViewController
{
// MARK: - YHPlayerDelegate
// MARK: - Player Control
extension
YHLivePlayerViewController
:
YHPlayerDelegate
{
private
func
play
(
with
url
:
String
)
{
func
player
(
_
player
:
YHPlayer
,
didChangedToState
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
let
mediaSource
=
AgoraMediaSource
()
DispatchQueue
.
main
.
async
{
mediaSource
.
url
=
url
// 更新通用UI状态
mediaSource
.
autoPlay
=
true
switch
state
{
case
.
playing
:
let
result
=
playerKit
.
open
(
with
:
mediaSource
)
// controlView.updatePlayButton(isPlaying: true)
if
result
!=
0
{
// 直播开始时的特殊处理
showAlert
(
message
:
"播放失败,错误码:
\(
result
)
"
)
break
case
.
paused
,
.
stopped
:
// controlView.updatePlayButton(isPlaying: false)
// 直播开始时的特殊处理
break
case
.
failed
:
self
.
showAlert
(
message
:
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
default
:
break
}
}
}
}
}
}
func
player
(
_
player
:
YHPlayer
,
didChangedToPosition
position
:
Int
)
{
extension
YHLivePlayerViewController
{
let
duration
=
player
.
playerKit
.
getDuration
()
override
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
guard
duration
>
0
else
{
return
}
didChangedTo
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
super
.
AgoraRtcMediaPlayer
(
playerKit
,
didChangedTo
:
state
,
reason
:
reason
)
// 添加直播特定的状态处理
let
progress
=
Float
(
position
)
/
Float
(
duration
)
switch
state
{
let
currentTime
=
formatTime
(
position
)
case
.
playing
:
let
totalTime
=
formatTime
(
duration
)
// 直播开始时的特殊处理
break
controlView
.
updateProgress
(
progress
,
case
.
stopped
:
currentTime
:
currentTime
,
// 直播结束时的特殊处理
totalTime
:
totalTime
)
break
}
default
:
break
func
player
(
_
player
:
YHPlayer
,
didReceiveVideoSize
size
:
CGSize
)
{
}
// 处理视频尺寸变化,如果需要的话
}
}
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHPlayer.swift
0 → 100644
View file @
613ab8a6
//
// YHPlayer.swift
// galaxy
//
// Created by alexzzw on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
AgoraRtcKit
import
Foundation
// MARK: - 播放器类型
enum
YHPlayerType
{
case
main
case
secondary
}
protocol
YHPlayerDelegate
:
AnyObject
{
func
player
(
_
player
:
YHPlayer
,
didChangedToState
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
func
player
(
_
player
:
YHPlayer
,
didChangedToPosition
position
:
Int
)
func
player
(
_
player
:
YHPlayer
,
didReceiveVideoSize
size
:
CGSize
)
}
// MARK: - 播放器实例封装
class
YHPlayer
{
weak
var
delegate
:
YHPlayerDelegate
?
let
type
:
YHPlayerType
let
playerKit
:
AgoraRtcMediaPlayerProtocol
private(set)
var
currentURL
:
String
?
weak
var
currentPlayView
:
UIView
?
private(set)
var
currentTitle
:
String
?
init
(
type
:
YHPlayerType
,
playerKit
:
AgoraRtcMediaPlayerProtocol
)
{
self
.
type
=
type
self
.
playerKit
=
playerKit
}
func
setPlayView
(
_
view
:
UIView
?)
{
currentPlayView
=
view
playerKit
.
setView
(
view
)
}
func
play
(
url
:
String
,
title
:
String
?
=
nil
)
{
currentURL
=
url
currentTitle
=
title
let
mediaSource
=
AgoraMediaSource
()
mediaSource
.
url
=
url
mediaSource
.
autoPlay
=
true
playerKit
.
open
(
with
:
mediaSource
)
}
func
stop
()
{
playerKit
.
stop
()
currentPlayView
=
nil
currentURL
=
nil
currentTitle
=
nil
}
func
pause
()
{
playerKit
.
pause
()
}
func
resume
()
{
playerKit
.
play
()
}
func
getPosition
()
->
Int
{
return
playerKit
.
getPosition
()
}
func
getDuration
()
->
Int
{
return
playerKit
.
getDuration
()
}
func
getPlayState
()
->
AgoraMediaPlayerState
{
return
playerKit
.
getPlayerState
()
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHPlayerManager.swift
0 → 100644
View file @
613ab8a6
//
// YHPlayerManager.swift
// galaxy
//
// Created by alexzzw on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
AgoraRtcKit
import
UIKit
// MARK: - 播放器管理中心
class
YHPlayerManager
:
NSObject
{
// 播放器状态信息
struct
PlaybackInfo
:
Equatable
{
let
id
:
Int
let
url
:
String
?
let
title
:
String
?
let
roomId
:
String
?
let
isLive
:
Bool
init
(
id
:
Int
,
url
:
String
?
=
nil
,
title
:
String
?
=
nil
,
roomId
:
String
?
=
nil
,
isLive
:
Bool
)
{
self
.
id
=
id
self
.
url
=
url
self
.
title
=
title
self
.
roomId
=
roomId
self
.
isLive
=
isLive
}
static
func
==
(
lhs
:
Self
,
rhs
:
Self
)
->
Bool
{
lhs
.
id
==
rhs
.
id
&&
lhs
.
isLive
==
rhs
.
isLive
}
}
static
let
shared
=
YHPlayerManager
()
// 核心组件
private
var
agoraKit
:
AgoraRtcEngineKit
!
private
var
players
:
[
YHPlayerType
:
YHPlayer
]
=
[:]
private
var
currentPlaybackInfo
:
[
YHPlayerType
:
PlaybackInfo
]
=
[:]
private
var
floatingWindow
:
YHFloatingWindow
?
// 转场动画相关
private
var
transitionSourceView
:
UIView
?
override
private
init
()
{
super
.
init
()
setupAgoraKit
()
setupPlayers
()
}
// MARK: - Setup Methods
private
func
setupAgoraKit
()
{
let
config
=
AgoraRtcEngineConfig
()
config
.
appId
=
YhConstant
.
AgoraRtcKit
.
appId
config
.
areaCode
=
.
global
config
.
channelProfile
=
.
liveBroadcasting
agoraKit
=
AgoraRtcEngineKit
.
sharedEngine
(
with
:
config
,
delegate
:
self
)
// 基础配置
agoraKit
.
enableVideo
()
}
private
func
setupPlayers
()
{
// 创建主播放器
if
let
mainPlayerKit
=
agoraKit
.
createMediaPlayer
(
with
:
self
)
{
players
[
.
main
]
=
YHPlayer
(
type
:
.
main
,
playerKit
:
mainPlayerKit
)
}
// 创建第二播放器
if
let
secondaryPlayerKit
=
agoraKit
.
createMediaPlayer
(
with
:
self
)
{
players
[
.
secondary
]
=
YHPlayer
(
type
:
.
secondary
,
playerKit
:
secondaryPlayerKit
)
}
}
// MARK: - Public Methods
// 获取播放器实例
func
getPlayer
(
_
type
:
YHPlayerType
)
->
YHPlayer
?
{
return
players
[
type
]
}
// 播放控制
func
play
(
url
:
String
,
inView
view
:
UIView
?
=
nil
,
title
:
String
?
=
nil
,
type
:
YHPlayerType
=
.
main
)
{
if
let
player
=
players
[
type
]
{
// 如果提供了视图,则设置播放视图
if
let
view
=
view
{
player
.
setPlayView
(
view
)
}
player
.
play
(
url
:
url
,
title
:
title
)
}
}
func
play
(
playbackInfo
:
PlaybackInfo
,
inView
view
:
UIView
?
=
nil
,
type
:
YHPlayerType
=
.
main
)
{
if
let
player
=
players
[
type
]
{
// 如果提供了视图,则设置播放视图
currentPlaybackInfo
[
type
]
=
playbackInfo
if
let
view
=
view
{
player
.
setPlayView
(
view
)
}
if
let
url
=
playbackInfo
.
url
{
player
.
play
(
url
:
url
,
title
:
playbackInfo
.
title
)
}
}
}
func
resume
(
type
:
YHPlayerType
=
.
main
)
{
players
[
type
]?
.
resume
()
}
func
pause
(
type
:
YHPlayerType
=
.
main
)
{
players
[
type
]?
.
pause
()
}
func
stop
(
type
:
YHPlayerType
=
.
main
)
{
players
[
type
]?
.
stop
()
}
func
stopAllPlayers
()
{
players
.
values
.
forEach
{
$0
.
stop
()
}
}
// 视图控制
func
setPlayView
(
_
view
:
UIView
?,
type
:
YHPlayerType
=
.
main
)
{
players
[
type
]?
.
setPlayView
(
view
)
}
// MARK: - 资源管理
func
destroy
()
{
stopAllPlayers
()
players
.
removeAll
()
exitFloating
()
AgoraRtcEngineKit
.
destroy
()
}
}
extension
YHPlayerManager
{
// MARK: - 场景切换
func
enterVOD
(
from
sourceView
:
UIView
?,
playbackInfo
:
PlaybackInfo
,
type
:
YHPlayerType
=
.
main
)
{
let
playerVC
=
YHVODPlayerViewController
(
id
:
playbackInfo
.
id
,
url
:
playbackInfo
.
url
,
title
:
playbackInfo
.
title
)
// 保存播放信息
currentPlaybackInfo
[
type
]
=
playbackInfo
if
let
player
=
players
[
type
]
{
player
.
delegate
=
playerVC
playerVC
.
player
=
player
}
playerVC
.
playbackInfo
=
playbackInfo
present
(
playerVC
,
from
:
sourceView
)
}
func
enterLive
(
from
sourceView
:
UIView
?,
playbackInfo
:
PlaybackInfo
,
type
:
YHPlayerType
=
.
main
)
{
// 保存播放信息
currentPlaybackInfo
[
type
]
=
playbackInfo
let
playerVC
=
YHLivePlayerViewController
(
id
:
playbackInfo
.
id
,
url
:
playbackInfo
.
url
,
title
:
playbackInfo
.
title
,
roomId
:
playbackInfo
.
roomId
)
playerVC
.
playbackInfo
=
playbackInfo
if
let
player
=
players
[
type
]
{
player
.
delegate
=
playerVC
playerVC
.
player
=
player
}
present
(
playerVC
,
from
:
sourceView
)
}
func
enterVOD
(
from
sourceView
:
UIView
?,
id
:
Int
,
url
:
String
,
title
:
String
?,
type
:
YHPlayerType
=
.
main
)
{
let
playerVC
=
YHVODPlayerViewController
(
id
:
id
,
url
:
url
,
title
:
title
)
// 保存播放信息
let
playbackInfo
=
PlaybackInfo
(
id
:
id
,
url
:
url
,
title
:
title
,
isLive
:
false
)
playerVC
.
playbackInfo
=
playbackInfo
currentPlaybackInfo
[
type
]
=
playbackInfo
if
let
player
=
players
[
type
]
{
player
.
delegate
=
playerVC
playerVC
.
player
=
player
}
present
(
playerVC
,
from
:
sourceView
)
}
func
enterLive
(
from
sourceView
:
UIView
?,
id
:
Int
,
url
:
String
?
=
nil
,
title
:
String
?
=
nil
,
roomId
:
String
?
=
nil
,
type
:
YHPlayerType
=
.
main
)
{
// 保存播放信息
let
playbackInfo
=
PlaybackInfo
(
id
:
id
,
url
:
url
,
title
:
title
,
roomId
:
roomId
,
isLive
:
true
)
currentPlaybackInfo
[
type
]
=
playbackInfo
let
playerVC
=
YHLivePlayerViewController
(
id
:
id
,
url
:
url
,
title
:
title
,
roomId
:
roomId
)
playerVC
.
playbackInfo
=
playbackInfo
if
let
player
=
players
[
type
]
{
player
.
delegate
=
playerVC
playerVC
.
player
=
player
}
present
(
playerVC
,
from
:
sourceView
)
}
func
enterFloating
(
from
viewController
:
UIViewController
?
=
nil
,
playbackInfo
:
PlaybackInfo
,
type
:
YHPlayerType
=
.
main
)
{
guard
let
window
=
UIApplication
.
shared
.
keyWindow
,
let
player
=
players
[
type
]
else
{
return
}
currentPlaybackInfo
[
type
]
=
playbackInfo
if
let
url
=
playbackInfo
.
url
{
player
.
play
(
url
:
url
,
title
:
playbackInfo
.
title
)
}
// 获取当前播放视图的截图和位置
if
let
sourceView
=
player
.
currentPlayView
,
let
sourceSuperview
=
sourceView
.
superview
{
let
sourceFrame
=
sourceSuperview
.
convert
(
sourceView
.
frame
,
to
:
window
)
let
snapshotView
=
sourceView
.
snapshotView
(
afterScreenUpdates
:
false
)
??
UIView
()
snapshotView
.
frame
=
sourceFrame
// 创建浮窗
let
floatingWindow
=
YHFloatingWindow
()
floatingWindow
.
playbackInfo
=
playbackInfo
floatingWindow
.
delegate
=
self
floatingWindow
.
player
=
player
self
.
floatingWindow
=
floatingWindow
// 添加截图视图到窗口
window
.
addSubview
(
snapshotView
)
// 计算目标位置
let
targetFrame
=
floatingWindow
.
calculateInitialFrame
()
// 执行动画
let
showFloatingWindow
=
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
// 动画过渡
UIView
.
animate
(
withDuration
:
0.3
,
animations
:
{
snapshotView
.
frame
=
targetFrame
},
completion
:
{
_
in
// 移除截图视图
snapshotView
.
removeFromSuperview
()
// 显示真正的浮窗
floatingWindow
.
show
(
in
:
window
)
// 切换播放视图
player
.
setPlayView
(
floatingWindow
.
contentView
)
})
}
// 如果有viewController需要关闭
if
let
viewController
=
viewController
{
// 先执行动画,动画完成后再关闭页面
showFloatingWindow
()
viewController
.
dismiss
(
animated
:
false
)
}
else
{
showFloatingWindow
()
}
}
else
{
// 创建浮窗
let
floatingWindow
=
YHFloatingWindow
()
floatingWindow
.
delegate
=
self
floatingWindow
.
player
=
players
[
type
]
self
.
floatingWindow
=
floatingWindow
let
showFloatingWindow
=
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
// 显示浮窗
floatingWindow
.
show
(
in
:
window
)
// 切换播放视图
if
let
player
=
self
.
players
[
type
]
{
player
.
setPlayView
(
floatingWindow
.
contentView
)
}
}
// 如果有viewController需要关闭,先关闭再显示浮窗
if
let
viewController
=
viewController
{
viewController
.
dismiss
(
animated
:
true
)
{
showFloatingWindow
()
}
}
else
{
// 直接显示浮窗
showFloatingWindow
()
}
}
}
// 退出浮窗
func
exitFloating
()
{
floatingWindow
?
.
dismiss
()
floatingWindow
=
nil
}
private
func
present
(
_
playerVC
:
UIViewController
,
from
sourceView
:
UIView
?)
{
playerVC
.
modalPresentationStyle
=
.
fullScreen
playerVC
.
transitioningDelegate
=
self
transitionSourceView
=
sourceView
if
let
topVC
=
UIApplication
.
shared
.
keyWindow
?
.
rootViewController
?
.
topMostViewController
()
{
topVC
.
present
(
playerVC
,
animated
:
true
)
{
[
weak
self
]
in
self
?
.
exitFloating
()
self
?
.
transitionSourceView
=
nil
}
}
}
}
// MARK: - AgoraRtcEngineDelegate
extension
YHPlayerManager
:
AgoraRtcEngineDelegate
{
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurWarning
warningCode
:
AgoraWarningCode
)
{
printLog
(
"AgoraRtcEngine Warning:
\(
warningCode
.
rawValue
)
"
)
}
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurError
errorCode
:
AgoraErrorCode
)
{
printLog
(
"AgoraRtcEngine Error:
\(
errorCode
.
rawValue
)
"
)
}
}
// MARK: - AgoraRtcMediaPlayerDelegate
extension
YHPlayerManager
:
AgoraRtcMediaPlayerDelegate
{
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didChangedTo
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
printLog
(
"Player state changed to:
\(
state
.
rawValue
)
, error:
\(
reason
.
rawValue
)
"
)
// 找到对应的播放器并通知代理
if
let
player
=
players
.
values
.
first
(
where
:
{
$0
.
playerKit
===
playerKit
})
{
player
.
delegate
?
.
player
(
player
,
didChangedToState
:
state
,
reason
:
reason
)
}
}
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didChangedTo
position
:
Int
)
{
// 处理播放进度更新
if
let
player
=
players
.
values
.
first
(
where
:
{
$0
.
playerKit
===
playerKit
})
{
player
.
delegate
?
.
player
(
player
,
didChangedToPosition
:
position
)
}
}
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didReceiveVideoSize
size
:
CGSize
)
{
if
let
player
=
players
.
values
.
first
(
where
:
{
$0
.
playerKit
===
playerKit
})
{
player
.
delegate
?
.
player
(
player
,
didReceiveVideoSize
:
size
)
// 如果是主播放器且在浮窗模式,更新浮窗尺寸
}
// 更新浮窗视频尺寸
if
floatingWindow
?
.
player
?
.
playerKit
===
playerKit
{
floatingWindow
?
.
setVideoSize
(
size
)
}
}
}
// MARK: - YHFloatingWindowDelegate
extension
YHPlayerManager
:
YHFloatingWindowDelegate
{
// 通过PlaybackInfo查找对应的PlayerType
private
func
findPlayerType
(
for
playbackInfo
:
PlaybackInfo
)
->
YHPlayerType
?
{
return
currentPlaybackInfo
.
first
(
where
:
{
$0
.
value
==
playbackInfo
})?
.
key
}
func
floatingWindowDidTap
(
_
window
:
YHFloatingWindow
)
{
// 点击浮窗时进入直播间
guard
let
playbackInfo
=
window
.
playbackInfo
else
{
return
}
guard
let
type
=
findPlayerType
(
for
:
playbackInfo
)
else
{
return
}
if
playbackInfo
.
isLive
{
enterLive
(
from
:
window
.
contentView
,
playbackInfo
:
playbackInfo
,
type
:
type
)
}
else
{
enterVOD
(
from
:
window
.
contentView
,
playbackInfo
:
playbackInfo
,
type
:
type
)
}
}
func
floatingWindowDidClose
(
_
window
:
YHFloatingWindow
)
{
// 关闭浮窗时停止播放
stopAllPlayers
()
floatingWindow
=
nil
}
func
floatingWindow
(
_
window
:
YHFloatingWindow
,
didChangeSize
size
:
CGSize
)
{
// 处理浮窗大小变化
}
func
floatingWindow
(
_
window
:
YHFloatingWindow
,
didChangePosition
point
:
CGPoint
)
{
// 处理浮窗位置变化
}
}
// MARK: - UIViewControllerTransitioningDelegate
extension
YHPlayerManager
:
UIViewControllerTransitioningDelegate
{
func
animationController
(
forPresented
presented
:
UIViewController
,
presenting
:
UIViewController
,
source
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
if
let
sourceView
=
transitionSourceView
{
return
YHPlayerTransitionAnimator
(
type
:
.
zoomIn
,
sourceView
:
sourceView
)
}
else
{
return
YHPlayerTransitionAnimator
(
type
:
.
push
)
}
}
func
animationController
(
forDismissed
dismissed
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
if
let
sourceView
=
transitionSourceView
{
return
YHPlayerTransitionAnimator
(
type
:
.
zoomOut
,
sourceView
:
sourceView
)
}
else
{
return
YHPlayerTransitionAnimator
(
type
:
.
pop
)
}
}
}
extension
UIViewController
{
func
topMostViewController
()
->
UIViewController
{
if
let
presented
=
presentedViewController
{
return
presented
.
topMostViewController
()
}
if
let
navigation
=
self
as?
UINavigationController
{
return
navigation
.
visibleViewController
?
.
topMostViewController
()
??
navigation
}
if
let
tab
=
self
as?
UITabBarController
{
return
tab
.
selectedViewController
?
.
topMostViewController
()
??
tab
}
return
self
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHPlayerTransitionAnimator.swift
0 → 100644
View file @
613ab8a6
//
// YHPlayerTransitionAnimator.swift
// galaxy
//
// Created by alexzzw on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
//class YHPlayerTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
// enum TransitionType {
// case zoomIn // 放大进入
// case zoomOut // 缩小退出
// }
//
// private let type: TransitionType
// private let sourceView: UIView?
// private let sourceFrame: CGRect?
//
// init(type: TransitionType, sourceView: UIView? = nil) {
// self.type = type
// self.sourceView = sourceView
//
// // 获取源视图在屏幕上的frame
// if let sourceView = sourceView {
// self.sourceFrame = sourceView.convert(sourceView.bounds, to: nil)
// } else {
// // 默认的浮窗位置和大小
// self.sourceFrame = CGRect(
// x: UIScreen.main.bounds.width - 150,
// y: UIScreen.main.bounds.height - 200,
// width: 140,
// height: 80
// )
// }
//
// super.init()
// }
//
// func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
// return 0.3
// }
//
// func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// guard let toVC = transitionContext.viewController(forKey: .to),
// let fromVC = transitionContext.viewController(forKey: .from) else {
// transitionContext.completeTransition(false)
// return
// }
//
// let containerView = transitionContext.containerView
// let duration = transitionDuration(using: transitionContext)
//
// switch type {
// case .zoomIn:
// // 放大进入动画
// guard let toView = toVC.view else {
// transitionContext.completeTransition(false)
// return
// }
//
// containerView.addSubview(toView)
//
// // 设置初始状态
// if let sourceFrame = sourceFrame {
// toView.frame = sourceFrame
// } else {
// toView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
// toView.alpha = 0
// }
//
// // 执行动画
// UIView.animate(withDuration: duration,
// delay: 0,
// options: .curveEaseInOut,
// animations: {
// if self.sourceFrame != nil {
// toView.frame = UIScreen.main.bounds
// } else {
// toView.transform = .identity
// toView.alpha = 1
// }
// }, completion: { finished in
// transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
// })
//
// case .zoomOut:
// // 缩小退出动画
// guard let fromView = fromVC.view else {
// transitionContext.completeTransition(false)
// return
// }
//
// // 执行动画
// UIView.animate(withDuration: duration,
// delay: 0,
// options: .curveEaseInOut,
// animations: {
// if let sourceFrame = self.sourceFrame {
// fromView.frame = sourceFrame
// } else {
// fromView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
// fromView.alpha = 0
// }
// }, completion: { finished in
// fromView.removeFromSuperview()
// transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
// })
// }
// }
//}
class
YHPlayerTransitionAnimator
:
NSObject
,
UIViewControllerAnimatedTransitioning
{
enum
TransitionType
{
case
zoomIn
// 放大进入
case
zoomOut
// 缩小退出
case
push
// 推入
case
pop
// 推出
}
private
let
type
:
TransitionType
private
let
sourceView
:
UIView
?
private
let
sourceFrame
:
CGRect
?
init
(
type
:
TransitionType
,
sourceView
:
UIView
?
=
nil
)
{
self
.
type
=
type
self
.
sourceView
=
sourceView
// 获取源视图在屏幕上的frame
if
let
sourceView
=
sourceView
{
self
.
sourceFrame
=
sourceView
.
convert
(
sourceView
.
bounds
,
to
:
nil
)
}
else
{
// 默认的浮窗位置和大小
self
.
sourceFrame
=
CGRect
(
x
:
UIScreen
.
main
.
bounds
.
width
-
150
,
y
:
UIScreen
.
main
.
bounds
.
height
-
200
,
width
:
140
,
height
:
80
)
}
super
.
init
()
}
func
transitionDuration
(
using
transitionContext
:
UIViewControllerContextTransitioning
?)
->
TimeInterval
{
return
0.3
}
func
animateTransition
(
using
transitionContext
:
UIViewControllerContextTransitioning
)
{
guard
let
toVC
=
transitionContext
.
viewController
(
forKey
:
.
to
),
let
fromVC
=
transitionContext
.
viewController
(
forKey
:
.
from
)
else
{
transitionContext
.
completeTransition
(
false
)
return
}
let
containerView
=
transitionContext
.
containerView
let
duration
=
transitionDuration
(
using
:
transitionContext
)
let
screenBounds
=
UIScreen
.
main
.
bounds
switch
type
{
case
.
zoomIn
:
animateZoomIn
(
containerView
:
containerView
,
fromVC
:
fromVC
,
toVC
:
toVC
,
duration
:
duration
,
transitionContext
:
transitionContext
)
case
.
zoomOut
:
animateZoomOut
(
containerView
:
containerView
,
fromVC
:
fromVC
,
toVC
:
toVC
,
duration
:
duration
,
transitionContext
:
transitionContext
)
case
.
push
:
guard
let
toView
=
toVC
.
view
else
{
return
}
containerView
.
addSubview
(
toView
)
// 设置初始位置(从右边推入)
toView
.
frame
=
screenBounds
.
offsetBy
(
dx
:
screenBounds
.
width
,
dy
:
0
)
UIView
.
animate
(
withDuration
:
duration
,
delay
:
0
,
options
:
.
curveEaseInOut
,
animations
:
{
// 移动到目标位置
toView
.
frame
=
screenBounds
fromVC
.
view
.
frame
=
screenBounds
.
offsetBy
(
dx
:
-
screenBounds
.
width
*
0.3
,
dy
:
0
)
},
completion
:
{
finished
in
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
case
.
pop
:
guard
let
fromView
=
fromVC
.
view
,
let
toView
=
toVC
.
view
else
{
return
}
// 确保目标视图在底层
containerView
.
insertSubview
(
toView
,
belowSubview
:
fromView
)
// 设置初始位置
toView
.
frame
=
screenBounds
.
offsetBy
(
dx
:
-
screenBounds
.
width
*
0.3
,
dy
:
0
)
UIView
.
animate
(
withDuration
:
duration
,
delay
:
0
,
options
:
.
curveEaseInOut
,
animations
:
{
// 移动到目标位置
fromView
.
frame
=
screenBounds
.
offsetBy
(
dx
:
screenBounds
.
width
,
dy
:
0
)
toView
.
frame
=
screenBounds
},
completion
:
{
finished
in
fromView
.
removeFromSuperview
()
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
}
}
private
func
animateZoomIn
(
containerView
:
UIView
,
fromVC
:
UIViewController
,
toVC
:
UIViewController
,
duration
:
TimeInterval
,
transitionContext
:
UIViewControllerContextTransitioning
)
{
guard
let
toView
=
toVC
.
view
else
{
return
}
containerView
.
addSubview
(
toView
)
if
let
sourceFrame
=
sourceFrame
{
// 有源视图时的放大动画
toView
.
frame
=
sourceFrame
UIView
.
animate
(
withDuration
:
duration
,
animations
:
{
toView
.
frame
=
UIScreen
.
main
.
bounds
},
completion
:
{
finished
in
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
}
else
{
// 无源视图时执行push动画
toView
.
frame
=
UIScreen
.
main
.
bounds
.
offsetBy
(
dx
:
UIScreen
.
main
.
bounds
.
width
,
dy
:
0
)
UIView
.
animate
(
withDuration
:
duration
,
animations
:
{
toView
.
frame
=
UIScreen
.
main
.
bounds
fromVC
.
view
.
frame
=
UIScreen
.
main
.
bounds
.
offsetBy
(
dx
:
-
UIScreen
.
main
.
bounds
.
width
*
0.3
,
dy
:
0
)
},
completion
:
{
finished
in
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
}
}
private
func
animateZoomOut
(
containerView
:
UIView
,
fromVC
:
UIViewController
,
toVC
:
UIViewController
,
duration
:
TimeInterval
,
transitionContext
:
UIViewControllerContextTransitioning
)
{
guard
let
fromView
=
fromVC
.
view
else
{
return
}
if
let
sourceFrame
=
sourceFrame
{
// 有源视图时的缩小动画
UIView
.
animate
(
withDuration
:
duration
,
animations
:
{
fromView
.
frame
=
sourceFrame
},
completion
:
{
finished
in
fromView
.
removeFromSuperview
()
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
}
else
{
// 无源视图时执行pop动画
containerView
.
insertSubview
(
toVC
.
view
,
belowSubview
:
fromView
)
toVC
.
view
.
frame
=
UIScreen
.
main
.
bounds
.
offsetBy
(
dx
:
-
UIScreen
.
main
.
bounds
.
width
*
0.3
,
dy
:
0
)
UIView
.
animate
(
withDuration
:
duration
,
animations
:
{
fromView
.
frame
=
UIScreen
.
main
.
bounds
.
offsetBy
(
dx
:
UIScreen
.
main
.
bounds
.
width
,
dy
:
0
)
toVC
.
view
.
frame
=
UIScreen
.
main
.
bounds
},
completion
:
{
finished
in
fromView
.
removeFromSuperview
()
transitionContext
.
completeTransition
(
!
transitionContext
.
transitionWasCancelled
)
})
}
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHVODPlayerViewController.swift
View file @
613ab8a6
...
@@ -6,168 +6,106 @@
...
@@ -6,168 +6,106 @@
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
//
import
AgoraRtcKi
t
// YHVODPlayerViewController.swif
t
import
UIKit
import
UIKit
import
AgoraRtcKit
class
YHVODPlayerViewController
:
YHBasePlayerViewController
{
class
YHVODPlayerViewController
:
YHBasePlayerViewController
{
private
let
videoInfo
:
YHVideoInfo
// MARK: - Properties
private
var
playConfig
:
YHVideoPlayConfig
private
let
vodId
:
Int
//private let viewModel = YHVideoViewModel()
init
(
videoInfo
:
YHVideoInfo
,
playConfig
:
YHVideoPlayConfig
=
YHVideoPlayConfig
())
{
// MARK: - Initialization
self
.
videoInfo
=
videoInfo
init
(
id
:
Int
,
url
:
String
?
=
nil
,
title
:
String
?
=
nil
)
{
self
.
playConfig
=
playConfig
self
.
vodId
=
id
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
player
?
.
delegate
=
self
if
let
url
=
url
{
//play(url: url, title: title)
}
}
}
required
init
?(
coder
:
NSCoder
)
{
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
fatalError
(
"init(coder:) has not been implemented"
)
}
}
// MARK: - Lifecycle
override
func
viewDidLoad
()
{
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
super
.
viewDidLoad
()
setupVideoInfo
()
loadVideoDetail
()
startPlayback
()
}
}
private
func
setupVideoInfo
()
{
// MARK: - Data Loading
// 设置视频标题
private
func
loadVideoDetail
()
{
title
=
videoInfo
.
title
// viewModel.getVideoDetail(videoId: vodId) { [weak self] videoDetail, error in
// guard let self = self else { return }
// 设置作者信息
//
setupAuthorInfo
()
// if let videoDetail = videoDetail {
// self.handleVideoDetailSuccess(videoDetail)
// 设置播放器配置
// } else {
setupPlayerConfig
()
// printLog("YHVODPlayerViewController: 请求失败")
// if let errorMsg = error?.errorMsg, !errorMsg.isEmpty {
// YHHUD.flash(message: errorMsg)
// }
// }
// }
}
}
private
func
setupAuthorInfo
()
{
// private func handleVideoDetailSuccess(_ detail: YHVideoDetailModel) {
// 添加作者信息视图
// // 如果没有预设URL,使用接口返回的URL播放
let
authorInfoView
=
YHVideoAuthorInfoView
(
author
:
videoInfo
.
author
)
// if currentPlayingURL == nil {
view
.
addSubview
(
authorInfoView
)
// play(url: detail.playUrl, title: detail.title)
// }
authorInfoView
.
snp
.
makeConstraints
{
make
in
// }
make
.
top
.
equalTo
(
playerView
.
snp
.
bottom
)
}
make
.
left
.
right
.
equalToSuperview
()
make
.
height
.
equalTo
(
60
)
// MARK: - YHPlayerDelegate
}
extension
YHVODPlayerViewController
:
YHPlayerDelegate
{
}
func
player
(
_
player
:
YHPlayer
,
didChangedToState
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
switch
state
{
private
func
setupPlayerConfig
()
{
case
.
playing
:
// 设置播放器配置
controlView
.
updatePlayButton
(
isPlaying
:
true
)
if
playConfig
.
muteOnStart
{
updatePlayCount
()
playerKit
.
adjustPlayoutVolume
(
0
)
}
case
.
paused
,
.
stopped
:
}
controlView
.
updatePlayButton
(
isPlaying
:
false
)
private
func
startPlayback
()
{
case
.
failed
:
// 获取合适的视频URL
showAlert
(
message
:
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
let
networkType
:
YHNetworkType
=
getNetworkType
()
if
let
videoUrl
=
videoInfo
.
getBestQualityUrl
(
for
:
networkType
)
{
default
:
play
(
with
:
videoUrl
)
break
}
else
{
//showAlert(message: "无法获取视频地址")
}
}
}
}
private
func
getNetworkType
()
->
YHNetworkType
{
func
player
(
_
player
:
YHPlayer
,
didChangedToPosition
position
:
Int
)
{
// 实现网络类型检测逻辑
let
duration
=
player
.
playerKit
.
getDuration
()
return
.
wifi
guard
duration
>
0
else
{
return
}
}
// MARK: - Player Control
private
func
play
(
with
url
:
String
)
{
let
mediaSource
=
AgoraMediaSource
()
mediaSource
.
url
=
url
mediaSource
.
autoPlay
=
true
let
result
=
playerKit
.
open
(
with
:
mediaSource
)
let
progress
=
Float
(
position
)
/
Float
(
duration
)
if
result
!=
0
{
let
currentTime
=
formatTime
(
position
)
showAlert
(
message
:
"播放失败,错误码:
\(
result
)
"
)
let
totalTime
=
formatTime
(
duration
)
}
controlView
.
updateProgress
(
progress
,
currentTime
:
currentTime
,
totalTime
:
totalTime
)
}
}
private
func
updatePlayCount
(
)
{
func
player
(
_
player
:
YHPlayer
,
didReceiveVideoSize
size
:
CGSize
)
{
//
更新播放次数的逻辑
//
处理视频尺寸变化,如有需要
}
}
}
}
// 作者信息视图
// MARK: - Helper Methods
class
YHVideoAuthorInfoView
:
UIView
{
private
extension
YHVODPlayerViewController
{
private
let
author
:
YHVideoAuthor
func
updatePlayCount
()
{
// YHAPIService.shared.updateVideoPlayCount(id: vodId) { [weak self] result in
private
lazy
var
avatarImageView
:
UIImageView
=
{
// switch result {
let
imageView
=
UIImageView
()
// case .success:
imageView
.
layer
.
cornerRadius
=
20
// break
imageView
.
layer
.
masksToBounds
=
true
// case .failure(let error):
imageView
.
contentMode
=
.
scaleAspectFill
// printLog("更新播放次数失败: \(error)")
return
imageView
// }
}()
// }
private
lazy
var
nicknameLabel
:
UILabel
=
{
let
label
=
UILabel
()
label
.
font
=
.
systemFont
(
ofSize
:
16
,
weight
:
.
medium
)
return
label
}()
private
lazy
var
followButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
layer
.
cornerRadius
=
15
button
.
clipsToBounds
=
true
return
button
}()
init
(
author
:
YHVideoAuthor
)
{
self
.
author
=
author
super
.
init
(
frame
:
.
zero
)
setupUI
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
private
func
setupUI
()
{
addSubview
(
avatarImageView
)
addSubview
(
nicknameLabel
)
addSubview
(
followButton
)
// 设置约束
avatarImageView
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalToSuperview
()
.
offset
(
16
)
make
.
centerY
.
equalToSuperview
()
make
.
size
.
equalTo
(
CGSize
(
width
:
40
,
height
:
40
))
}
nicknameLabel
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
avatarImageView
.
snp
.
right
)
.
offset
(
12
)
make
.
centerY
.
equalToSuperview
()
}
followButton
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalToSuperview
()
.
offset
(
-
16
)
make
.
centerY
.
equalToSuperview
()
make
.
width
.
equalTo
(
80
)
make
.
height
.
equalTo
(
30
)
}
// 设置数据
nicknameLabel
.
text
=
author
.
nickname
updateFollowButton
()
// 加载头像
if
let
avatarUrl
=
author
.
avatarUrl
{
// 使用SDWebImage或其他图片加载库加载头像
// avatarImageView.sd_setImage(with: URL(string: avatarUrl))
}
}
private
func
updateFollowButton
()
{
let
title
=
author
.
isFollowed
?
"已关注"
:
"关注"
let
backgroundColor
=
author
.
isFollowed
?
UIColor
.
systemGray3
:
UIColor
.
systemBlue
followButton
.
setTitle
(
title
,
for
:
.
normal
)
followButton
.
backgroundColor
=
backgroundColor
followButton
.
setTitleColor
(
.
white
,
for
:
.
normal
)
}
}
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/IM(环信)/YHIMHelper.swift
View file @
613ab8a6
...
@@ -180,7 +180,7 @@ extension YHIMHelper {
...
@@ -180,7 +180,7 @@ extension YHIMHelper {
func
fetchHistoryMessage
(
roomID
:
String
,
completion
:
@escaping
([
EMChatMessage
]?,
EMError
?)
->
Void
)
{
func
fetchHistoryMessage
(
roomID
:
String
,
completion
:
@escaping
([
EMChatMessage
]?,
EMError
?)
->
Void
)
{
let
option
=
EMFetchServerMessagesOption
()
let
option
=
EMFetchServerMessagesOption
()
option
.
direction
=
.
up
// 时间戳逆序的消息因为要倒置表格
option
.
direction
=
.
up
// 时间戳逆序的消息因为要倒置表格
EMClient
.
shared
()
.
chatManager
?
.
fetchMessagesFromServer
(
by
:
roomID
,
conversationType
:
.
chatRoom
,
cursor
:
nil
,
pageSize
:
5
0
,
option
:
option
,
completion
:
{
result
,
err
in
EMClient
.
shared
()
.
chatManager
?
.
fetchMessagesFromServer
(
by
:
roomID
,
conversationType
:
.
chatRoom
,
cursor
:
nil
,
pageSize
:
1
0
,
option
:
option
,
completion
:
{
result
,
err
in
DispatchQueue
.
main
.
async
{
DispatchQueue
.
main
.
async
{
if
let
err
=
err
{
if
let
err
=
err
{
// 获取失败
// 获取失败
...
...
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHFloatingWindow.swift
0 → 100644
View file @
613ab8a6
//
// YHFloatingWindow.swift
// galaxy
//
// Created by alexzzw on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
// MARK: - 浮窗管理
protocol
YHFloatingWindowDelegate
:
AnyObject
{
func
floatingWindowDidTap
(
_
window
:
YHFloatingWindow
)
func
floatingWindowDidClose
(
_
window
:
YHFloatingWindow
)
func
floatingWindow
(
_
window
:
YHFloatingWindow
,
didChangeSize
size
:
CGSize
)
func
floatingWindow
(
_
window
:
YHFloatingWindow
,
didChangePosition
point
:
CGPoint
)
}
class
YHFloatingWindow
:
NSObject
{
// MARK: - Properties
weak
var
player
:
YHPlayer
?
weak
var
delegate
:
YHFloatingWindowDelegate
?
var
playbackInfo
:
YHPlayerManager
.
PlaybackInfo
?
// 视频方向
enum
VideoOrientation
{
case
portrait
// 竖屏 9:16
case
landscape
// 横屏 16:9
var
aspectRatio
:
CGFloat
{
switch
self
{
case
.
portrait
:
return
9.0
/
16.0
case
.
landscape
:
return
16.0
/
9.0
}
}
}
// 窗口尺寸
private
struct
Size
{
static
let
minWidth
:
CGFloat
=
120
static
let
maxWidth
:
CGFloat
=
UIScreen
.
main
.
bounds
.
width
static
let
minHeight
:
CGFloat
=
67.5
// 16:9
static
let
maxHeight
:
CGFloat
=
UIScreen
.
main
.
bounds
.
height
static
let
defaultWidth
:
CGFloat
=
150
static
let
defaultHeight
:
CGFloat
=
84.375
// 16:9
}
private(set)
var
contentView
:
UIView
private
var
containerView
:
UIView
private
var
videoOrientation
:
VideoOrientation
=
.
landscape
// 手势相关
private
var
initialFrame
:
CGRect
=
.
zero
private
var
lastScale
:
CGFloat
=
1.0
private
var
isResizing
:
Bool
=
false
private
var
initialCenter
:
CGPoint
=
.
zero
// UI组件
private
lazy
var
closeButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
custom
)
button
.
setImage
(
UIImage
(
named
:
"icon_close"
),
for
:
.
normal
)
button
.
addTarget
(
self
,
action
:
#selector(
closeButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
// MARK: - Initialization
override
init
()
{
// 创建容器视图
containerView
=
UIView
(
frame
:
CGRect
(
x
:
0
,
y
:
0
,
width
:
Size
.
defaultWidth
,
height
:
Size
.
defaultHeight
))
// 创建内容视图
contentView
=
UIView
(
frame
:
containerView
.
bounds
)
super
.
init
()
setupUI
()
setupGestures
()
}
// MARK: - Setup
private
func
setupUI
()
{
// 容器视图设置
containerView
.
backgroundColor
=
.
black
containerView
.
layer
.
cornerRadius
=
8
containerView
.
clipsToBounds
=
true
containerView
.
layer
.
masksToBounds
=
true
// 添加阴影
containerView
.
layer
.
shadowColor
=
UIColor
.
black
.
cgColor
containerView
.
layer
.
shadowOffset
=
CGSize
(
width
:
0
,
height
:
2
)
containerView
.
layer
.
shadowRadius
=
4
containerView
.
layer
.
shadowOpacity
=
0.3
// 添加内容视图
containerView
.
addSubview
(
contentView
)
contentView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
// 添加关闭按钮
containerView
.
addSubview
(
closeButton
)
closeButton
.
snp
.
makeConstraints
{
make
in
make
.
top
.
right
.
equalToSuperview
()
.
inset
(
8
)
make
.
size
.
equalTo
(
CGSize
(
width
:
24
,
height
:
24
))
}
}
private
func
setupGestures
()
{
// 平移手势
let
panGesture
=
UIPanGestureRecognizer
(
target
:
self
,
action
:
#selector(
handlePan(_:)
)
)
// 缩放手势
let
pinchGesture
=
UIPinchGestureRecognizer
(
target
:
self
,
action
:
#selector(
handlePinch(_:)
)
)
// 点击手势
let
tapGesture
=
UITapGestureRecognizer
(
target
:
self
,
action
:
#selector(
handleTap(_:)
)
)
tapGesture
.
delegate
=
self
containerView
.
addGestureRecognizer
(
panGesture
)
containerView
.
addGestureRecognizer
(
pinchGesture
)
containerView
.
addGestureRecognizer
(
tapGesture
)
}
// MARK: - Public Methods
func
calculateInitialFrame
()
->
CGRect
{
// 计算浮窗的初始位置和大小
let
width
:
CGFloat
=
150
// 或其他合适的宽度
let
height
:
CGFloat
=
width
*
9
/
16
// 保持16:9比例
let
x
=
UIScreen
.
main
.
bounds
.
width
-
width
-
16
let
y
=
UIScreen
.
main
.
bounds
.
height
-
height
-
100
// 距离底部适当距离
return
CGRect
(
x
:
x
,
y
:
y
,
width
:
width
,
height
:
height
)
}
func
show
(
in
window
:
UIWindow
)
{
containerView
.
frame
=
calculateInitialFrame
()
window
.
addSubview
(
containerView
)
}
func
show
(
in
window
:
UIWindow
,
at
point
:
CGPoint
?
=
nil
)
{
// 设置初始位置
if
let
point
=
point
{
containerView
.
center
=
point
}
else
{
// 默认位置:右下角
containerView
.
frame
.
origin
=
CGPoint
(
x
:
window
.
bounds
.
width
-
containerView
.
bounds
.
width
-
20
,
y
:
window
.
bounds
.
height
-
containerView
.
bounds
.
height
-
100
)
}
window
.
addSubview
(
containerView
)
// 显示动画
containerView
.
alpha
=
0
// containerView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
UIView
.
animate
(
withDuration
:
0.3
)
{
self
.
containerView
.
alpha
=
1
self
.
containerView
.
transform
=
.
identity
}
}
func
dismiss
()
{
UIView
.
animate
(
withDuration
:
0.3
,
animations
:
{
self
.
containerView
.
alpha
=
0
//self.containerView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
})
{
_
in
self
.
containerView
.
removeFromSuperview
()
}
}
func
setVideoSize
(
_
size
:
CGSize
)
{
// 更新视频方向
let
orientation
:
VideoOrientation
=
size
.
width
>
size
.
height
?
.
landscape
:
.
portrait
if
orientation
!=
videoOrientation
{
videoOrientation
=
orientation
updateLayoutForOrientation
()
}
}
// MARK: - Private Methods
private
func
updateLayoutForOrientation
()
{
let
currentWidth
=
containerView
.
bounds
.
width
let
newHeight
=
currentWidth
/
videoOrientation
.
aspectRatio
UIView
.
animate
(
withDuration
:
0.3
)
{
var
frame
=
self
.
containerView
.
frame
frame
.
size
.
height
=
newHeight
self
.
containerView
.
frame
=
frame
// 通知代理
self
.
delegate
?
.
floatingWindow
(
self
,
didChangeSize
:
frame
.
size
)
}
}
private
func
snapToNearestSize
()
{
let
currentWidth
=
containerView
.
bounds
.
width
// 定义三种尺寸状态
let
smallWidth
=
Size
.
minWidth
let
mediumWidth
=
Size
.
maxWidth
*
0.5
let
largeWidth
=
Size
.
maxWidth
// 确定目标尺寸
let
targetWidth
:
CGFloat
if
currentWidth
<
(
smallWidth
+
mediumWidth
)
/
2
{
targetWidth
=
smallWidth
}
else
if
currentWidth
<
(
mediumWidth
+
largeWidth
)
/
2
{
targetWidth
=
mediumWidth
}
else
{
targetWidth
=
largeWidth
}
// 计算对应高度
let
targetHeight
=
targetWidth
/
videoOrientation
.
aspectRatio
// 执行动画
UIView
.
animate
(
withDuration
:
0.3
)
{
var
frame
=
self
.
containerView
.
frame
frame
.
size
=
CGSize
(
width
:
targetWidth
,
height
:
targetHeight
)
frame
.
origin
.
x
=
self
.
containerView
.
center
.
x
-
targetWidth
/
2
frame
.
origin
.
y
=
self
.
containerView
.
center
.
y
-
targetHeight
/
2
self
.
containerView
.
frame
=
frame
// 通知代理
self
.
delegate
?
.
floatingWindow
(
self
,
didChangeSize
:
frame
.
size
)
}
}
// MARK: - Gesture Handlers
@objc
private
func
handlePan
(
_
gesture
:
UIPanGestureRecognizer
)
{
guard
!
isResizing
else
{
return
}
let
translation
=
gesture
.
translation
(
in
:
containerView
.
superview
)
switch
gesture
.
state
{
case
.
began
:
initialCenter
=
containerView
.
center
case
.
changed
:
var
newCenter
=
CGPoint
(
x
:
initialCenter
.
x
+
translation
.
x
,
y
:
initialCenter
.
y
+
translation
.
y
)
newCenter
=
adjustedPosition
(
for
:
newCenter
)
containerView
.
center
=
newCenter
// 通知代理
delegate
?
.
floatingWindow
(
self
,
didChangePosition
:
newCenter
)
case
.
ended
:
let
velocity
=
gesture
.
velocity
(
in
:
containerView
.
superview
)
handlePanEndedWithVelocity
(
velocity
)
default
:
break
}
}
@objc
private
func
handlePinch
(
_
gesture
:
UIPinchGestureRecognizer
)
{
switch
gesture
.
state
{
case
.
began
:
isResizing
=
true
initialFrame
=
containerView
.
frame
lastScale
=
1.0
case
.
changed
:
let
scale
=
gesture
.
scale
/
lastScale
let
newWidth
=
initialFrame
.
width
*
scale
let
newHeight
=
newWidth
/
videoOrientation
.
aspectRatio
var
newFrame
=
initialFrame
newFrame
.
size
=
constrainSize
(
CGSize
(
width
:
newWidth
,
height
:
newHeight
))
newFrame
.
origin
.
x
=
containerView
.
center
.
x
-
newFrame
.
width
/
2
newFrame
.
origin
.
y
=
containerView
.
center
.
y
-
newFrame
.
height
/
2
containerView
.
frame
=
newFrame
// 通知代理
delegate
?
.
floatingWindow
(
self
,
didChangeSize
:
newFrame
.
size
)
case
.
ended
:
isResizing
=
false
snapToNearestSize
()
default
:
break
}
}
@objc
private
func
handleTap
(
_
gesture
:
UITapGestureRecognizer
)
{
delegate
?
.
floatingWindowDidTap
(
self
)
}
@objc
private
func
closeButtonTapped
()
{
delegate
?
.
floatingWindowDidClose
(
self
)
dismiss
()
}
// MARK: - Helper Methods
private
func
constrainSize
(
_
size
:
CGSize
)
->
CGSize
{
var
width
=
size
.
width
var
height
=
size
.
height
// 限制最小/最大尺寸
width
=
max
(
min
(
width
,
Size
.
maxWidth
),
Size
.
minWidth
)
height
=
width
/
videoOrientation
.
aspectRatio
return
CGSize
(
width
:
width
,
height
:
height
)
}
private
func
adjustedPosition
(
for
center
:
CGPoint
)
->
CGPoint
{
guard
let
superview
=
containerView
.
superview
else
{
return
center
}
var
adjustedCenter
=
center
let
halfWidth
=
containerView
.
bounds
.
width
/
2
let
halfHeight
=
containerView
.
bounds
.
height
/
2
// 限制不超出屏幕边界
adjustedCenter
.
x
=
min
(
max
(
halfWidth
,
adjustedCenter
.
x
),
superview
.
bounds
.
width
-
halfWidth
)
adjustedCenter
.
y
=
min
(
max
(
halfHeight
,
adjustedCenter
.
y
),
superview
.
bounds
.
height
-
halfHeight
)
// 计算实际可用区域
let
topLimit
=
superview
.
safeAreaInsets
.
top
+
halfHeight
let
bottomLimit
=
superview
.
bounds
.
height
-
superview
.
safeAreaInsets
.
bottom
-
halfHeight
// 限制不超出屏幕边界和安全区域
adjustedCenter
.
x
=
min
(
max
(
halfWidth
,
adjustedCenter
.
x
),
superview
.
bounds
.
width
-
halfWidth
)
adjustedCenter
.
y
=
min
(
max
(
topLimit
,
adjustedCenter
.
y
),
bottomLimit
)
return
adjustedCenter
}
private
func
handlePanEndedWithVelocity
(
_
velocity
:
CGPoint
)
{
let
magnitude
=
sqrt
((
velocity
.
x
*
velocity
.
x
)
+
(
velocity
.
y
*
velocity
.
y
))
let
slideMultiplier
=
magnitude
/
200
let
slideFactor
=
0.1
*
slideMultiplier
var
finalCenter
=
CGPoint
(
x
:
containerView
.
center
.
x
+
(
velocity
.
x
*
slideFactor
),
y
:
containerView
.
center
.
y
+
(
velocity
.
y
*
slideFactor
)
)
finalCenter
=
adjustedPosition
(
for
:
finalCenter
)
UIView
.
animate
(
withDuration
:
0.3
)
{
self
.
containerView
.
center
=
finalCenter
// 通知代理
self
.
delegate
?
.
floatingWindow
(
self
,
didChangePosition
:
finalCenter
)
}
}
}
// MARK: - UIGestureRecognizerDelegate
extension
YHFloatingWindow
:
UIGestureRecognizerDelegate
{
func
gestureRecognizer
(
_
gestureRecognizer
:
UIGestureRecognizer
,
shouldRecognizeSimultaneouslyWith
otherGestureRecognizer
:
UIGestureRecognizer
)
->
Bool
{
return
true
}
func
gestureRecognizer
(
_
gestureRecognizer
:
UIGestureRecognizer
,
shouldReceive
touch
:
UITouch
)
->
Bool
{
// 如果点击的是按钮,不触发点击手势
if
touch
.
view
is
UIButton
{
return
false
}
return
true
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHPlayerControlView.swift
View file @
613ab8a6
...
@@ -8,20 +8,34 @@
...
@@ -8,20 +8,34 @@
import
UIKit
import
UIKit
protocol
YHPlayerControlViewDelegate
:
AnyObject
{
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapBack
button
:
UIButton
)
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapPlay
button
:
UIButton
)
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didSeekTo
position
:
Float
)
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didChangeQuality
quality
:
YHVideoQuality
)
func
playerControlView
(
_
view
:
YHPlayerControlView
,
didTapFullscreen
button
:
UIButton
)
}
class
YHPlayerControlView
:
UIView
{
class
YHPlayerControlView
:
UIView
{
// MARK: - Properties
// MARK: - Properties
weak
var
delegate
:
YH
MediaPlayer
ViewDelegate
?
weak
var
delegate
:
YH
PlayerControl
ViewDelegate
?
// MARK: - UI Components
// MARK: - UI Components
private
lazy
var
dimView
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.4
)
return
view
}()
private
lazy
var
topBar
:
UIView
=
{
private
lazy
var
topBar
:
UIView
=
{
let
view
=
UIView
()
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.5
)
view
.
backgroundColor
=
.
clear
return
view
return
view
}()
}()
private
lazy
var
backButton
:
UIButton
=
{
private
lazy
var
backButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
let
button
=
UIButton
(
type
:
.
system
)
button
.
setImage
(
UIImage
(
systemName
:
"chevron.left
"
),
for
:
.
normal
)
button
.
setImage
(
UIImage
(
named
:
"icon_back_white
"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
tintColor
=
.
white
button
.
addTarget
(
self
,
action
:
#selector(
backButtonTapped
)
,
for
:
.
touchUpInside
)
button
.
addTarget
(
self
,
action
:
#selector(
backButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
return
button
...
@@ -36,24 +50,26 @@ class YHPlayerControlView: UIView {
...
@@ -36,24 +50,26 @@ class YHPlayerControlView: UIView {
private
lazy
var
bottomBar
:
UIView
=
{
private
lazy
var
bottomBar
:
UIView
=
{
let
view
=
UIView
()
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.5
)
view
.
backgroundColor
=
.
clear
return
view
return
view
}()
}()
private
lazy
var
playButton
:
UIButton
=
{
private
lazy
var
playButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
syste
m
)
let
button
=
UIButton
(
type
:
.
custo
m
)
button
.
setImage
(
UIImage
(
systemName
:
"play.fill
"
),
for
:
.
normal
)
button
.
setImage
(
UIImage
(
named
:
"icon_play
"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
setImage
(
UIImage
(
named
:
"icon_pause"
),
for
:
.
selected
)
button
.
addTarget
(
self
,
action
:
#selector(
playButtonTapped
)
,
for
:
.
touchUpInside
)
button
.
addTarget
(
self
,
action
:
#selector(
playButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
return
button
}()
}()
private
lazy
var
progressSlider
:
UISlider
=
{
private
lazy
var
progressSlider
:
UISlider
=
{
let
slider
=
UISlider
()
let
slider
=
UISlider
()
slider
.
minimumTrackTintColor
=
.
systemBlue
slider
.
minimumTrackTintColor
=
UIColor
.
brandMainColor
slider
.
maximumTrackTintColor
=
.
gray
slider
.
maximumTrackTintColor
=
.
white
.
withAlphaComponent
(
0.3
)
slider
.
setThumbImage
(
UIImage
(
systemName
:
"circle.fill"
)?
.
withTintColor
(
.
white
,
renderingMode
:
.
alwaysOriginal
),
for
:
.
normal
)
slider
.
setThumbImage
(
UIImage
(
named
:
"icon_slider_thumb"
),
for
:
.
normal
)
slider
.
addTarget
(
self
,
action
:
#selector(
sliderValueChanged(_:)
)
,
for
:
.
valueChanged
)
slider
.
addTarget
(
self
,
action
:
#selector(
sliderValueChanged(_:)
)
,
for
:
.
valueChanged
)
slider
.
addTarget
(
self
,
action
:
#selector(
sliderTouchBegan(_:)
)
,
for
:
.
touchDown
)
slider
.
addTarget
(
self
,
action
:
#selector(
sliderTouchEnded(_:)
)
,
for
:
[
.
touchUpInside
,
.
touchUpOutside
,
.
touchCancel
])
return
slider
return
slider
}()
}()
...
@@ -68,15 +84,16 @@ class YHPlayerControlView: UIView {
...
@@ -68,15 +84,16 @@ class YHPlayerControlView: UIView {
private
lazy
var
qualityButton
:
UIButton
=
{
private
lazy
var
qualityButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
let
button
=
UIButton
(
type
:
.
system
)
button
.
setTitle
(
"清晰度"
,
for
:
.
normal
)
button
.
setTitle
(
"清晰度"
,
for
:
.
normal
)
button
.
titleLabel
?
.
font
=
.
systemFont
(
ofSize
:
14
)
button
.
setTitleColor
(
.
white
,
for
:
.
normal
)
button
.
setTitleColor
(
.
white
,
for
:
.
normal
)
button
.
addTarget
(
self
,
action
:
#selector(
qualityButtonTapped
)
,
for
:
.
touchUpInside
)
button
.
addTarget
(
self
,
action
:
#selector(
qualityButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
return
button
}()
}()
private
lazy
var
fullscreenButton
:
UIButton
=
{
private
lazy
var
fullscreenButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
syste
m
)
let
button
=
UIButton
(
type
:
.
custo
m
)
button
.
setImage
(
UIImage
(
systemName
:
"arrow.up.left.and.arrow.down.right
"
),
for
:
.
normal
)
button
.
setImage
(
UIImage
(
named
:
"icon_fullscreen
"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
setImage
(
UIImage
(
named
:
"icon_fullscreen_exit"
),
for
:
.
selected
)
button
.
addTarget
(
self
,
action
:
#selector(
fullscreenButtonTapped
)
,
for
:
.
touchUpInside
)
button
.
addTarget
(
self
,
action
:
#selector(
fullscreenButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
return
button
}()
}()
...
@@ -94,6 +111,7 @@ class YHPlayerControlView: UIView {
...
@@ -94,6 +111,7 @@ class YHPlayerControlView: UIView {
// MARK: - Setup
// MARK: - Setup
private
func
setupUI
()
{
private
func
setupUI
()
{
addSubview
(
dimView
)
addSubview
(
topBar
)
addSubview
(
topBar
)
addSubview
(
bottomBar
)
addSubview
(
bottomBar
)
...
@@ -110,6 +128,10 @@ class YHPlayerControlView: UIView {
...
@@ -110,6 +128,10 @@ class YHPlayerControlView: UIView {
}
}
private
func
setupConstraints
()
{
private
func
setupConstraints
()
{
dimView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
topBar
.
snp
.
makeConstraints
{
make
in
topBar
.
snp
.
makeConstraints
{
make
in
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
height
.
equalTo
(
44
)
make
.
height
.
equalTo
(
44
)
...
@@ -122,7 +144,9 @@ class YHPlayerControlView: UIView {
...
@@ -122,7 +144,9 @@ class YHPlayerControlView: UIView {
}
}
titleLabel
.
snp
.
makeConstraints
{
make
in
titleLabel
.
snp
.
makeConstraints
{
make
in
make
.
center
.
equalTo
(
topBar
)
make
.
left
.
equalTo
(
backButton
.
snp
.
right
)
.
offset
(
8
)
make
.
right
.
equalTo
(
topBar
)
.
offset
(
-
16
)
make
.
centerY
.
equalTo
(
topBar
)
}
}
bottomBar
.
snp
.
makeConstraints
{
make
in
bottomBar
.
snp
.
makeConstraints
{
make
in
...
@@ -162,15 +186,25 @@ class YHPlayerControlView: UIView {
...
@@ -162,15 +186,25 @@ class YHPlayerControlView: UIView {
// MARK: - Actions
// MARK: - Actions
@objc
private
func
backButtonTapped
()
{
@objc
private
func
backButtonTapped
()
{
// Handle back action
delegate
?
.
playerControlView
(
self
,
didTapBack
:
backButton
)
}
}
@objc
private
func
playButtonTapped
()
{
@objc
private
func
playButtonTapped
()
{
delegate
?
.
didTapPlayButton
()
playButton
.
isSelected
.
toggle
()
delegate
?
.
playerControlView
(
self
,
didTapPlay
:
playButton
)
}
}
@objc
private
func
sliderValueChanged
(
_
slider
:
UISlider
)
{
@objc
private
func
sliderValueChanged
(
_
slider
:
UISlider
)
{
delegate
?
.
didSeekToPosition
(
slider
.
value
)
delegate
?
.
playerControlView
(
self
,
didSeekTo
:
slider
.
value
)
timeLabel
.
text
=
"
\(
formatTime
(
slider
.
value
)
)
/
\(
timeLabel
.
text
?
.
components
(
separatedBy
:
" / "
)
.
last
??
"00:00"
)
"
}
@objc
private
func
sliderTouchBegan
(
_
slider
:
UISlider
)
{
// 可以在这里暂停播放
}
@objc
private
func
sliderTouchEnded
(
_
slider
:
UISlider
)
{
// 可以在这里恢复播放
}
}
@objc
private
func
qualityButtonTapped
()
{
@objc
private
func
qualityButtonTapped
()
{
...
@@ -178,7 +212,8 @@ class YHPlayerControlView: UIView {
...
@@ -178,7 +212,8 @@ class YHPlayerControlView: UIView {
}
}
@objc
private
func
fullscreenButtonTapped
()
{
@objc
private
func
fullscreenButtonTapped
()
{
delegate
?
.
didToggleFullscreen
()
fullscreenButton
.
isSelected
.
toggle
()
delegate
?
.
playerControlView
(
self
,
didTapFullscreen
:
fullscreenButton
)
}
}
private
func
showQualitySelector
()
{
private
func
showQualitySelector
()
{
...
@@ -186,7 +221,8 @@ class YHPlayerControlView: UIView {
...
@@ -186,7 +221,8 @@ class YHPlayerControlView: UIView {
YHVideoQuality
.
allCases
.
forEach
{
quality
in
YHVideoQuality
.
allCases
.
forEach
{
quality
in
let
action
=
UIAlertAction
(
title
:
quality
.
rawValue
,
style
:
.
default
)
{
[
weak
self
]
_
in
let
action
=
UIAlertAction
(
title
:
quality
.
rawValue
,
style
:
.
default
)
{
[
weak
self
]
_
in
self
?
.
delegate
?
.
didChangeQuality
(
quality
)
guard
let
self
=
self
else
{
return
}
self
.
delegate
?
.
playerControlView
(
self
,
didChangeQuality
:
quality
)
}
}
alert
.
addAction
(
action
)
alert
.
addAction
(
action
)
}
}
...
@@ -197,13 +233,10 @@ class YHPlayerControlView: UIView {
...
@@ -197,13 +233,10 @@ class YHPlayerControlView: UIView {
viewController
.
present
(
alert
,
animated
:
true
)
viewController
.
present
(
alert
,
animated
:
true
)
}
}
}
}
}
// MARK: - Public Methods
// MARK: - YHPlayerControlView Extension
extension
YHPlayerControlView
{
func
updatePlayButton
(
isPlaying
:
Bool
)
{
func
updatePlayButton
(
isPlaying
:
Bool
)
{
let
imageName
=
isPlaying
?
"pause.fill"
:
"play.fill"
playButton
.
isSelected
=
isPlaying
playButton
.
setImage
(
UIImage
(
systemName
:
imageName
),
for
:
.
normal
)
}
}
func
updateProgress
(
_
progress
:
Float
,
currentTime
:
String
,
totalTime
:
String
)
{
func
updateProgress
(
_
progress
:
Float
,
currentTime
:
String
,
totalTime
:
String
)
{
...
@@ -220,4 +253,24 @@ extension YHPlayerControlView {
...
@@ -220,4 +253,24 @@ extension YHPlayerControlView {
self
.
alpha
=
show
?
1.0
:
0.0
self
.
alpha
=
show
?
1.0
:
0.0
}
}
}
}
func
updateFullscreenButton
(
isFullscreen
:
Bool
)
{
fullscreenButton
.
isSelected
=
isFullscreen
}
// MARK: - Helper Methods
private
func
formatTime
(
_
progress
:
Float
)
->
String
{
let
duration
=
timeLabel
.
text
?
.
components
(
separatedBy
:
" / "
)
.
last
??
"00:00"
let
components
=
duration
.
components
(
separatedBy
:
":"
)
guard
let
minuteString
=
components
.
first
,
let
secondString
=
components
.
last
,
let
minutes
=
Int
(
minuteString
),
let
seconds
=
Int
(
secondString
)
else
{
return
"00:00"
}
let
totalSeconds
=
minutes
*
60
+
seconds
let
currentSeconds
=
Int
(
Float
(
totalSeconds
)
*
progress
)
return
String
(
format
:
"%02d:%02d"
,
currentSeconds
/
60
,
currentSeconds
%
60
)
}
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/live_room_test_bg.png
deleted
100644 → 0
View file @
2a71c649
2.3 MB
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment