480 lines
16 KiB
Swift
480 lines
16 KiB
Swift
//
|
||
// FS_DetailsVC.swift
|
||
// Funny_sounds
|
||
//
|
||
// Created by 忆海16 on 2024/8/16.
|
||
//
|
||
|
||
|
||
import UIKit
|
||
import SnapKit
|
||
import AVFoundation
|
||
import SDWebImage
|
||
|
||
class FS_DetailsVC: RootVC {
|
||
|
||
var backBtn = UIButton()
|
||
var collectBtn = UIButton()
|
||
var camearBtn = UIButton()
|
||
var bgimageV = UIImageView()
|
||
var conentView = UIView()
|
||
var conentBgImageV = UIImageView()
|
||
var contengImageV = UIImageView()
|
||
var nameLabel = UILabel()
|
||
var bottomView = UIView()
|
||
var bottomImageV = UIImageView()
|
||
|
||
var delayView = UIView()
|
||
var delayLabel = BorderedLabel()
|
||
var delayBtn = UIButton()
|
||
var delayimageV = UIImageView()
|
||
|
||
var countdown: CountdownTimer?
|
||
|
||
lazy var collectionView: UICollectionView = {
|
||
let layout = UICollectionViewFlowLayout()
|
||
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||
collectionView.backgroundColor = .clear
|
||
collectionView.showsVerticalScrollIndicator = true
|
||
return collectionView
|
||
}()
|
||
|
||
var captureSession: AVCaptureSession!
|
||
var previewLayer: AVCaptureVideoPreviewLayer!
|
||
|
||
var arr: [AudioFile] = []
|
||
|
||
var model:AudioFile?
|
||
|
||
var audioPlayer: AVPlayer?
|
||
var playerItem: AVPlayerItem?
|
||
|
||
let cellWidth: CGFloat = 92
|
||
let cellHeight: CGFloat = 92
|
||
let cellSpacing: CGFloat = 10
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
// 初始化捕捉会话
|
||
captureSession = AVCaptureSession()
|
||
captureSession.sessionPreset = .high
|
||
|
||
// 获取设备的默认视频捕捉设备 (通常是后置摄像头)
|
||
guard let camera = AVCaptureDevice.default(for: .video) else {
|
||
print("无法访问摄像头")
|
||
return
|
||
}
|
||
|
||
do {
|
||
// 将摄像头作为输入设备添加到会话中
|
||
let input = try AVCaptureDeviceInput(device: camera)
|
||
if captureSession.canAddInput(input) {
|
||
captureSession.addInput(input)
|
||
} else {
|
||
print("无法添加输入设备")
|
||
return
|
||
}
|
||
} catch {
|
||
print("Error: \(error.localizedDescription)")
|
||
return
|
||
}
|
||
|
||
// 创建一个预览层,用来展示相机捕捉到的内容
|
||
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
||
previewLayer.videoGravity = .resizeAspectFill
|
||
previewLayer.frame = view.layer.bounds
|
||
view.layer.addSublayer(previewLayer)
|
||
|
||
// 启动捕捉会话
|
||
captureSession.startRunning()
|
||
|
||
// 添加点击手势识别器
|
||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap))
|
||
bgimageV.isUserInteractionEnabled = true
|
||
bgimageV.addGestureRecognizer(tapGesture)
|
||
|
||
setUI()
|
||
setcollectionView()
|
||
|
||
|
||
setEditUI()
|
||
|
||
let audioFile = AudioFile(title: model?.title ?? "", mp3Url: model?.mp3Url ?? "", preUrl: model?.preUrl ?? "")
|
||
// 检查是否已收藏
|
||
if collectManager.shared.isFavorite(audioFile: audioFile) {
|
||
print("音频文件已被收藏")
|
||
self.collectBtn.isSelected = true
|
||
} else {
|
||
print("音频文件未被收藏")
|
||
self.collectBtn.isSelected = false
|
||
}
|
||
|
||
preloadAudio()
|
||
|
||
}
|
||
|
||
|
||
// MARK: - Preload Audio
|
||
func preloadAudio() {
|
||
// MP3 file URL
|
||
if let url = URL(string: model!.mp3Url) {
|
||
// Create AVPlayerItem with the URL
|
||
playerItem = AVPlayerItem(url: url)
|
||
|
||
// Initialize AVPlayer with the preloaded AVPlayerItem
|
||
audioPlayer = AVPlayer(playerItem: playerItem)
|
||
} else {
|
||
print("Invalid audio URL")
|
||
}
|
||
}
|
||
|
||
|
||
// MARK: - UI
|
||
|
||
func setUI(){
|
||
bgimageV.image = UIImage(named: "settingBg")
|
||
bgimageV.contentMode = .scaleAspectFill
|
||
self.view.addSubview(bgimageV)
|
||
|
||
backBtn.setImage(UIImage(named: "back"), for: .normal)
|
||
backBtn.addTarget(self, action: #selector(back), for: .touchUpInside)
|
||
self.view.addSubview(backBtn)
|
||
|
||
|
||
collectBtn.setImage(UIImage(named: "collect_n"), for: .normal)
|
||
collectBtn.setImage(UIImage(named: "collect_s"), for: .selected)
|
||
collectBtn.addTarget(self, action: #selector(collectTouch), for: .touchUpInside)
|
||
self.view.addSubview(collectBtn)
|
||
|
||
camearBtn.setImage(UIImage(named: "camear_n"), for: .normal)
|
||
camearBtn.setImage(UIImage(named: "camear_s"), for: .selected)
|
||
camearBtn.addTarget(self, action: #selector(camearBtnTouch), for: .touchUpInside)
|
||
self.view.addSubview(camearBtn)
|
||
|
||
|
||
conentView.backgroundColor = .clear
|
||
self.view.addSubview(conentView)
|
||
|
||
|
||
conentBgImageV.image = UIImage(named: "contentBg")
|
||
conentBgImageV.contentMode = .scaleAspectFill
|
||
self.conentView.addSubview(conentBgImageV)
|
||
|
||
|
||
contengImageV.image = UIImage(named: "collectionImageV")
|
||
contengImageV.layer.cornerRadius = 70
|
||
contengImageV.contentMode = .scaleToFill
|
||
contengImageV.clipsToBounds = true
|
||
self.conentView.addSubview(contengImageV)
|
||
|
||
nameLabel.text = "Air Horn1"
|
||
nameLabel.font = UIFont(name: "PaytoneOne-Regular", size: 25)
|
||
nameLabel.textColor = .white
|
||
nameLabel.numberOfLines = 0
|
||
nameLabel.textAlignment = .center
|
||
self.conentView.addSubview(nameLabel)
|
||
|
||
|
||
bottomView.backgroundColor = .clear
|
||
self.view.addSubview(bottomView)
|
||
|
||
bottomImageV.image = UIImage(named: "alphabgImageV")
|
||
bottomImageV.contentMode = .scaleAspectFill
|
||
self.bottomView.addSubview(bottomImageV)
|
||
|
||
|
||
collectionView.backgroundColor = .clear
|
||
collectionView.showsHorizontalScrollIndicator = false
|
||
self.bottomView.addSubview(collectionView)
|
||
|
||
delayView.backgroundColor = UIColor.hexSting(color: "#4EDB96", alpha: 0.8)
|
||
delayView.layer.cornerRadius = 8
|
||
delayView.clipsToBounds = true
|
||
self.view.addSubview(delayView)
|
||
|
||
|
||
delayLabel.text = "Delay: OFF"
|
||
delayLabel.font = UIFont(name: "PaytoneOne-Regular", size: 15)
|
||
delayLabel.textColor = .white // 设置文本颜色为白色
|
||
delayLabel.numberOfLines = 0
|
||
delayLabel.textAlignment = .center
|
||
|
||
// 设置阴影效果
|
||
let shadow = NSShadow()
|
||
shadow.shadowColor = UIColor.black.withAlphaComponent(1.0) // 黑色阴影
|
||
shadow.shadowOffset = CGSize(width: 0, height: 6) // 向下偏移2个点
|
||
shadow.shadowBlurRadius = 0 // 无模糊度
|
||
|
||
// 设置属性字符串并应用阴影
|
||
let attributedText = NSMutableAttributedString(string: delayLabel.text!)
|
||
attributedText.addAttribute(.shadow, value: shadow, range: NSRange(location: 0, length: delayLabel.text!.count))
|
||
delayLabel.attributedText = attributedText
|
||
delayLabel.sizeToFit()
|
||
self.delayView.addSubview(delayLabel)
|
||
|
||
delayimageV.image = UIImage(named: "delayimageV")
|
||
self.delayView.addSubview(delayimageV)
|
||
|
||
delayBtn.addTarget(self, action: #selector(delaysTouch), for: .touchUpInside)
|
||
|
||
self.delayView.addSubview(delayBtn)
|
||
|
||
bgimageV.snp.makeConstraints { make in
|
||
make.width.equalTo(self.view)
|
||
make.height.equalTo(self.view)
|
||
}
|
||
|
||
|
||
backBtn.snp.makeConstraints { make in
|
||
make.top.equalTo(self.view.safeAreaLayoutGuide).offset(20)
|
||
make.height.width.equalTo(36)
|
||
make.left.equalToSuperview().offset(20)
|
||
}
|
||
|
||
collectBtn.snp.makeConstraints { make in
|
||
make.centerY.equalTo(backBtn)
|
||
make.right.equalToSuperview().offset(-13)
|
||
make.width.height.equalTo(48)
|
||
}
|
||
|
||
camearBtn.snp.makeConstraints { make in
|
||
make.centerX.equalTo(collectBtn)
|
||
make.top.equalTo(collectBtn.snp.bottom).offset(18)
|
||
make.width.height.equalTo(48)
|
||
}
|
||
|
||
conentView.snp.makeConstraints { make in
|
||
make.height.equalTo(260)
|
||
make.width.equalTo(220)
|
||
make.top.equalTo(backBtn.snp.bottom).offset(60)
|
||
make.centerX.equalToSuperview()
|
||
}
|
||
|
||
conentBgImageV.snp.makeConstraints { make in
|
||
make.width.height.equalTo(200)
|
||
make.top.equalTo(conentView.snp.top)
|
||
make.centerX.equalTo(conentView)
|
||
}
|
||
|
||
contengImageV.snp.makeConstraints { make in
|
||
make.height.width.equalTo(140)
|
||
make.centerX.centerY.equalTo(conentBgImageV)
|
||
}
|
||
|
||
nameLabel.snp.makeConstraints { make in
|
||
make.top.equalTo(conentBgImageV.snp.bottom).offset(10)
|
||
make.left.equalTo(conentView.snp.left).offset(5)
|
||
make.right.equalTo(conentView.snp.right).offset(5)
|
||
}
|
||
|
||
bottomView.snp.makeConstraints { make in
|
||
make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(0)
|
||
make.height.equalTo(120)
|
||
make.width.equalToSuperview()
|
||
}
|
||
|
||
bottomImageV.snp.makeConstraints { make in
|
||
make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(0)
|
||
make.height.equalTo(120)
|
||
make.width.equalToSuperview()
|
||
|
||
}
|
||
|
||
collectionView.snp.makeConstraints { make in
|
||
make.width.height.equalTo(bottomView)
|
||
}
|
||
|
||
delayView.snp.makeConstraints { make in
|
||
make.width.equalToSuperview().offset(-40)
|
||
make.height.equalTo(40)
|
||
make.bottom.equalTo(collectionView.snp.top).offset(-60)
|
||
make.centerX.equalToSuperview()
|
||
}
|
||
|
||
delayLabel.snp.makeConstraints { make in
|
||
make.left.equalTo(delayView.snp.left).offset(16)
|
||
make.centerY.equalTo(delayView).offset(-4)
|
||
}
|
||
|
||
delayimageV.snp.makeConstraints { make in
|
||
make.centerY.equalTo(delayView)
|
||
// make.width.equalTo(6)
|
||
// make.height.equalTo(7)
|
||
make.left.equalTo(delayLabel.snp.right).offset(15)
|
||
}
|
||
|
||
delayBtn.snp.makeConstraints { make in
|
||
make.width.height.equalTo(delayView)
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
// MARK: -collectionView
|
||
func setcollectionView(){
|
||
|
||
let layout = UICollectionViewFlowLayout()
|
||
layout.scrollDirection = .horizontal
|
||
collectionView.delegate = self
|
||
collectionView.dataSource = self
|
||
collectionView.collectionViewLayout = layout
|
||
collectionView.register(FS_DetailsCollectionViewCell.self, forCellWithReuseIdentifier: "FS_DetailsCollectionViewCell")
|
||
|
||
}
|
||
// MARK: - 修改UI
|
||
func setEditUI(){
|
||
if let imageURL = URL(string: model?.preUrl ?? "") {
|
||
contengImageV.sd_setImage(with: imageURL, placeholderImage: UIImage(named: "collectionImageV"))
|
||
}
|
||
self.nameLabel.text = model?.title
|
||
|
||
}
|
||
|
||
// MARK: - 返回
|
||
@objc func back(){
|
||
self.navigationController?.popViewController(animated: true)
|
||
}
|
||
|
||
// MARK: - 收藏点击
|
||
@objc func collectTouch(){
|
||
let audioFile = AudioFile(title: model?.title ?? "", mp3Url: model?.mp3Url ?? "", preUrl: model?.preUrl ?? "")
|
||
|
||
collectManager.shared.toggleFavorite(audioFile: audioFile)
|
||
|
||
// 检查是否已收藏
|
||
if collectManager.shared.isFavorite(audioFile: audioFile) {
|
||
print("音频文件已被收藏")
|
||
self.collectBtn.isSelected = true
|
||
} else {
|
||
print("音频文件未被收藏")
|
||
self.collectBtn.isSelected = false
|
||
}
|
||
|
||
}
|
||
|
||
|
||
// MARK: - 相机点击
|
||
@objc func camearBtnTouch(){
|
||
if self.camearBtn.isSelected == true{
|
||
self.camearBtn.isSelected = false
|
||
self.conentView.isHidden = false
|
||
self.bgimageV.isHidden = false
|
||
}else{
|
||
self.camearBtn.isSelected = true
|
||
self.camearBtn.isSelected = true
|
||
self.conentView.isHidden = true
|
||
self.bgimageV.isHidden = true
|
||
}
|
||
|
||
}
|
||
|
||
|
||
// MARK: - 相机
|
||
override func viewWillLayoutSubviews() {
|
||
super.viewWillLayoutSubviews()
|
||
// 确保预览层的大小在旋转或布局改变时更新
|
||
previewLayer.frame = view.layer.bounds
|
||
}
|
||
|
||
|
||
// MARK: - 全屏点击
|
||
@objc func handleTap() {
|
||
// 点击时播放音频
|
||
playAudio()
|
||
}
|
||
// MARK: - 播放
|
||
func playAudio() {
|
||
// MP3 文件的 URL
|
||
// if let url = URL(string: model!.mp3Url) {
|
||
// // 初始化 AVPlayer
|
||
// audioPlayer = AVPlayer(url: url)
|
||
// audioPlayer?.play()
|
||
// } else {
|
||
// print("无效的音频 URL")
|
||
// }
|
||
if let player = audioPlayer {
|
||
player.seek(to: .zero)
|
||
player.play()
|
||
} else {
|
||
print("Audio player is not ready")
|
||
}
|
||
}
|
||
|
||
// MARK: - 点击选择倒计时
|
||
@objc func delaysTouch() {
|
||
print("delaysTouch called")
|
||
|
||
let pickerView = CustomPickerView()
|
||
pickerView.onValueSelected = { value in
|
||
print("Selected value: \(value)")
|
||
|
||
// 停止旧的计时器(如果有的话)
|
||
self.countdown?.stop()
|
||
|
||
// 创建新的计时器实例
|
||
self.countdown = CountdownTimer(seconds: value, onTick: { remainingSeconds in
|
||
print("剩余时间: \(remainingSeconds) 秒")
|
||
|
||
// 在主线程上更新 UI
|
||
DispatchQueue.main.async {
|
||
self.delayLabel.text = "Delay: \(remainingSeconds) S"
|
||
}
|
||
|
||
}, onComplete: {
|
||
print("倒计时结束!")
|
||
|
||
// 在主线程上更新 UI 和播放音频
|
||
DispatchQueue.main.async {
|
||
self.playAudio()
|
||
self.delayLabel.text = "Delay: OFF"
|
||
}
|
||
})
|
||
|
||
// 在主线程上启动计时器
|
||
DispatchQueue.main.async {
|
||
self.countdown?.start()
|
||
}
|
||
}
|
||
|
||
pickerView.showInView(self.view)
|
||
print("pickerView shown")
|
||
}
|
||
|
||
|
||
}
|
||
// MARK: -设置collectionView代理
|
||
extension FS_DetailsVC:UICollectionViewDelegateFlowLayout,UICollectionViewDataSource,UICollectionViewDelegate{
|
||
|
||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||
return arr.count
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
|
||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FS_DetailsCollectionViewCell", for: indexPath)as!FS_DetailsCollectionViewCell
|
||
cell.model = arr[indexPath.row]
|
||
|
||
return cell
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||
return CGSize(width: cellWidth, height: cellHeight)
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
|
||
return cellSpacing
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
|
||
return UIEdgeInsets(top: 0, left: cellSpacing, bottom: 0, right: cellSpacing)
|
||
}
|
||
|
||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
|
||
|
||
self.model = arr[indexPath.row]
|
||
setEditUI()
|
||
preloadAudio()
|
||
playAudio()
|
||
}
|
||
}
|