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
603302ca
Commit
603302ca
authored
Nov 25, 2024
by
Alex朱枝文
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
接入声网SDK以及直播间搭建
parent
23987683
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
1338 additions
and
1 deletion
+1338
-1
Podfile
galaxy/Podfile
+2
-1
project.pbxproj
galaxy/galaxy.xcodeproj/project.pbxproj
+72
-0
YHBasePlayerViewController.swift
.../LivestreamSales(直播销售)/C/YHBasePlayerViewController.swift
+218
-0
YHLivePlayerViewController.swift
.../LivestreamSales(直播销售)/C/YHLivePlayerViewController.swift
+81
-0
YHVODPlayerViewController.swift
...s/LivestreamSales(直播销售)/C/YHVODPlayerViewController.swift
+173
-0
YHMediaProtocolUtils.swift
...odules/LivestreamSales(直播销售)/M/YHMediaProtocolUtils.swift
+194
-0
YHLiveMessageCell.swift
...s/Modules/LivestreamSales(直播销售)/V/YHLiveMessageCell.swift
+147
-0
YHLiveMessageListView.swift
...dules/LivestreamSales(直播销售)/V/YHLiveMessageListView.swift
+126
-0
YHMessageInputView.swift
.../Modules/LivestreamSales(直播销售)/V/YHMessageInputView.swift
+97
-0
YHPlayerControlView.swift
...Modules/LivestreamSales(直播销售)/V/YHPlayerControlView.swift
+223
-0
YhConstant.swift
galaxy/galaxy/Classes/Tools/Helper/YhConstant.swift
+5
-0
No files found.
galaxy/Podfile
View file @
603302ca
...
...
@@ -107,7 +107,8 @@ target 'galaxy' do
pod
'QY_iOS_SDK'
,
'9.9.2'
#阿里云日志
pod
'AliyunLogProducer'
,
'4.3.3'
#声网SDK播放器等(先弄完整的,后续剪裁)
pod
'AgoraRtcEngine_iOS'
,
'4.4.0'
#, :subspecs => ['RtcBasic']
end
...
...
galaxy/galaxy.xcodeproj/project.pbxproj
View file @
603302ca
This diff is collapsed.
Click to expand it.
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHBasePlayerViewController.swift
0 → 100644
View file @
603302ca
//
// YHBasePlayerViewController.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
AgoraRtcKit
import
UIKit
class
YHBasePlayerViewController
:
UIViewController
{
// MARK: - Properties
var
playerKit
:
AgoraRtcMediaPlayerProtocol
!
var
agoraKit
:
AgoraRtcEngineKit
!
// MARK: - UI Components
private
lazy
var
containerView
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
.
black
return
view
}()
lazy
var
playerView
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
.
black
return
view
}()
private
lazy
var
controlView
:
YHPlayerControlView
=
{
let
view
=
YHPlayerControlView
()
view
.
delegate
=
self
return
view
}()
// MARK: - Lifecycle
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
setupUI
()
setupAgoraKit
()
}
// MARK: - Setup
private
func
setupUI
()
{
view
.
backgroundColor
=
.
black
view
.
addSubview
(
containerView
)
containerView
.
addSubview
(
playerView
)
containerView
.
addSubview
(
controlView
)
setupConstraints
()
}
private
func
setupConstraints
()
{
containerView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
playerView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
controlView
.
snp
.
makeConstraints
{
make
in
make
.
edges
.
equalToSuperview
()
}
}
private
func
setupAgoraKit
()
{
let
config
=
AgoraRtcEngineConfig
()
config
.
appId
=
YhConstant
.
AgoraRtcKit
.
appId
agoraKit
=
AgoraRtcEngineKit
.
sharedEngine
(
with
:
config
,
delegate
:
self
)
playerKit
=
agoraKit
.
createMediaPlayer
(
with
:
self
)
playerKit
.
setView
(
playerView
)
playerKit
.
setRenderMode
(
.
fit
)
}
}
extension
YHBasePlayerViewController
:
YHMediaPlayerViewDelegate
{
// MARK: - MediaPlayerViewDelegate
func
didTapPlayButton
()
{
if
playerKit
.
getPlayerState
()
==
.
playing
{
playerKit
.
pause
()
}
else
{
playerKit
.
play
()
}
}
func
didTapStopButton
()
{
playerKit
.
stop
()
}
func
didSeekToPosition
(
_
position
:
Float
)
{
let
duration
=
playerKit
.
getDuration
()
let
seekPosition
=
Int64
(
Float
(
duration
)
*
position
)
playerKit
.
seek
(
toPosition
:
Int
(
seekPosition
))
}
func
didChangeQuality
(
_
quality
:
YHVideoQuality
)
{
// 根据不同清晰度切换播放源
switch
quality
{
case
.
auto
:
// 自动码率逻辑
break
case
.
sd
:
// 切换到标清源
break
case
.
hd
:
// 切换到高清源
break
case
.
fhd
:
// 切换到超清源
break
}
}
func
didToggleFullscreen
()
{
// 切换全屏
if
UIDevice
.
current
.
orientation
.
isPortrait
{
let
value
=
UIInterfaceOrientation
.
landscapeRight
.
rawValue
UIDevice
.
current
.
setValue
(
value
,
forKey
:
"orientation"
)
}
else
{
let
value
=
UIInterfaceOrientation
.
portrait
.
rawValue
UIDevice
.
current
.
setValue
(
value
,
forKey
:
"orientation"
)
}
}
}
extension
YHBasePlayerViewController
:
AgoraRtcMediaPlayerDelegate
{
// MARK: - AgoraRtcMediaPlayerDelegate
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didChangedTo
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
switch
state
{
case
.
opening
:
print
(
"正在打开媒体源"
)
case
.
playing
:
self
.
controlView
.
updatePlayButton
(
isPlaying
:
true
)
print
(
"正在播放"
)
case
.
paused
:
self
.
controlView
.
updatePlayButton
(
isPlaying
:
false
)
print
(
"已暂停"
)
case
.
stopped
:
self
.
controlView
.
updatePlayButton
(
isPlaying
:
false
)
print
(
"已停止"
)
case
.
failed
:
print
(
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
self
.
showAlert
(
message
:
"播放失败,错误原因:
\(
reason
.
rawValue
)
"
)
default
:
break
}
}
}
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didChangedTo
position
:
Int
)
{
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
)
}
}
}
extension
YHBasePlayerViewController
:
AgoraRtcEngineDelegate
{
// MARK: - AgoraRtcEngineDelegate
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurWarning
warningCode
:
AgoraWarningCode
)
{
print
(
"Warning:
\(
warningCode
.
rawValue
)
"
)
}
func
rtcEngine
(
_
engine
:
AgoraRtcEngineKit
,
didOccurError
errorCode
:
AgoraErrorCode
)
{
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
self
?
.
showAlert
(
message
:
"RTC错误:
\(
errorCode
.
rawValue
)
"
)
}
}
}
extension
YHBasePlayerViewController
{
// MARK: - Helper Methods
private
func
formatTime
(
_
timeInMilliseconds
:
Int
)
->
String
{
let
totalSeconds
=
timeInMilliseconds
/
1000
let
minutes
=
totalSeconds
/
60
let
seconds
=
totalSeconds
%
60
return
String
(
format
:
"%02d:%02d"
,
minutes
,
seconds
)
}
private
func
showAlert
(
message
:
String
)
{
let
alert
=
UIAlertController
(
title
:
"提示"
,
message
:
message
,
preferredStyle
:
.
alert
)
alert
.
addAction
(
UIAlertAction
(
title
:
"确定"
,
style
:
.
default
))
present
(
alert
,
animated
:
true
)
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHLivePlayerViewController.swift
0 → 100644
View file @
603302ca
//
// YHLivePlayerViewController.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
AgoraRtcKit
import
UIKit
class
YHLivePlayerViewController
:
YHBasePlayerViewController
{
// MARK: - Properties
private
var
roomInfo
:
YHLiveRoomInfo
private
var
messageListView
:
YHLiveMessageListView
!
// MARK: - Initialization
init
(
roomInfo
:
YHLiveRoomInfo
)
{
self
.
roomInfo
=
roomInfo
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
// MARK: - Lifecycle
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
setupMessageList
()
startLive
()
}
// MARK: - Setup
private
func
setupMessageList
()
{
messageListView
=
YHLiveMessageListView
()
view
.
addSubview
(
messageListView
)
messageListView
.
snp
.
makeConstraints
{
make
in
make
.
left
.
right
.
equalToSuperview
()
make
.
bottom
.
equalTo
(
view
.
safeAreaLayoutGuide
)
make
.
height
.
equalTo
(
view
.
snp
.
height
)
.
multipliedBy
(
0.3
)
}
}
private
func
startLive
()
{
play
(
with
:
roomInfo
.
streamUrl
)
}
// MARK: - Player Control
private
func
play
(
with
url
:
String
)
{
let
mediaSource
=
AgoraMediaSource
()
mediaSource
.
url
=
url
mediaSource
.
autoPlay
=
true
let
result
=
playerKit
.
open
(
with
:
mediaSource
)
if
result
!=
0
{
//showAlert(message: "播放失败,错误码:\(result)")
}
}
}
extension
YHLivePlayerViewController
{
override
func
AgoraRtcMediaPlayer
(
_
playerKit
:
AgoraRtcMediaPlayerProtocol
,
didChangedTo
state
:
AgoraMediaPlayerState
,
reason
:
AgoraMediaPlayerReason
)
{
super
.
AgoraRtcMediaPlayer
(
playerKit
,
didChangedTo
:
state
,
reason
:
reason
)
// 添加直播特定的状态处理
switch
state
{
case
.
playing
:
// 直播开始时的特殊处理
break
case
.
stopped
:
// 直播结束时的特殊处理
break
default
:
break
}
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/C/YHVODPlayerViewController.swift
0 → 100644
View file @
603302ca
//
// YHVODPlayerViewController.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
AgoraRtcKit
import
UIKit
class
YHVODPlayerViewController
:
YHBasePlayerViewController
{
private
let
videoInfo
:
YHVideoInfo
private
var
playConfig
:
YHVideoPlayConfig
init
(
videoInfo
:
YHVideoInfo
,
playConfig
:
YHVideoPlayConfig
=
YHVideoPlayConfig
())
{
self
.
videoInfo
=
videoInfo
self
.
playConfig
=
playConfig
super
.
init
(
nibName
:
nil
,
bundle
:
nil
)
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
setupVideoInfo
()
startPlayback
()
}
private
func
setupVideoInfo
()
{
// 设置视频标题
title
=
videoInfo
.
title
// 设置作者信息
setupAuthorInfo
()
// 设置播放器配置
setupPlayerConfig
()
}
private
func
setupAuthorInfo
()
{
// 添加作者信息视图
let
authorInfoView
=
YHVideoAuthorInfoView
(
author
:
videoInfo
.
author
)
view
.
addSubview
(
authorInfoView
)
authorInfoView
.
snp
.
makeConstraints
{
make
in
make
.
top
.
equalTo
(
playerView
.
snp
.
bottom
)
make
.
left
.
right
.
equalToSuperview
()
make
.
height
.
equalTo
(
60
)
}
}
private
func
setupPlayerConfig
()
{
// 设置播放器配置
if
playConfig
.
muteOnStart
{
playerKit
.
adjustPlayoutVolume
(
0
)
}
}
private
func
startPlayback
()
{
// 获取合适的视频URL
let
networkType
:
YHNetworkType
=
getNetworkType
()
if
let
videoUrl
=
videoInfo
.
getBestQualityUrl
(
for
:
networkType
)
{
play
(
with
:
videoUrl
)
}
else
{
//showAlert(message: "无法获取视频地址")
}
}
private
func
getNetworkType
()
->
YHNetworkType
{
// 实现网络类型检测逻辑
return
.
wifi
}
// MARK: - Player Control
private
func
play
(
with
url
:
String
)
{
let
mediaSource
=
AgoraMediaSource
()
mediaSource
.
url
=
url
mediaSource
.
autoPlay
=
true
let
result
=
playerKit
.
open
(
with
:
mediaSource
)
if
result
!=
0
{
//showAlert(message: "播放失败,错误码:\(result)")
}
}
private
func
updatePlayCount
()
{
// 更新播放次数的逻辑
}
}
// 作者信息视图
class
YHVideoAuthorInfoView
:
UIView
{
private
let
author
:
YHVideoAuthor
private
lazy
var
avatarImageView
:
UIImageView
=
{
let
imageView
=
UIImageView
()
imageView
.
layer
.
cornerRadius
=
20
imageView
.
layer
.
masksToBounds
=
true
imageView
.
contentMode
=
.
scaleAspectFill
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(直播销售)/M/YHMediaProtocolUtils.swift
0 → 100644
View file @
603302ca
//
// YHMediaProtocolUtils.swift
// galaxy
//
// Created by alexzzw on 2024/11/24.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
Foundation
// MARK: - Protocols
protocol
YHMediaPlayerViewDelegate
:
AnyObject
{
func
didTapPlayButton
()
func
didTapStopButton
()
func
didSeekToPosition
(
_
position
:
Float
)
func
didChangeQuality
(
_
quality
:
YHVideoQuality
)
func
didToggleFullscreen
()
}
// MARK: - Enums
enum
YHVideoQuality
:
String
,
CaseIterable
{
case
auto
=
"自动"
case
sd
=
"标清"
case
hd
=
"高清"
case
fhd
=
"超清"
}
enum
YHPlayerStatus
{
case
idle
case
playing
case
paused
case
stopped
case
error
(
String
)
}
// MARK: - Data Models
struct
YHLiveRoomInfo
{
let
roomId
:
String
let
anchorId
:
String
let
anchorName
:
String
let
streamUrl
:
String
let
title
:
String
let
coverUrl
:
String
?
var
currentQuality
:
YHVideoQuality
=
.
auto
var
viewerCount
:
Int
=
0
var
likeCount
:
Int
=
0
var
status
:
YHLiveRoomStatus
=
.
idle
}
enum
YHLiveRoomStatus
{
case
idle
case
living
case
ended
case
error
(
String
)
}
struct
YHLiveMessage
{
let
id
:
String
=
UUID
()
.
uuidString
let
type
:
YHLiveMessageType
let
sender
:
YHLiveUser
let
content
:
String
let
timestamp
:
Date
static
func
createNormalMessage
(
sender
:
YHLiveUser
,
content
:
String
)
->
YHLiveMessage
{
return
YHLiveMessage
(
type
:
.
normal
,
sender
:
sender
,
content
:
content
,
timestamp
:
Date
())
}
static
func
createSystemMessage
(
_
content
:
String
)
->
YHLiveMessage
{
let
system
=
YHLiveUser
(
userId
:
"system"
,
nickname
:
"系统"
,
level
:
0
,
isAdmin
:
true
)
return
YHLiveMessage
(
type
:
.
system
,
sender
:
system
,
content
:
content
,
timestamp
:
Date
())
}
}
enum
YHLiveMessageType
{
case
normal
// 普通消息
case
system
// 系统消息
case
gift
// 礼物消息
case
enter
// 进入消息
case
like
// 点赞消息
}
struct
YHLiveUser
{
let
userId
:
String
let
nickname
:
String
let
level
:
Int
let
isAdmin
:
Bool
var
avatar
:
String
?
}
// MARK: - Video Models
struct
YHVideoInfo
{
let
videoId
:
String
let
title
:
String
let
description
:
String
?
let
coverUrl
:
String
?
let
duration
:
Int
// 视频时长(秒)
let
createTime
:
Date
// 创建时间
var
playCount
:
Int
// 播放次数
let
category
:
YHVideoCategory
let
author
:
YHVideoAuthor
let
videoUrls
:
[
YHVideoQualityURL
]
// 不同清晰度的视频地址
// 获取指定清晰度的视频URL
func
getVideoUrl
(
for
quality
:
YHVideoQuality
)
->
String
?
{
return
videoUrls
.
first
{
$0
.
quality
==
quality
}?
.
url
}
// 获取最佳清晰度(根据网络情况)
func
getBestQualityUrl
(
for
networkType
:
YHNetworkType
)
->
String
?
{
switch
networkType
{
case
.
wifi
:
return
videoUrls
.
sorted
{
$0
.
quality
.
rawValue
>
$1
.
quality
.
rawValue
}
.
first
?
.
url
case
.
cellular
:
return
videoUrls
.
first
{
$0
.
quality
==
.
sd
}?
.
url
case
.
none
:
return
videoUrls
.
first
{
$0
.
quality
==
.
sd
}?
.
url
}
}
}
// 视频作者信息
struct
YHVideoAuthor
{
let
userId
:
String
let
nickname
:
String
let
avatarUrl
:
String
?
let
description
:
String
?
var
followerCount
:
Int
var
isFollowed
:
Bool
}
// 视频分类
enum
YHVideoCategory
:
String
{
case
entertainment
=
"娱乐"
case
sports
=
"体育"
case
news
=
"新闻"
case
education
=
"教育"
case
gaming
=
"游戏"
case
music
=
"音乐"
case
other
=
"其他"
}
// 不同清晰度的视频URL
struct
YHVideoQualityURL
{
let
quality
:
YHVideoQuality
let
url
:
String
let
bitrate
:
Int
// 比特率
let
resolution
:
YHVideoResolution
}
// 视频分辨率
struct
YHVideoResolution
{
let
width
:
Int
let
height
:
Int
var
description
:
String
{
return
"
\(
width
)
x
\(
height
)
"
}
}
// 网络类型
enum
YHNetworkType
{
case
wifi
case
cellular
case
none
}
// 视频播放相关的配置
struct
YHVideoPlayConfig
{
var
autoPlay
:
Bool
=
true
// 自动播放
var
loopPlay
:
Bool
=
false
// 循环播放
var
muteOnStart
:
Bool
=
false
// 开始时静音
var
preferredQuality
:
YHVideoQuality
=
.
auto
// 首选清晰度
var
enableBackgroundPlay
:
Bool
=
false
// 允许后台播放
}
// 视频播放状态
enum
YHVideoPlayState
{
case
idle
// 空闲
case
loading
// 加载中
case
playing
// 播放中
case
paused
// 暂停
case
ended
// 结束
case
error
(
String
)
// 错误
}
// 视频播放进度信息
struct
YHVideoProgress
{
let
currentTime
:
TimeInterval
let
duration
:
TimeInterval
var
progress
:
Float
{
return
duration
>
0
?
Float
(
currentTime
/
duration
)
:
0
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHLiveMessageCell.swift
0 → 100644
View file @
603302ca
//
// YHLiveMessageCell.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
class
YHLiveMessageCell
:
UITableViewCell
{
static
let
reuseIdentifier
=
"YHLiveMessageCell"
// MARK: - UI Components
private
let
containerView
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.4
)
view
.
layer
.
cornerRadius
=
8
return
view
}()
private
let
levelLabel
:
YHPaddedLabel
=
{
let
label
=
YHPaddedLabel
(
padding
:
UIEdgeInsets
(
top
:
2
,
left
:
4
,
bottom
:
2
,
right
:
4
))
label
.
font
=
.
systemFont
(
ofSize
:
10
)
label
.
textColor
=
.
white
label
.
backgroundColor
=
.
systemBlue
label
.
layer
.
cornerRadius
=
4
label
.
layer
.
masksToBounds
=
true
return
label
}()
private
let
nicknameLabel
:
UILabel
=
{
let
label
=
UILabel
()
label
.
font
=
.
systemFont
(
ofSize
:
14
,
weight
:
.
medium
)
label
.
textColor
=
.
systemYellow
return
label
}()
private
let
contentLabel
:
UILabel
=
{
let
label
=
UILabel
()
label
.
font
=
.
systemFont
(
ofSize
:
14
)
label
.
textColor
=
.
white
label
.
numberOfLines
=
0
return
label
}()
// MARK: - Initialization
override
init
(
style
:
UITableViewCell
.
CellStyle
,
reuseIdentifier
:
String
?)
{
super
.
init
(
style
:
style
,
reuseIdentifier
:
reuseIdentifier
)
setupUI
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
private
func
setupUI
()
{
backgroundColor
=
.
clear
selectionStyle
=
.
none
contentView
.
addSubview
(
containerView
)
containerView
.
addSubview
(
levelLabel
)
containerView
.
addSubview
(
nicknameLabel
)
containerView
.
addSubview
(
contentLabel
)
setupConstraints
()
}
private
func
setupConstraints
()
{
containerView
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalToSuperview
()
.
offset
(
8
)
make
.
right
.
lessThanOrEqualToSuperview
()
.
offset
(
-
8
)
make
.
top
.
equalToSuperview
()
.
offset
(
4
)
make
.
bottom
.
equalToSuperview
()
.
offset
(
-
4
)
}
levelLabel
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalToSuperview
()
.
offset
(
8
)
make
.
centerY
.
equalTo
(
nicknameLabel
)
}
nicknameLabel
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
levelLabel
.
snp
.
right
)
.
offset
(
8
)
make
.
top
.
equalToSuperview
()
.
offset
(
8
)
}
contentLabel
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalToSuperview
()
.
offset
(
8
)
make
.
right
.
equalToSuperview
()
.
offset
(
-
8
)
make
.
top
.
equalTo
(
nicknameLabel
.
snp
.
bottom
)
.
offset
(
4
)
make
.
bottom
.
equalToSuperview
()
.
offset
(
-
8
)
}
}
// MARK: - Configuration
func
configure
(
with
message
:
YHLiveMessage
)
{
levelLabel
.
text
=
"Lv.
\(
message
.
sender
.
level
)
"
nicknameLabel
.
text
=
message
.
sender
.
nickname
switch
message
.
type
{
case
.
normal
:
configureNormalMessage
(
message
)
case
.
system
:
configureSystemMessage
(
message
)
case
.
gift
:
configureGiftMessage
(
message
)
case
.
enter
:
configureEnterMessage
(
message
)
case
.
like
:
configureLikeMessage
(
message
)
}
}
private
func
configureNormalMessage
(
_
message
:
YHLiveMessage
)
{
contentLabel
.
text
=
message
.
content
contentLabel
.
textColor
=
.
white
if
message
.
sender
.
isAdmin
{
nicknameLabel
.
textColor
=
.
systemRed
}
else
{
nicknameLabel
.
textColor
=
.
systemYellow
}
}
private
func
configureSystemMessage
(
_
message
:
YHLiveMessage
)
{
levelLabel
.
isHidden
=
true
nicknameLabel
.
isHidden
=
true
contentLabel
.
text
=
message
.
content
contentLabel
.
textColor
=
.
systemGray
contentLabel
.
textAlignment
=
.
center
}
private
func
configureGiftMessage
(
_
message
:
YHLiveMessage
)
{
contentLabel
.
text
=
message
.
content
contentLabel
.
textColor
=
.
systemPink
}
private
func
configureEnterMessage
(
_
message
:
YHLiveMessage
)
{
contentLabel
.
text
=
"进入了直播间"
contentLabel
.
textColor
=
.
systemGreen
}
private
func
configureLikeMessage
(
_
message
:
YHLiveMessage
)
{
contentLabel
.
text
=
"点了赞"
contentLabel
.
textColor
=
.
systemPink
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHLiveMessageListView.swift
0 → 100644
View file @
603302ca
//
// YHLiveMessageListView.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
class
YHLiveMessageListView
:
UIView
{
// MARK: - Properties
private
var
messages
:
[
YHLiveMessage
]
=
[]
private
let
maxMessageCount
=
200
// MARK: - UI Components
private
lazy
var
tableView
:
UITableView
=
{
let
tableView
=
UITableView
()
tableView
.
backgroundColor
=
.
clear
tableView
.
separatorStyle
=
.
none
tableView
.
showsVerticalScrollIndicator
=
false
tableView
.
allowsSelection
=
false
tableView
.
delegate
=
self
tableView
.
dataSource
=
self
tableView
.
register
(
YHLiveMessageCell
.
self
,
forCellReuseIdentifier
:
YHLiveMessageCell
.
reuseIdentifier
)
return
tableView
}()
private
lazy
var
messageInputView
:
YHMessageInputView
=
{
let
view
=
YHMessageInputView
(
frame
:
CGRect
.
zero
)
view
.
delegate
=
self
return
view
}()
// MARK: - Initialization
override
init
(
frame
:
CGRect
)
{
super
.
init
(
frame
:
frame
)
setupUI
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
private
func
setupUI
()
{
backgroundColor
=
.
clear
addSubview
(
tableView
)
addSubview
(
messageInputView
)
setupConstraints
()
}
private
func
setupConstraints
()
{
tableView
.
snp
.
makeConstraints
{
make
in
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
bottom
.
equalTo
(
messageInputView
.
snp
.
top
)
}
messageInputView
.
snp
.
makeConstraints
{
make
in
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
height
.
equalTo
(
44
)
}
}
// MARK: - Public Methods
func
addMessage
(
_
message
:
YHLiveMessage
)
{
messages
.
append
(
message
)
if
messages
.
count
>
maxMessageCount
{
messages
.
removeFirst
(
messages
.
count
-
maxMessageCount
)
}
let
indexPath
=
IndexPath
(
row
:
messages
.
count
-
1
,
section
:
0
)
tableView
.
insertRows
(
at
:
[
indexPath
],
with
:
.
fade
)
scrollToBottom
(
animated
:
true
)
}
func
clearMessages
()
{
messages
.
removeAll
()
tableView
.
reloadData
()
}
private
func
scrollToBottom
(
animated
:
Bool
)
{
guard
!
messages
.
isEmpty
else
{
return
}
let
lastIndex
=
IndexPath
(
row
:
messages
.
count
-
1
,
section
:
0
)
tableView
.
scrollToRow
(
at
:
lastIndex
,
at
:
.
bottom
,
animated
:
animated
)
}
}
// MARK: - UITableViewDelegate & UITableViewDataSource
extension
YHLiveMessageListView
:
UITableViewDelegate
,
UITableViewDataSource
{
func
tableView
(
_
tableView
:
UITableView
,
numberOfRowsInSection
section
:
Int
)
->
Int
{
return
messages
.
count
}
func
tableView
(
_
tableView
:
UITableView
,
cellForRowAt
indexPath
:
IndexPath
)
->
UITableViewCell
{
guard
let
cell
=
tableView
.
dequeueReusableCell
(
withIdentifier
:
YHLiveMessageCell
.
reuseIdentifier
,
for
:
indexPath
)
as?
YHLiveMessageCell
else
{
return
UITableViewCell
()
}
let
message
=
messages
[
indexPath
.
row
]
cell
.
configure
(
with
:
message
)
return
cell
}
}
// MARK: - MessageInputViewDelegate
extension
YHLiveMessageListView
:
YHMessageInputViewDelegate
{
func
messageInputView
(
_
inputView
:
YHMessageInputView
,
didSendMessage
text
:
String
)
{
// 创建当前用户
let
currentUser
=
YHLiveUser
(
userId
:
"current_user"
,
nickname
:
"我"
,
level
:
1
,
isAdmin
:
false
)
// 创建消息
let
message
=
YHLiveMessage
.
createNormalMessage
(
sender
:
currentUser
,
content
:
text
)
// 添加消息到列表
addMessage
(
message
)
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHMessageInputView.swift
0 → 100644
View file @
603302ca
//
// YHMessageInputView.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
protocol
YHMessageInputViewDelegate
:
AnyObject
{
func
messageInputView
(
_
inputView
:
YHMessageInputView
,
didSendMessage
text
:
String
)
}
class
YHMessageInputView
:
UIView
{
// MARK: - Properties
weak
var
delegate
:
YHMessageInputViewDelegate
?
// MARK: - UI Components
private
lazy
var
textField
:
UITextField
=
{
let
textField
=
UITextField
()
textField
.
placeholder
=
"说点什么..."
textField
.
textColor
=
.
white
textField
.
backgroundColor
=
UIColor
.
white
.
withAlphaComponent
(
0.2
)
textField
.
layer
.
cornerRadius
=
17
textField
.
leftView
=
UIView
(
frame
:
CGRect
(
x
:
0
,
y
:
0
,
width
:
10
,
height
:
34
))
textField
.
leftViewMode
=
.
always
textField
.
returnKeyType
=
.
send
textField
.
delegate
=
self
return
textField
}()
private
lazy
var
sendButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
setTitle
(
"发送"
,
for
:
.
normal
)
button
.
setTitleColor
(
.
white
,
for
:
.
normal
)
button
.
addTarget
(
self
,
action
:
#selector(
sendButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
// MARK: - Initialization
override
init
(
frame
:
CGRect
)
{
super
.
init
(
frame
:
frame
)
setupUI
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
private
func
setupUI
()
{
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.4
)
addSubview
(
textField
)
addSubview
(
sendButton
)
setupConstraints
()
}
private
func
setupConstraints
()
{
textField
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalToSuperview
()
.
offset
(
8
)
make
.
centerY
.
equalToSuperview
()
make
.
right
.
equalTo
(
sendButton
.
snp
.
left
)
.
offset
(
-
8
)
make
.
height
.
equalTo
(
34
)
}
sendButton
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalToSuperview
()
.
offset
(
-
8
)
make
.
centerY
.
equalToSuperview
()
make
.
width
.
equalTo
(
50
)
}
}
@objc
private
func
sendButtonTapped
()
{
sendMessage
()
}
private
func
sendMessage
()
{
guard
let
text
=
textField
.
text
?
.
trimmingCharacters
(
in
:
.
whitespacesAndNewlines
),
!
text
.
isEmpty
else
{
return
}
delegate
?
.
messageInputView
(
self
,
didSendMessage
:
text
)
textField
.
text
=
""
}
}
// MARK: - UITextFieldDelegate
extension
YHMessageInputView
:
UITextFieldDelegate
{
func
textFieldShouldReturn
(
_
textField
:
UITextField
)
->
Bool
{
sendMessage
()
textField
.
resignFirstResponder
()
return
true
}
}
galaxy/galaxy/Classes/Modules/LivestreamSales(直播销售)/V/YHPlayerControlView.swift
0 → 100644
View file @
603302ca
//
// YHPlayerControlView.swift
// galaxy
//
// Created by alexzzw on 2024/11/25.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import
UIKit
class
YHPlayerControlView
:
UIView
{
// MARK: - Properties
weak
var
delegate
:
YHMediaPlayerViewDelegate
?
// MARK: - UI Components
private
lazy
var
topBar
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.5
)
return
view
}()
private
lazy
var
backButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
setImage
(
UIImage
(
systemName
:
"chevron.left"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
addTarget
(
self
,
action
:
#selector(
backButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
private
lazy
var
titleLabel
:
UILabel
=
{
let
label
=
UILabel
()
label
.
textColor
=
.
white
label
.
font
=
.
systemFont
(
ofSize
:
16
,
weight
:
.
medium
)
return
label
}()
private
lazy
var
bottomBar
:
UIView
=
{
let
view
=
UIView
()
view
.
backgroundColor
=
UIColor
.
black
.
withAlphaComponent
(
0.5
)
return
view
}()
private
lazy
var
playButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
setImage
(
UIImage
(
systemName
:
"play.fill"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
addTarget
(
self
,
action
:
#selector(
playButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
private
lazy
var
progressSlider
:
UISlider
=
{
let
slider
=
UISlider
()
slider
.
minimumTrackTintColor
=
.
systemBlue
slider
.
maximumTrackTintColor
=
.
gray
slider
.
setThumbImage
(
UIImage
(
systemName
:
"circle.fill"
)?
.
withTintColor
(
.
white
,
renderingMode
:
.
alwaysOriginal
),
for
:
.
normal
)
slider
.
addTarget
(
self
,
action
:
#selector(
sliderValueChanged(_:)
)
,
for
:
.
valueChanged
)
return
slider
}()
private
lazy
var
timeLabel
:
UILabel
=
{
let
label
=
UILabel
()
label
.
textColor
=
.
white
label
.
font
=
.
systemFont
(
ofSize
:
12
)
label
.
text
=
"00:00 / 00:00"
return
label
}()
private
lazy
var
qualityButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
setTitle
(
"清晰度"
,
for
:
.
normal
)
button
.
setTitleColor
(
.
white
,
for
:
.
normal
)
button
.
addTarget
(
self
,
action
:
#selector(
qualityButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
private
lazy
var
fullscreenButton
:
UIButton
=
{
let
button
=
UIButton
(
type
:
.
system
)
button
.
setImage
(
UIImage
(
systemName
:
"arrow.up.left.and.arrow.down.right"
),
for
:
.
normal
)
button
.
tintColor
=
.
white
button
.
addTarget
(
self
,
action
:
#selector(
fullscreenButtonTapped
)
,
for
:
.
touchUpInside
)
return
button
}()
// MARK: - Initialization
override
init
(
frame
:
CGRect
)
{
super
.
init
(
frame
:
frame
)
setupUI
()
}
required
init
?(
coder
:
NSCoder
)
{
super
.
init
(
coder
:
coder
)
setupUI
()
}
// MARK: - Setup
private
func
setupUI
()
{
addSubview
(
topBar
)
addSubview
(
bottomBar
)
topBar
.
addSubview
(
backButton
)
topBar
.
addSubview
(
titleLabel
)
bottomBar
.
addSubview
(
playButton
)
bottomBar
.
addSubview
(
progressSlider
)
bottomBar
.
addSubview
(
timeLabel
)
bottomBar
.
addSubview
(
qualityButton
)
bottomBar
.
addSubview
(
fullscreenButton
)
setupConstraints
()
}
private
func
setupConstraints
()
{
topBar
.
snp
.
makeConstraints
{
make
in
make
.
top
.
left
.
right
.
equalToSuperview
()
make
.
height
.
equalTo
(
44
)
}
backButton
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
topBar
)
.
offset
(
16
)
make
.
centerY
.
equalTo
(
topBar
)
make
.
size
.
equalTo
(
CGSize
(
width
:
44
,
height
:
44
))
}
titleLabel
.
snp
.
makeConstraints
{
make
in
make
.
center
.
equalTo
(
topBar
)
}
bottomBar
.
snp
.
makeConstraints
{
make
in
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
height
.
equalTo
(
44
)
}
playButton
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
bottomBar
)
.
offset
(
16
)
make
.
centerY
.
equalTo
(
bottomBar
)
make
.
size
.
equalTo
(
CGSize
(
width
:
44
,
height
:
44
))
}
progressSlider
.
snp
.
makeConstraints
{
make
in
make
.
left
.
equalTo
(
playButton
.
snp
.
right
)
.
offset
(
8
)
make
.
right
.
equalTo
(
timeLabel
.
snp
.
left
)
.
offset
(
-
8
)
make
.
centerY
.
equalTo
(
bottomBar
)
}
timeLabel
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalTo
(
qualityButton
.
snp
.
left
)
.
offset
(
-
8
)
make
.
centerY
.
equalTo
(
bottomBar
)
make
.
width
.
equalTo
(
100
)
}
qualityButton
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalTo
(
fullscreenButton
.
snp
.
left
)
.
offset
(
-
8
)
make
.
centerY
.
equalTo
(
bottomBar
)
}
fullscreenButton
.
snp
.
makeConstraints
{
make
in
make
.
right
.
equalTo
(
bottomBar
)
.
offset
(
-
16
)
make
.
centerY
.
equalTo
(
bottomBar
)
make
.
size
.
equalTo
(
CGSize
(
width
:
44
,
height
:
44
))
}
}
// MARK: - Actions
@objc
private
func
backButtonTapped
()
{
// Handle back action
}
@objc
private
func
playButtonTapped
()
{
delegate
?
.
didTapPlayButton
()
}
@objc
private
func
sliderValueChanged
(
_
slider
:
UISlider
)
{
delegate
?
.
didSeekToPosition
(
slider
.
value
)
}
@objc
private
func
qualityButtonTapped
()
{
showQualitySelector
()
}
@objc
private
func
fullscreenButtonTapped
()
{
delegate
?
.
didToggleFullscreen
()
}
private
func
showQualitySelector
()
{
let
alert
=
UIAlertController
(
title
:
"选择清晰度"
,
message
:
nil
,
preferredStyle
:
.
actionSheet
)
YHVideoQuality
.
allCases
.
forEach
{
quality
in
let
action
=
UIAlertAction
(
title
:
quality
.
rawValue
,
style
:
.
default
)
{
[
weak
self
]
_
in
self
?
.
delegate
?
.
didChangeQuality
(
quality
)
}
alert
.
addAction
(
action
)
}
alert
.
addAction
(
UIAlertAction
(
title
:
"取消"
,
style
:
.
cancel
))
if
let
viewController
=
self
.
window
?
.
rootViewController
{
viewController
.
present
(
alert
,
animated
:
true
)
}
}
}
// MARK: - YHPlayerControlView Extension
extension
YHPlayerControlView
{
func
updatePlayButton
(
isPlaying
:
Bool
)
{
let
imageName
=
isPlaying
?
"pause.fill"
:
"play.fill"
playButton
.
setImage
(
UIImage
(
systemName
:
imageName
),
for
:
.
normal
)
}
func
updateProgress
(
_
progress
:
Float
,
currentTime
:
String
,
totalTime
:
String
)
{
progressSlider
.
value
=
progress
timeLabel
.
text
=
"
\(
currentTime
)
/
\(
totalTime
)
"
}
func
setTitle
(
_
title
:
String
)
{
titleLabel
.
text
=
title
}
func
showControls
(
_
show
:
Bool
)
{
UIView
.
animate
(
withDuration
:
0.3
)
{
self
.
alpha
=
show
?
1.0
:
0.0
}
}
}
galaxy/galaxy/Classes/Tools/Helper/YhConstant.swift
View file @
603302ca
...
...
@@ -228,6 +228,11 @@ extension YhConstant {
}
struct
AgoraRtcKit
{
static
let
appId
:
String
=
"f1da9c5b9fb946148761278273f43a14"
static
let
certificate
:
String
?
=
"1776134c2dcb4d60bc3d1983fef02212"
}
// MARK: - 通知相关 名称
class
YhNotification
{
//登录成功
...
...
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