/// 获取音乐分类
case musicCategory
/// 获取音乐列表
- case muiscCategoryList(categoryId:String,page:Int)
+ case musicCategoryList(categoryId:String,page:Int)
+ /// 获取音乐地址
+ case musicUrl(filedId:String)
}
// 实现TargetType协议
return "/res/voice-file/storage-size"
case .musicCategory:
return "/res/music-category/list"
- case .muiscCategoryList(let categoryId,let page):
+ case .musicCategoryList(let categoryId,let page):
return "/res/sleep-music/list/\(page)?categoryId=\(categoryId)"
-
+ case .musicUrl( let id):
+ return "/res/voice-file/play/\(id)"
}
}
--- /dev/null
+{
+ "images" : [
+ {
+ "filename" : "logo.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Frame 1985@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Frame 1985@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Frame@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Frame@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Frame@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Frame@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
}
}
+
+extension UIColor {
+ func image(with size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
+ UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
+ defer {
+ UIGraphicsEndImageContext()
+ }
+ let context = UIGraphicsGetCurrentContext()
+ context?.setFillColor(self.cgColor)
+ context?.fill(CGRect(origin:.zero, size: size))
+ return UIGraphicsGetImageFromCurrentImageContext()
+ }
+ func imageCircle(with size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
+ UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
+ defer {
+ UIGraphicsEndImageContext()
+ }
+ let context = UIGraphicsGetCurrentContext()
+ let rect = CGRect(origin:.zero, size: size)
+ // 创建圆形路径
+ let circularPath = UIBezierPath(ovalIn: rect)
+ context?.addPath(circularPath.cgPath)
+ context?.clip()
+ context?.setFillColor(self.cgColor)
+ context?.fill(rect)
+ return UIGraphicsGetImageFromCurrentImageContext()
+ }
+}
+
// MARK: - ********* 压缩图片
public extension YCDevelop where Base: UIImage {
let seconds = totalSeconds % 60
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
+ static func formatSeconds1(_ totalSeconds: Int) -> String {
+ let minutes = (totalSeconds) / 60
+ let seconds = totalSeconds % 60
+ return String(format: "%02d:%02d", minutes, seconds)
+ }
}
self.tabBar.backgroundColor = .white
view.backgroundColor = .color(.bgColor)
- self.selectedIndex = 0
+ self.selectedIndex = 2
}
}
super.init(frame: frame)
setupViews()
setupLayout()
+ setupData()
}
required init?(coder: NSCoder) {
}
func setupLayout(){
+ }
+ func setupData(){
+
}
}
import UIKit
import IQKeyboardManagerSwift
-
+import netfox
class AppLaunch:NSObject{
@MainActor static func start(){
+ NFX.sharedInstance().start()
+
JoySdkManager.shared.setupSdk()
setupKeyboard()
loadCache()
+
+
}
@MainActor static func setupKeyboard(){
App.shared.window = window
window?.rootViewController = BaseTC()
- window?.rootViewController = UINavigationController(rootViewController: LoginVC())
+// window?.rootViewController = UINavigationController(rootViewController: AudioPlayerVC())
AppLaunch.start()
}
--- /dev/null
+//
+// AudioPlayer.swift
+// ShenYin
+//
+// Created by liangshi on 2025/10/9.
+//
+
+import AVFoundation
+
+class AudioPlayerManager {
+ var player: AVPlayer?
+ var playerItem: AVPlayerItem?
+
+ func play(url: URL) {
+ playerItem = AVPlayerItem(url: url)
+ player = AVPlayer(playerItem: playerItem)
+ player?.play()
+ }
+
+ func pause() {
+ player?.pause()
+ }
+
+ func setProgress(_ progress: Float) {
+ let duration = CMTimeGetSeconds((player?.currentItem?.duration)!)
+ let seekTime = CMTimeMakeWithSeconds(Float64(progress) * duration, preferredTimescale: 1)
+ player?.seek(to: seekTime)
+ }
+}
--- /dev/null
+//
+// AudioPlayerVC.swift
+// ShenYin
+//
+// Created by liangshi on 2025/10/9.
+//
+
+import UIKit
+
+class AudioPlayerVC:BaseVC{
+
+ let bgView = UIImageView()
+ let musicView = UIImageView()
+ let slider = UISlider()
+ let nameLabel = UILabel()
+ let titleLabel = UILabel()
+ let valueLabel = UILabel()
+ let playBtn = UIButton(type: .custom)
+ let musicModel:MusicModel
+
+ init(model:MusicModel) {
+ self.musicModel = model
+ super.init()
+ }
+
+
+ override func setupViews() {
+ navBar.leftView.setImage(UIImage(named: "arrow_back_white"), for: .normal)
+ navBar.backgroundColor = .clear
+ view.addSubviews([bgView,musicView,slider,nameLabel,titleLabel,valueLabel,playBtn])
+ view.bringSubviewToFront(navBar)
+
+
+ bgView.do { v in
+// v.image = UIImage(named: "logo")
+ v.kf.setImage(with: musicModel.cover.yc.asUrl)
+ // 创建虚化效果
+ let blurEffect = UIBlurEffect(style:.light)
+ let blurEffectView = UIVisualEffectView(effect: blurEffect)
+ blurEffectView.frame = UIScreen.main.bounds
+ v.addSubview(blurEffectView)
+ }
+ musicView.do { v in
+ v.kf.setImage(with: musicModel.cover.yc.asUrl)
+ v.yc.setCornerRadius(121)
+ }
+
+ slider.do { v in
+ // v.backgroundColor = .red
+ v.minimumValue = 0
+ v.maximumValue = Float(musicModel.duration)
+ v.value = 0
+ v.minimumTrackTintColor = .white
+ v.setThumbImage(UIColor.white.imageCircle(with: CGSize(width: 20, height: 20)), for: .normal)
+ v.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) // continuous changes
+
+ }
+ nameLabel.do { v in
+ v.text = musicModel.name
+ v.textColor = .white
+ v.textAlignment = .center
+ v.font = .medium(18)
+ }
+ titleLabel.do { v in
+ v.text = "00:00"
+ v.textColor = .white
+ v.textAlignment = .center
+ v.font = .regular(12)
+ }
+ valueLabel.do { v in
+ v.text = YLToos.formatSeconds1(musicModel.duration)
+ v.textColor = .white
+ v.textAlignment = .center
+ v.font = .regular(12)
+ }
+ playBtn.do { v in
+ v.setImage(UIImage(named: "music_play"), for: .normal)
+ v.setImage(UIImage(named: "music_pause"), for: .selected)
+ v.addTarget(self, action: #selector(btnAction), for: .touchUpInside)
+ }
+ }
+ override func setupLayout() {
+ bgView.snp.makeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+ musicView.snp.makeConstraints { make in
+ make.size.equalTo(242)
+ make.top.equalToSuperview().inset(kNavBtm + 100)
+ make.centerX.equalToSuperview()
+ }
+ nameLabel.snp.makeConstraints { make in
+ make.centerX.equalToSuperview()
+ make.top.equalTo(musicView.snp.bottom).offset(40)
+ }
+ slider.snp.makeConstraints { make in
+// make.width.equalTo(200)
+ make.height.equalTo(40)
+ make.centerX.equalToSuperview()
+ make.left.right.equalToSuperview().inset(40)
+ make.top.equalTo(musicView.snp.bottom).offset(100)
+ }
+ titleLabel.snp.makeConstraints { make in
+ make.left.equalTo(slider)
+ make.top.equalTo(slider.snp.bottom)
+ }
+ valueLabel.snp.makeConstraints { make in
+ make.right.equalTo(slider)
+ make.top.equalTo(slider.snp.bottom)
+ }
+ playBtn.snp.makeConstraints { make in
+ make.size.equalTo(66)
+ make.centerX.equalToSuperview()
+ make.top.equalTo(slider.snp.bottom).offset(40)
+ }
+ }
+ override func setupData() {
+
+ SleepVM.getMusicUrl(filedId: musicModel.fileId) { [weak self] model in
+ guard let self = self else{return}
+
+ }
+ }
+
+ override func navRightViewTapped() {
+
+ }
+ @objc func addAction(){
+
+ }
+ @objc func btnAction(){
+ playBtn.isSelected = !playBtn.isSelected
+ }
+ @objc func sliderChanged(_ slider:UISlider){
+ let value = YLToos.formatSeconds1(Int(slider.value))
+ Logger.debug(slider.value)
+ if let text = titleLabel.text, text != value {
+ titleLabel.text = value
+ }
+ }
+
+}
--- /dev/null
+//
+// Music.swift
+// ShenYin
+//
+// Created by liangshi on 2025/10/9.
+//
+import HandyJSON
+
+class MusicModelRoot:ResponseBaseModel {
+ var data = MusicData()
+}
+class MusicData:HandyJSON {
+ var list = [MusicModel]()
+ var total = 0
+ var pageSize = 0
+ var currentPage = 0
+ var totalPage = 0
+ var hasNext = false
+ var hasPre = false
+
+ required public init() { }
+}
+class MusicModel:HandyJSON {
+ var id = ""
+ var name = ""
+ var cover = ""
+ var fileId = ""
+ var duration = 0
+ var categoryId = ""
+ required public init() { }
+}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
+
+ var model:MusicModel!{
+ didSet{
+ titleLab.text = model.name
+ imgV.kf.setImage(with: model.cover.yc.asUrl)
+ }
+ }
}
class SleepListView: BaseView,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
private var collectionView:UICollectionView!
+ let vm:SleepVM
- init(selectType type:Int){
+ init(selectType type:MusicCategory){
+ vm = SleepVM(category: type)
super.init(frame: .zero)
+
// setupLayout()
// setupStyle()
- // tableView.mj_header?.beginRefreshing()
+ collectionView.mj_header?.beginRefreshing()
}
- override init(frame: CGRect) {
- super.init(frame: frame)
- }
override func setupViews() {
let layout = UICollectionViewFlowLayout()
// layout.minimumInteritemSpacing = 10.fitR
v.bounces = true
v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
v.register(SleepCell.self, forCellWithReuseIdentifier: SleepCell.reuseID)
-// v.register(VipHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier:VipHeaderView.reuseID )
-// v.register(VipSectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier:VipSectionHeader.reuseID )
+ v.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(refreshAction))
+ v.mj_footer = MJRefreshBackNormalFooter(refreshingTarget: self, refreshingAction: #selector(loadMoreAction))
}
}
- override func setupLayout() {
-
+ override func setupLayout() {
collectionView.snp.makeConstraints { (m) in
m.top.equalToSuperview().inset(10)
m.left.right.bottom.equalToSuperview()
}
}
-
+
+ @objc func refreshAction(){
+ vm.refresh {[weak self] result in
+ guard let self = self else {return}
+ collectionView.mj_header?.endRefreshing()
+ if result.success{
+ collectionView.reloadData()
+ }
+
+ if result.success,result.data.hasNext == false{
+ collectionView.mj_footer?.endRefreshingWithNoMoreData()
+ }
+ else{
+ collectionView.mj_footer?.endRefreshing()
+ }
+ }
+ }
+ @objc func loadMoreAction(){
+ vm.loadMore {[weak self] result in
+ guard let self = self else {return}
+ if result.success{
+ collectionView.reloadData()
+ }
+ }
+ }
+
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
- return 30
+ return vm.list.count
}
-// func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView{
-// if indexPath.section == 0 {
-// let header = VipHeaderView.header(with: collectionView, indexPath: indexPath)
-// header.banner.config(list: vm.bannerList)
-// return header
-// }
-// else{
-// let header = VipSectionHeader.header(with: collectionView, indexPath: indexPath)
-// return header
-// }
-// }
-
-// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
-// if section == 0 {
-// return CGSize(width: kScreenWid, height: 320.fitR)
-// }
-// else{
-// return CGSize(width: kScreenWid, height: 50.fitR)
-// }
-// }
-
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = SleepCell.cell(withCollectionView: collectionView, indexPath: indexPath)
-// cell.model = self.vm.cellList[indexPath.row]
+ cell.model = vm.list[indexPath.row]
return cell
}
-
-
-
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+ let model = vm.list[indexPath.row]
+ App.shared.goToPage(AudioPlayerVC(model: model))
+ }
}
extension SleepListView: JXSegmentedListContainerViewListDelegate {
+++ /dev/null
-////
-//// SleepPageVC.swift
-//// ShenYin
-////
-//// Created by liangshi on 2025/8/6.
-////
-//
-//import UIKit
-//
-//class SleepPageVC:UIViewController{
-//
-// override func viewDidLoad() {
-// super.viewDidLoad()
-// view.backgroundColor = UIColor.random()
-// }
-//
-//}
-//extension UIColor {
-// static func random() -> UIColor {
-// let red = CGFloat.random(in: 0...1)
-// let green = CGFloat.random(in: 0...1)
-// let blue = CGFloat.random(in: 0...1)
-// return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
-// }
-//}
return view
}()
- let vm = SleepVM()
+ var list = [MusicCategory]()
override func viewDidLoad() {
super.viewDidLoad()
segmentedDataSource.isItemSpacingAverageEnabled = true
segmentedDataSource.isTitleColorGradientEnabled = true
// segmentedDataSource.titles = titles.map{"\($0.name)"}
- segmentedDataSource.titles = vm.catogory.map{"\($0.name)"}
+ segmentedDataSource.titles = list.map{"\($0.name)"}
segmentedView.backgroundColor = .clear
//配置指示器
}
override func setupData() {
- vm.getCagory { [weak self ]model in
+ SleepVM.getCagory { [weak self ]model in
guard let self = self else{return}
-
- segmentedDataSource.titles = vm.catogory.map{"\($0.name)"}
- segmentedView.dataSource = segmentedDataSource
-
- listContainerView.reloadData()
- segmentedView.reloadData()
-
+ if model.isSuccess{
+ list = model.data
+ segmentedDataSource.titles = model.data.map{"\($0.name)"}
+ segmentedView.dataSource = segmentedDataSource
+
+ listContainerView.reloadData()
+ segmentedView.reloadData()
+ }
}
}
}
func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate {
// let dele = SleepListView(selectType: titles[index])
- let dele = SleepListView(selectType: index)
+
+ let dele = SleepListView(selectType: list[index])
// dele.popToRoot = { [unowned self] in
// navigationController?.popToRootViewController(animated: true)
//
class SleepVM:BaseVM{
- var catogory = [MusicCategory]()
+ var category = MusicCategory()
+ var list = [MusicModel]()
+ var page = 0
- public func getCagory( completion:@escaping (_ result:MusicCategoryRoot) ->Void) {
- let api = CommomApi.musicCategory
- HttpClient.request(api) {[weak self] (model:MusicCategoryRoot,res) in
+ init(category: MusicCategory) {
+ self.category = category
+ }
+
+ public func refresh(completion:@escaping (_ result:MusicModelRoot) ->Void) {
+ self.page = 1
+ let api = CommomApi.musicCategoryList(categoryId: category.id, page: self.page)
+ HttpClient.request(api) {[weak self] (model:MusicModelRoot,res) in
guard let self = self else {return}
if model.isSuccess{
- catogory = model.data
+ page = model.data.currentPage
+ if model.data.currentPage == 1 {
+ list = model.data.list
+ }
+ else{
+ list += model.data.list
+ }
}
completion(model)
}
}
-
- public func getCagoryList(categoryId:String,page:Int, completion:@escaping (_ result:ResponseBaseModel) ->Void) {
- let api = CommomApi.muiscCategoryList(categoryId: categoryId, page: 1)
- HttpClient.request(api) {[weak self] (model:ResponseBaseModel,res) in
+ public func loadMore(completion:@escaping (_ result:MusicModelRoot) ->Void) {
+ self.page += 1
+ let api = CommomApi.musicCategoryList(categoryId: category.id, page: self.page)
+ HttpClient.request(api) {[weak self] (model:MusicModelRoot,res) in
guard let self = self else {return}
if model.isSuccess{
+ page = model.data.currentPage
+ if model.data.currentPage == 1 {
+ list = model.data.list
+ }
+ else{
+ list += model.data.list
+ }
}
completion(model)
}
}
}
+
+extension SleepVM{
+ static public func getCagory( completion:@escaping (_ result:MusicCategoryRoot) ->Void) {
+ let api = CommomApi.musicCategory
+ HttpClient.request(api) { (model:MusicCategoryRoot,res) in
+ completion(model)
+ }
+ }
+ static public func getMusicUrl(filedId id:String, completion:@escaping (_ result:ResponseBaseModel) ->Void) {
+ let api = CommomApi.musicUrl(filedId: id)
+ HttpClient.request(api) { (model:MusicCategoryRoot,res) in
+ completion(model)
+ }
+ }
+}