From 8f9479a7dcee32f5807321b1f456a043dde80318 Mon Sep 17 00:00:00 2001 From: wenlei Date: Thu, 8 Feb 2024 16:51:12 +0800 Subject: [PATCH] Filter module --- .../IndieMusic/Application/Application.swift | 4 +- .../IndieMusic/Application/Navigator.swift | 4 +- .../IndieMusic/Managers/LaunchADManager.swift | 172 +++++++-------- IndieMusic/IndieMusic/Models/Search.swift | 2 + .../Modules/Home/FilterViewController.swift | 2 +- .../Modules/Home/FilterViewModel.swift | 85 +++++--- .../IndieMusic/Modules/Home/HomeView.swift | 26 ++- .../Modules/Home/HomeViewController.swift | 54 +++-- .../Modules/Home/HomeViewModel.swift | 58 +++++- .../JournalDetailController.swift | 4 +- .../JournalDetailViewModel.swift | 68 ++++-- .../Search/MusicStyleViewController.swift | 197 ++++++++++-------- .../Modules/Search/MusicStyleViewModel.swift | 26 ++- .../Search/SearchResultsController.swift | 51 ++++- .../Search/SearchResultsViewModel.swift | 30 +-- .../Modules/Setting/SettingViewMdel.swift | 2 +- IndieMusic/IndieMusic/Networking/Api.swift | 5 +- .../Networking/Rest/APIConfig.swift | 13 +- .../IndieMusic/Networking/Rest/RestApi.swift | 16 +- IndieMusic/Podfile | 2 +- 20 files changed, 516 insertions(+), 305 deletions(-) diff --git a/IndieMusic/IndieMusic/Application/Application.swift b/IndieMusic/IndieMusic/Application/Application.swift index 5dc0432..c81245f 100644 --- a/IndieMusic/IndieMusic/Application/Application.swift +++ b/IndieMusic/IndieMusic/Application/Application.swift @@ -16,12 +16,12 @@ final class Application: NSObject { var provider: IndieMusicAPI? let navigator: Navigator let authManager: AuthManager - let launchADManager: LaunchADManager +// let launchADManager: LaunchADManager private override init() { authManager = AuthManager.shared - launchADManager = LaunchADManager.shared +// launchADManager = LaunchADManager.shared navigator = Navigator.default super.init() updateProvider() diff --git a/IndieMusic/IndieMusic/Application/Navigator.swift b/IndieMusic/IndieMusic/Application/Navigator.swift index 79b3450..7a7805c 100644 --- a/IndieMusic/IndieMusic/Application/Navigator.swift +++ b/IndieMusic/IndieMusic/Application/Navigator.swift @@ -239,9 +239,9 @@ class Navigator { private func show(target: UIViewController, sender: UIViewController?, transition: Transition) { switch transition { case .root(in: let window): - UIView.transition(with: window, duration: 0.5, options: .transitionFlipFromLeft, animations: { +// UIView.transition(with: window, duration: 0, options: .curveEaseInOut, animations: { window.rootViewController = target - }, completion: nil) +// }, completion: nil) return case .custom: return default: break diff --git a/IndieMusic/IndieMusic/Managers/LaunchADManager.swift b/IndieMusic/IndieMusic/Managers/LaunchADManager.swift index 685f406..6a689f1 100644 --- a/IndieMusic/IndieMusic/Managers/LaunchADManager.swift +++ b/IndieMusic/IndieMusic/Managers/LaunchADManager.swift @@ -6,9 +6,9 @@ // import UIKit -import XHLaunchAd +//import XHLaunchAd -class LaunchADManager: NSObject, XHLaunchAdDelegate { +class LaunchADManager: NSObject { static let shared = LaunchADManager() @@ -27,94 +27,94 @@ class LaunchADManager: NSObject, XHLaunchAdDelegate { } func setupXHLaunchAd() { - photoAD() +// photoAD() } - func photoAD() { - XHLaunchAd.setLaunch(.launchScreen) - XHLaunchAd.setWaitDataDuration(2) - - //配置广告数据 - let imageAdconfiguration = XHLaunchImageAdConfiguration.init() - imageAdconfiguration.duration = 4 - imageAdconfiguration.frame = CGRect.init(x: 0, y: 0, width: BaseDimensions.screenWidth, height: BaseDimensions.screenHeight) -// imageAdconfiguration.imageNameOrURLString = adModel.mediaUrl ?? "" - - //缓存机制(仅对网络图片有效) - //为告展示效果更好,可设置为XHLaunchAdImageCacheInBackground,先缓存,下次显示 - imageAdconfiguration.imageOption = .cacheInBackground - imageAdconfiguration.contentMode = .scaleAspectFill -// imageAdconfiguration.openModel = adModel.pointTo ?? "" - - //广告显示完成动画 - imageAdconfiguration.showFinishAnimate = .fadein - //广告显示完成动画时间 - imageAdconfiguration.showFinishAnimateTime = 0.4 - //跳过按钮类型 - imageAdconfiguration.skipButtonType = .timeText -// imageAdconfiguration.customSkipView = self.skipButton() - //后台返回时,是否显示广告 - imageAdconfiguration.showEnterForeground = false - - //图片已缓存 - 显示一个 "已预载" 视图 (可选) - // if XHLaunchAd.checkImageInCache(with: URL.init(string: model.mediaUrl)) { - // //设置要添加的自定义视图(可选) - //// imageAdconfiguration.subViews = [self launchAdSubViews_alreadyView]; - // } - - //显示开屏广告 - XHLaunchAd.imageAd(with: imageAdconfiguration, delegate: self) - - - } - - func skipButton() -> UIButton { - let skipButton = UIButton.init(frame: CGRect.init(x: BaseDimensions.screenWidth - 100, y: BaseDimensions.topHeight, width: 85, height: 30)) - skipButton.titleLabel?.font = UIFont.systemFont(ofSize: 14) - skipButton.layer.cornerRadius = 5.0 - skipButton.layer.borderWidth = 1.5 - skipButton.layer.borderColor = UIColor.lightGray.cgColor - skipButton.backgroundColor = .gray - skipButton.setTitleColor(.white, for: .normal) - skipButton.addTarget(self, action: #selector(skipAction), for: .touchUpInside) - skipButton.setTitle("跳过", for: .normal) - return skipButton - } - -// @objc func skipButtonClick() { +// func photoAD() { +// XHLaunchAd.setLaunch(.launchScreen) +// XHLaunchAd.setWaitDataDuration(2) +// +// //配置广告数据 +// let imageAdconfiguration = XHLaunchImageAdConfiguration.init() +// imageAdconfiguration.duration = 4 +// imageAdconfiguration.frame = CGRect.init(x: 0, y: 0, width: BaseDimensions.screenWidth, height: BaseDimensions.screenHeight) +//// imageAdconfiguration.imageNameOrURLString = adModel.mediaUrl ?? "" +// +// //缓存机制(仅对网络图片有效) +// //为告展示效果更好,可设置为XHLaunchAdImageCacheInBackground,先缓存,下次显示 +// imageAdconfiguration.imageOption = .cacheInBackground +// imageAdconfiguration.contentMode = .scaleAspectFill +//// imageAdconfiguration.openModel = adModel.pointTo ?? "" +// +// //广告显示完成动画 +// imageAdconfiguration.showFinishAnimate = .fadein +// //广告显示完成动画时间 +// imageAdconfiguration.showFinishAnimateTime = 0.4 +// //跳过按钮类型 +// imageAdconfiguration.skipButtonType = .timeText +//// imageAdconfiguration.customSkipView = self.skipButton() +// //后台返回时,是否显示广告 +// imageAdconfiguration.showEnterForeground = false +// +// //图片已缓存 - 显示一个 "已预载" 视图 (可选) +// // if XHLaunchAd.checkImageInCache(with: URL.init(string: model.mediaUrl)) { +// // //设置要添加的自定义视图(可选) +// //// imageAdconfiguration.subViews = [self launchAdSubViews_alreadyView]; +// // } +// +// //显示开屏广告 +// XHLaunchAd.imageAd(with: imageAdconfiguration, delegate: self) +// +// +// } +// +// func skipButton() -> UIButton { +// let skipButton = UIButton.init(frame: CGRect.init(x: BaseDimensions.screenWidth - 100, y: BaseDimensions.topHeight, width: 85, height: 30)) +// skipButton.titleLabel?.font = UIFont.systemFont(ofSize: 14) +// skipButton.layer.cornerRadius = 5.0 +// skipButton.layer.borderWidth = 1.5 +// skipButton.layer.borderColor = UIColor.lightGray.cgColor +// skipButton.backgroundColor = .gray +// skipButton.setTitleColor(.white, for: .normal) +// skipButton.addTarget(self, action: #selector(skipAction), for: .touchUpInside) +// skipButton.setTitle("跳过", for: .normal) +// return skipButton +// } +// +//// @objc func skipButtonClick() { +//// +//// } +// +// +// /// 跳过 +// @objc func skipAction() { +// XHLaunchAd.removeAnd(animated: true) +// } +// +// // 点击事件 +// func xhLaunchAd(_ launchAd: XHLaunchAd, clickAndOpenModel openModel: Any, click clickPoint: CGPoint) { +// /** openModel即配置广告数据设置的点击广告时打开页面参数(configuration.openModel) */ +// +//// let webVC = WebViewController.init(viewModel: nil, ) +//// webVC.url = openModel as? String +//// MainRootVc?.children.first?.navigationController?.pushViewController(webVC, animated: true) +// } +// +// +// func xhLaunchAdShowFinish(_ launchAd: XHLaunchAd) { +// +// +// +// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notiShowAD"), object: nil, userInfo: nil) +// +// } +// +// func xhLaunchAd(_ launchAd: XHLaunchAd, customSkip customSkipView: UIView, duration: Int) { +// var button = self.skipButton() +// //设置自定义跳过按钮倒计时 +// button.setTitle("\(duration)跳过", for: .normal) // // } - - - /// 跳过 - @objc func skipAction() { - XHLaunchAd.removeAnd(animated: true) - } - - // 点击事件 - func xhLaunchAd(_ launchAd: XHLaunchAd, clickAndOpenModel openModel: Any, click clickPoint: CGPoint) { - /** openModel即配置广告数据设置的点击广告时打开页面参数(configuration.openModel) */ - -// let webVC = WebViewController.init(viewModel: nil, ) -// webVC.url = openModel as? String -// MainRootVc?.children.first?.navigationController?.pushViewController(webVC, animated: true) - } - - - func xhLaunchAdShowFinish(_ launchAd: XHLaunchAd) { - - - - NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notiShowAD"), object: nil, userInfo: nil) - - } - - func xhLaunchAd(_ launchAd: XHLaunchAd, customSkip customSkipView: UIView, duration: Int) { - var button = self.skipButton() - //设置自定义跳过按钮倒计时 - button.setTitle("\(duration)跳过", for: .normal) - - } } diff --git a/IndieMusic/IndieMusic/Models/Search.swift b/IndieMusic/IndieMusic/Models/Search.swift index 085f244..6ff40d7 100644 --- a/IndieMusic/IndieMusic/Models/Search.swift +++ b/IndieMusic/IndieMusic/Models/Search.swift @@ -72,6 +72,8 @@ struct SearchCategory: Codable { let nameEn: String? let image: String? let description: String? + + var isExpand: Bool? } diff --git a/IndieMusic/IndieMusic/Modules/Home/FilterViewController.swift b/IndieMusic/IndieMusic/Modules/Home/FilterViewController.swift index 964ee87..f7ae620 100644 --- a/IndieMusic/IndieMusic/Modules/Home/FilterViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Home/FilterViewController.swift @@ -91,7 +91,7 @@ class FilterViewController: ViewController { output.modelSelected.drive { [weak self] sectionItem in print(sectionItem) -// self?.navigator.dismiss(sender: self) + self?.navigator.dismiss(sender: self) }.disposed(by: rx.disposeBag) } diff --git a/IndieMusic/IndieMusic/Modules/Home/FilterViewModel.swift b/IndieMusic/IndieMusic/Modules/Home/FilterViewModel.swift index b5db6b4..7cd88f8 100644 --- a/IndieMusic/IndieMusic/Modules/Home/FilterViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Home/FilterViewModel.swift @@ -25,6 +25,15 @@ class FilterViewModel: ViewModel, ViewModelType { let itemSelected = PublishSubject() let items = BehaviorRelay<[FilterSection]>.init(value: []) + + var filter: BehaviorRelay = .init(value: nil) + + init(filter: BehaviorRelay, provider: IndieMusicAPI) { + super.init(provider: provider) + + self.filter = filter + } + func transform(input: Input) -> Output { input.viewWillAppear.subscribe { _ in @@ -46,6 +55,10 @@ class FilterViewModel: ViewModel, ViewModelType { let selection2 = FilterSection.journalNo(header: "期刊", items: journalNoList ?? []) self.items.accept([selection0, selection1, selection2]) + + if let filter = self.filter.value { + self.selectItem(selectedFilter: filter) + } } onError: { error in }.disposed(by: self.rx.disposeBag) @@ -54,38 +67,7 @@ class FilterViewModel: ViewModel, ViewModelType { input.modelSelected.drive { [weak self] selectedFilter in guard let self = self else { return } - var updatedSections: [FilterSection] = [] - - // 遍历每个section - self.items.value.forEach { section in - switch section { - case .style(let header, var items), .language(let header, var items), .journalNo(let header, var items): - // 遍历每个item,更新isSelected状态 - for index in items.indices { - if items[index].name == selectedFilter.name { - // 选中的cell设置为true - items[index].isSelected = true - } else { - // 非选中的cell设置为false - items[index].isSelected = false - } - } - // 根据section类型,创建新的section实例 - let updatedSection: FilterSection - switch section { - case .style: - updatedSection = .style(header: header, items: items) - case .language: - updatedSection = .language(header: header, items: items) - case .journalNo: - updatedSection = .journalNo(header: header, items: items) - } - updatedSections.append(updatedSection) - } - } - - self.items.accept(updatedSections) - + selectItem(selectedFilter: selectedFilter) }.disposed(by: rx.disposeBag) @@ -95,6 +77,45 @@ class FilterViewModel: ViewModel, ViewModelType { } + func selectItem(selectedFilter: Filter) { + var updatedSections: [FilterSection] = [] + + // 遍历每个section + self.items.value.forEach { section in + switch section { + case .style(let header, var items), .language(let header, var items), .journalNo(let header, var items): + // 遍历每个item,更新isSelected状态 + for index in items.indices { + if items[index].name == selectedFilter.name { + // 选中的cell设置为true + items[index].isSelected = true + } else { + // 非选中的cell设置为false + items[index].isSelected = false + } + } + // 根据section类型,创建新的section实例 + let updatedSection: FilterSection + switch section { + case .style: + updatedSection = .style(header: header, items: items) + case .language: + updatedSection = .language(header: header, items: items) + case .journalNo: + updatedSection = .journalNo(header: header, items: items) + } + updatedSections.append(updatedSection) + } + } + + self.items.accept(updatedSections) + + self.filter.accept(selectedFilter) + + + } + + func fetchFilterData() -> Observable { return self.provider.filterMenu() diff --git a/IndieMusic/IndieMusic/Modules/Home/HomeView.swift b/IndieMusic/IndieMusic/Modules/Home/HomeView.swift index c5312be..5686079 100644 --- a/IndieMusic/IndieMusic/Modules/Home/HomeView.swift +++ b/IndieMusic/IndieMusic/Modules/Home/HomeView.swift @@ -452,17 +452,23 @@ class HomeFilterButton: UIControl { }() - var isFiltered: Bool = false { + var filter: Filter? { didSet { - - imageView.image = isFiltered ? UIImage.init(named: "home_drop_icon") : UIImage.init(named: "play_more_btn") - - titleLabel.text = "测试" - - layer.cornerRadius = isFiltered ? 12 : 0 - layer.borderColor = UIColor.black.cgColor - layer.borderWidth = isFiltered ? 1 : 0 + if let filter = filter { + imageView.image = UIImage.init(named: "home_drop_icon") + titleLabel.text = filter.name + layer.cornerRadius = 12 + layer.borderColor = UIColor.black.cgColor + layer.borderWidth = 1 + + } else { + imageView.image = UIImage.init(named: "play_more_btn") + layer.cornerRadius = 0 + layer.borderColor = UIColor.black.cgColor + layer.borderWidth = 0 + } + } } @@ -485,7 +491,7 @@ class HomeFilterButton: UIControl { - if isFiltered { + if filter != nil { imageView.snp.remakeConstraints { make in make.right.equalTo(self).offset(-8) make.centerY.equalTo(self) diff --git a/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift b/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift index 692dc2f..f82f9ef 100644 --- a/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Home/HomeViewController.swift @@ -24,7 +24,10 @@ class HomeViewController: TableViewController { var currentNavBarBgAlpha: CGFloat = 0 + + let randomAudioTrackTrigger = PublishRelay.init() + weak var headerView: HomeSectionView? override func viewDidLoad() { super.viewDidLoad() @@ -63,8 +66,8 @@ class HomeViewController: TableViewController { override func makeUI() { super.makeUI() - let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: BaseDimensions.screenWidth, height: 120)) - homePagerView.frame = CGRect.init(x: 18, y: 0, width: BaseDimensions.screenWidth - 18 * 2, height: 120) + let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: BaseDimensions.screenWidth, height: 138)) + homePagerView.frame = CGRect.init(x: 18, y: 18, width: BaseDimensions.screenWidth - 18 * 2, height: 120) homePagerView.pagerView.dataSource = self homePagerView.pagerView.delegate = self @@ -86,7 +89,8 @@ class HomeViewController: TableViewController { let input = HomeViewModel.Input.init(viewWillAppear: rx.viewWillAppear, headerRefresh: refresh, footerRefresh: footerRefreshTrigger, - selection: tableView.rx.itemSelected.asDriver()) + selection: tableView.rx.itemSelected.asDriver(), + randomAudioTrackTrigger: randomAudioTrackTrigger) let output = viewModel.transform(input: input) let dataSource = HomeViewController.dataSource { cell, item in @@ -148,6 +152,34 @@ class HomeViewController: TableViewController { }.disposed(by: rx.disposeBag) + + output.randomAudioTrack.subscribe { audioTrackArray in + guard let audioTrackArray = audioTrackArray.element, + let audioTrack = audioTrackArray.first else { return } + + let playerViewModel = PlayerViewModel.init(track: audioTrack, provider: viewModel.provider) + self.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + // 这里放置延迟执行的代码 + AudioManager.sharedInstance.setPlaylist(list: audioTrackArray) + AudioManager.sharedInstance.playTrack(track: audioTrack) + } + + }.disposed(by: rx.disposeBag) + + + + viewModel.filter.subscribe { filter in + guard let filter = filter.element else { return } + + self.headerView?.filterButton.filter = filter + + + }.disposed(by: rx.disposeBag) + + + } override func viewDidLayoutSubviews() { @@ -269,25 +301,21 @@ extension HomeViewController { } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let viewModel = self.viewModel as? HomeViewModel else { return nil } + let header = HomeSectionView.init() + self.headerView = header + self.headerView?.filterButton.filter = viewModel.filter.value header.theFMButtonClosures = {[weak self] in - guard let viewModel = self?.viewModel, - let track = AudioManager.sharedInstance.currentTrack else { return } - - let playerViewModel = PlayerViewModel.init(track: track, provider: viewModel.provider) - self?.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal) - + self?.randomAudioTrackTrigger.accept(()) } header.filterButtonClosures = {[weak self] in - header.filterButton.isFiltered.toggle() guard let viewModel = self?.viewModel as? HomeViewModel, let navigator = self?.navigator else { return } - let filterViewModel = FilterViewModel.init(provider: viewModel.provider) -// let filter = FilterViewController.init(viewModel: filterViewModel, navigator: navigator) -// self.present(filter, animated: true) + let filterViewModel = FilterViewModel.init(filter: viewModel.filter, provider: viewModel.provider) navigator.show(segue: .filter(viewModel: filterViewModel), sender: self, transition: .navigationPresent(type: .filter)) } diff --git a/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift b/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift index 93868a3..ce1e3da 100644 --- a/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Home/HomeViewModel.swift @@ -16,6 +16,7 @@ class HomeViewModel: ViewModel, ViewModelType { let headerRefresh: Observable let footerRefresh: Observable let selection: Driver + let randomAudioTrackTrigger: PublishRelay } struct Output { @@ -25,7 +26,7 @@ class HomeViewModel: ViewModel, ViewModelType { let selection: Driver let itemSelected: PublishSubject let footerState: PublishRelay -// let filterTrigger: Driver + let randomAudioTrack: PublishRelay<[AudioTrack]> } @@ -34,7 +35,10 @@ class HomeViewModel: ViewModel, ViewModelType { let categoryId = BehaviorRelay.init(value: "") let journalNoRange = BehaviorRelay.init(value: nil) + let randomAudioTrack = PublishRelay<[AudioTrack]>.init() + let filter = BehaviorRelay.init(value: nil) + func transform(input: Input) -> Output { let carouselItems = BehaviorRelay<[Carousel]>(value: []) @@ -104,17 +108,56 @@ class HomeViewModel: ViewModel, ViewModelType { guard let sectionItem = elements.value.first?.items[indexPath.row] else { return } self.itemSelected.onNext(sectionItem) }.disposed(by: rx.disposeBag) + + + input.randomAudioTrackTrigger.subscribe { _ in + self.requestRandomAudioTrack().subscribe { audioTrackArray in + self.randomAudioTrack.accept(audioTrackArray) + } onError: { error in + + }.disposed(by: self.rx.disposeBag) + }.disposed(by: rx.disposeBag) + + + + filter.subscribe { filter in + guard let filter = filter.element else { return } + + self.page = 1 + let categoryId = filter?.id + + self.request(categoryId: filter?.id ?? nil, journalNoRange: filter?.id == "" ? filter?.name : nil, pageNum: self.page, pageSize: 10) + .subscribe { items in + let array = items.map { journal in + return HomeSectionItem.journalDetil(model: journal) + } + + let section = HomeSectionModel.journal(title: "", items: array) + + elements.accept([section]) + + } onError: { error in + print("筛选错误\(error)") + + } .disposed(by: self.rx.disposeBag) + + + + }.disposed(by: rx.disposeBag) + + return Output.init(carouselItems: carouselItems, items: elements, selection: input.selection, itemSelected: itemSelected, - footerState: footerState) + footerState: footerState, + randomAudioTrack: randomAudioTrack) } - func request(categoryId: String, + func request(categoryId: String?, journalNoRange: String?, pageNum: Int, pageSize: Int) -> Observable<[Journal]> { @@ -131,4 +174,13 @@ class HomeViewModel: ViewModel, ViewModelType { .trackError(error) } + + func requestRandomAudioTrack() -> Observable<[AudioTrack]> { + return self.provider.randomAudioTrack(limit: 30) + .trackActivity(loading) + .trackError(error) + } + + + } diff --git a/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailController.swift b/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailController.swift index fb3a83c..6a65c87 100644 --- a/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailController.swift +++ b/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailController.swift @@ -121,8 +121,10 @@ class JournalDetailController: ViewController, UIScrollViewDelegate { guard let viewModel = viewModel as? JournalDetailViewModel else { return } + let viewDidLoad = Observable.of(()) let input = JournalDetailViewModel.Input.init(viewWillAppear: rx.viewWillAppear, - selection: collectionView.rx.itemSelected.asDriver(), + viewDidLoad: viewDidLoad, + selection: collectionView.rx.itemSelected.asDriver(), notiPlayAudioTrack: NotificationCenter.default.rx.notification(.notiPlayAudioTrack)) // dropButtonTrigger: headerView.dropButton.rx.tap.asDriver(), diff --git a/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailViewModel.swift b/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailViewModel.swift index fc9ea7a..b837591 100644 --- a/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/JournalDetail/JournalDetailViewModel.swift @@ -13,6 +13,7 @@ class JournalDetailViewModel: ViewModel, ViewModelType { struct Input { let viewWillAppear: ControlEvent + let viewDidLoad: Observable let selection: Driver let notiPlayAudioTrack: Observable @@ -72,34 +73,52 @@ class JournalDetailViewModel: ViewModel, ViewModelType { input.viewWillAppear.subscribe { (_) in - guard let journalNo = self.journal.journalNo else { return } - self.requestMusic(journalNo: journalNo).subscribe { audioTrackArray in - - let audioArray = audioTrackArray.map { audioTrack in - return JournalItem.audioItem(model: audioTrack) - } + }.disposed(by: rx.disposeBag) + - self.journal.trackCount = audioArray.count - isLike.accept(self.journal.haveCollect ?? false) - - let journalArray = [JournalItem.journaItem(model: self.journal)] - - let section = [JournalSection.audio(header: self.journal, items: audioArray), - JournalSection.journal(header: self.journal, items: journalArray)] + input.viewDidLoad.subscribe { (_) in + guard let journalNo = self.journal.journalNo, let journalID = self.journal.id else { return } - - elements.accept(section) + // 追踪接口返回的数据 + var audioSection: JournalSection? + var journalSection: JournalSection? - - } onError: { error in - - }.disposed(by: self.rx.disposeBag) + // 更新elements的帮助方法 + let updateElements = { + var newSections: [JournalSection] = [] + if let audio = audioSection { + newSections.append(audio) // 确保音乐数据总是第一个 + } + if let journal = journalSection { + newSections.append(journal) + } + elements.accept(newSections) + } - }.disposed(by: rx.disposeBag) - + // 处理音乐请求 + self.requestMusic(journalNo: journalNo) + .subscribe(onNext: { audioTrackArray in + let audioArray = audioTrackArray.map { JournalItem.audioItem(model: $0) } + self.journal.trackCount = audioArray.count + isLike.accept(self.journal.haveCollect ?? false) + audioSection = JournalSection.audio(header: self.journal, items: audioArray) + updateElements() + }, onError: { error in + // 处理错误 + }).disposed(by: self.rx.disposeBag) + // 处理期刊推荐请求 + self.requestJournalRecommend(journalID: journalID) + .subscribe(onNext: { journals in + let journalArray = journals.map { JournalItem.journaItem(model: $0) } + journalSection = JournalSection.journal(header: nil, items: journalArray) + updateElements() + }, onError: { error in + // 处理错误 + }).disposed(by: self.rx.disposeBag) + }.disposed(by: rx.disposeBag) @@ -266,4 +285,11 @@ class JournalDetailViewModel: ViewModel, ViewModelType { .trackError(error) } + + + func requestJournalRecommend(journalID: String) -> Observable<[Journal]> { + self.provider.journalRecommend(journalID: journalID) + .trackActivity(loading) + .trackError(error) + } } diff --git a/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewController.swift b/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewController.swift index 2dc6547..a393a25 100644 --- a/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewController.swift @@ -11,28 +11,43 @@ import RxCocoa import RxDataSources class MusicStyleViewController: ViewController { - let collectionView: UICollectionView = { - let layout = UICollectionViewFlowLayout() - layout.minimumInteritemSpacing = 15 - layout.minimumLineSpacing = 24 - - layout.sectionInset = UIEdgeInsets.init(top: 0, left: 18, bottom: 0, right: 18) + lazy var collectionView: UICollectionView = { - layout.itemSize = CGSize(width: (BaseDimensions.screenWidth - 18 * 2 - 15) / 2, height: 147) + let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in + let viewModel = self.viewModel as? JournalDetailViewModel + let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147)) + let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize) + + let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2) + doubleColumnGroup.interItemSpacing = .fixed(15) + doubleColumnGroup.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18) + + let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup) + +// doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18) + + + let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(viewModel?.headerHeight ?? 100)) + let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top) + + doubleColumnSection.boundarySupplementaryItems = [header] + + return doubleColumnSection + + } let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout) - collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell") - + collectionView.register(MusicStyleHeaderView.self, forSupplementaryViewOfKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "MusicStyleHeaderView") - if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { - layout.headerReferenceSize = CGSize(width: collectionView.frame.width, height: 360) // 设置 header 的高度 - } - + collectionView.backgroundColor = .white + + + return collectionView }() - + lazy var headerView: MusicStyleHeaderView = { let headerView = MusicStyleHeaderView.init() @@ -78,13 +93,11 @@ class MusicStyleViewController: ViewController { switch refreshType.element { case .refresh: self.headerRefreshTrigger.onNext(()) - case .loadMore where AuthManager.shared.token?.isValid == true: + case .loadMore: self.footerRefreshTrigger.onNext(()) default: break } - - }.disposed(by: rx.disposeBag) @@ -106,7 +119,35 @@ class MusicStyleViewController: ViewController { let output = viewModel.transform(input: input) - let dataSource = MusicStyleViewController.dataSource() + let dataSource = RxCollectionViewSectionedReloadDataSource( + configureCell: { dataSource, collectionView, indexPath, item in + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell + cell.journal = item + return cell + }, + configureSupplementaryView: { dataSource, collectionView, kind, indexPath in + guard kind == UICollectionView.elementKindSectionHeader else { + return UICollectionReusableView() + } + let section = dataSource[indexPath.section] + let resuableView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "MusicStyleHeaderView", for: indexPath) as! MusicStyleHeaderView + + + resuableView.searchCategory = section.header + + resuableView.dropButtonTapObservable.subscribe { _ in + + viewModel.isExpand.accept(!resuableView.isExpand) + viewModel.updateHeaderHeight(newHeight: viewModel.isExpand.value ? 506 + BaseDimensions.topHeight : 400 + BaseDimensions.topHeight) + + }.disposed(by: self.rx.disposeBag) + + + + + return resuableView + } + ) output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag) @@ -122,18 +163,18 @@ class MusicStyleViewController: ViewController { // _ = self.headerView.dropButton.rx.isSelected <-> viewModel.isExpand - viewModel.isExpand - .bind(to: self.headerView.dropButton.rx.isSelected) - .disposed(by: rx.disposeBag) - - viewModel.isExpand.subscribe { [weak self] isExpand in - - print("dropButtonTrigger \(isExpand)") - self?.headerView.isExpand = isExpand - self?.updateHeader() - - } .disposed(by: rx.disposeBag) - +// viewModel.isExpand +// .bind(to: self.headerView.dropButton.rx.isSelected) +// .disposed(by: rx.disposeBag) +// +// viewModel.isExpand.subscribe { [weak self] isExpand in +// +// print("dropButtonTrigger \(isExpand)") +// self?.headerView.isExpand = isExpand +// self?.updateHeader() +// +// } .disposed(by: rx.disposeBag) +// } @@ -163,44 +204,6 @@ class MusicStyleViewController: ViewController { -} - -extension MusicStyleViewController: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - // 根据 section 或其他条件计算 header 的高度 - let width = collectionView.bounds.width - headerView.frame = CGRect(x: 0, y: 0, width: width, height: 1000) - headerView.layoutIfNeeded() - let size = headerView.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height), - withHorizontalFittingPriority: .required, - verticalFittingPriority: .fittingSizeLevel) - - return CGSize(width: collectionView.bounds.width, height: size.height) - } - - - static func dataSource() -> RxCollectionViewSectionedReloadDataSource { - return RxCollectionViewSectionedReloadDataSource( - configureCell: { dataSource, collectionView, indexPath, item in - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell - cell.journal = item - return cell - }, - configureSupplementaryView: { dataSource, collectionView, kind, indexPath in - guard kind == UICollectionView.elementKindSectionHeader else { - return UICollectionReusableView() - } - let section = dataSource[indexPath.section] - let resuableView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "MusicStyleHeaderView", for: indexPath) as! MusicStyleHeaderView - - - resuableView.searchCategory = section.header - - return resuableView - } - ) - } - } @@ -252,10 +255,12 @@ class MusicStyleHeaderView: UICollectionReusableView { var dropButton: UIButton = { - let dropButton = UIButton.init() + let dropButton = LayoutableButton.init() + dropButton.imageHorizontalAlignment = .right dropButton.setTitle("展开", for: .normal) dropButton.setTitle("收起", for: .selected) - dropButton.setTitleColor(.init(hex: 0x000000, alpha: 0.6), for: .normal) + dropButton.setTitleColor(.secondaryText(), for: .normal) + dropButton.titleLabel?.font = UIFont.systemFont(ofSize: 15) dropButton.setImage(UIImage.init(named: "journal_drop_down"), for: .normal) dropButton.setImage(UIImage.init(named: "journal_drop_up"), for: .selected) @@ -265,36 +270,38 @@ class MusicStyleHeaderView: UICollectionReusableView { - var gradientLayer: CAGradientLayer = { - let gradientLayer = CAGradientLayer.init() - gradientLayer.colors = [UIColor.init(hex: 0xFFFFFF, alpha: 0).cgColor, - UIColor.init(hex: 0xFFFFFF, alpha: 0.8).cgColor] - gradientLayer.locations = [0.0, 0.9] + var gradientLayerView: GradientLayerView = { + let gradientLayerView = GradientLayerView.init(colors: [UIColor.init(hex: 0xFFFFFF, alpha: 0).cgColor, + UIColor.init(hex: 0xFFFFFF, alpha: 0.8).cgColor], frame: .zero) +// gradientLayerView.backgroundColor = .red - gradientLayer.startPoint = CGPoint.init(x: 0 , y: 0) - gradientLayer.endPoint = CGPoint.init(x: 0, y: 1) - - return gradientLayer + return gradientLayerView }() var isExpand = false { didSet { - switch isExpand { case true: dropButton.isSelected = true contentLabel.numberOfLines = 0 - gradientLayer.isHidden = true + + gradientLayerView.isHidden = true case false: dropButton.isSelected = false contentLabel.numberOfLines = 4 - gradientLayer.isHidden = false + + gradientLayerView.isHidden = false } } } + + + var dropButtonTapObservable: Observable { + return dropButton.rx.tap.asObservable() + } var searchCategory: SearchCategory? { didSet { @@ -308,6 +315,9 @@ class MusicStyleHeaderView: UICollectionReusableView { contentLabel.text = searchCategory.description + + self.isExpand = searchCategory.isExpand ?? false + } } @@ -316,6 +326,8 @@ class MusicStyleHeaderView: UICollectionReusableView { super.init(frame: frame) makeUI() + + backgroundColor = .red } required init?(coder: NSCoder) { @@ -329,13 +341,9 @@ class MusicStyleHeaderView: UICollectionReusableView { containerView.addSubview(styleLabel) containerView.addSubview(subStyleLabel) containerView.addSubview(contentLabel) + containerView.addSubview(gradientLayerView) containerView.addSubview(dropButton) - } - - override func layoutSubviews() { - super.layoutSubviews() - titleImageView.snp.makeConstraints { make in make.left.equalTo(self) @@ -361,22 +369,33 @@ class MusicStyleHeaderView: UICollectionReusableView { make.centerY.equalTo(styleLabel) } - contentLabel.snp.makeConstraints { make in + contentLabel.snp.remakeConstraints { make in make.left.equalTo(containerView).offset(18) make.right.equalTo(containerView).offset(-18) make.top.equalTo(styleLabel.snp.bottom).offset(15) } + gradientLayerView.snp.remakeConstraints { make in + make.edges.equalTo(contentLabel) + } + dropButton.snp.remakeConstraints { make in make.left.equalTo(containerView).offset(18) make.top.equalTo(contentLabel.snp.bottom).offset(16) make.bottom.equalTo(containerView.snp.bottom).offset(-35) } + - gradientLayer.frame = self.contentLabel.bounds + } + + override func layoutSubviews() { + super.layoutSubviews() + + + } } diff --git a/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewModel.swift b/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewModel.swift index 4afcce7..0762ee2 100644 --- a/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Search/MusicStyleViewModel.swift @@ -25,10 +25,10 @@ class MusicStyleViewModel: ViewModel, ViewModelType { } let itemSelected = PublishSubject() - + + var headerHeight: CGFloat = 506 + BaseDimensions.topHeight let isExpand = BehaviorRelay.init(value: false) - - + var searchCategory: SearchCategory? init(searchCategory: SearchCategory?, provider: IndieMusicAPI) { self.searchCategory = searchCategory @@ -89,6 +89,21 @@ class MusicStyleViewModel: ViewModel, ViewModelType { // self.itemSelected.onNext(sectionItem) // }.disposed(by: rx.disposeBag) + isExpand.subscribe { isExpand in + var new = elements.value + if var header = new.first?.header { + header.isExpand = isExpand.element + + if let items = new.first?.items { + new[0] = .init(header: header, items: items) + elements.accept(new) + } + } + + }.disposed(by: rx.disposeBag) + + + let journal = PublishSubject.init() @@ -108,4 +123,9 @@ class MusicStyleViewModel: ViewModel, ViewModelType { .trackActivity(loading) .trackError(error) } + + + func updateHeaderHeight(newHeight: CGFloat) { + headerHeight = newHeight + } } diff --git a/IndieMusic/IndieMusic/Modules/Search/SearchResultsController.swift b/IndieMusic/IndieMusic/Modules/Search/SearchResultsController.swift index d69a149..7e5e846 100644 --- a/IndieMusic/IndieMusic/Modules/Search/SearchResultsController.swift +++ b/IndieMusic/IndieMusic/Modules/Search/SearchResultsController.swift @@ -103,10 +103,12 @@ class SearchResultsController: ViewController { guard let viewModel = viewModel as? SearchResultsViewModel else { return } + + let input = SearchResultsViewModel.Input.init(viewWillAppear: rx.viewWillAppear, closeButtonTrigger: self.searchTopBar.cancelButton.rx.tap.asDriver(), searchText: searchTopBar.searchControl.textField.rx.text.asDriver(), - itemSelected: collectionView.rx.modelSelected(SearchResultsItem.self).asDriver(), + modelSelected: collectionView.rx.modelSelected(SearchResultsItem.self).asDriver(), searchType: searchType) @@ -126,6 +128,30 @@ class SearchResultsController: ViewController { }.disposed(by: rx.disposeBag) + + output.modelSelected.drive { [weak self] searchResultsItem in + + switch searchResultsItem { + case .single(let audioTrack): + + let playerViewModel = PlayerViewModel.init(track: audioTrack, provider: viewModel.provider) + self?.navigator.show(segue: .player(viewModel: playerViewModel), sender: self, transition: .modal) + + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + AudioManager.sharedInstance.setPlaylist(list: [audioTrack]) + AudioManager.sharedInstance.playTrack(track: audioTrack) + } + + case .journal(let journal): + let journalDetailViewModel = JournalDetailViewModel.init(journal: journal, provider: viewModel.provider) + + self?.navigator.show(segue: .journalDetail(viewModel: journalDetailViewModel), sender: self) + + } + + }.disposed(by: rx.disposeBag) + } @@ -190,29 +216,32 @@ extension SearchResultsController { func createSingleColumnSection() -> NSCollectionLayoutSection { let singleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)) let singleColumnItem = NSCollectionLayoutItem(layoutSize: singleColumnItemSize) - + let singleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: singleColumnItemSize, subitems: [singleColumnItem]) - + let singleColumnSection = NSCollectionLayoutSection(group: singleColumnGroup) return singleColumnSection } - + func createDoubleColumnSection() -> NSCollectionLayoutSection { let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147)) let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize) -// doubleColumnItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 7.5, bottom: 0, trailing: 7.5) - + let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2) - doubleColumnGroup.interItemSpacing = .fixed(10) - + doubleColumnGroup.interItemSpacing = .fixed(15) + doubleColumnGroup.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18) + let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup) - - doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18) - + + return doubleColumnSection + } } + + + diff --git a/IndieMusic/IndieMusic/Modules/Search/SearchResultsViewModel.swift b/IndieMusic/IndieMusic/Modules/Search/SearchResultsViewModel.swift index b267fc9..914ba55 100644 --- a/IndieMusic/IndieMusic/Modules/Search/SearchResultsViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Search/SearchResultsViewModel.swift @@ -16,7 +16,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType { let viewWillAppear: ControlEvent let closeButtonTrigger: Driver let searchText: Driver - let itemSelected: Driver + let modelSelected: Driver let searchType: BehaviorRelay } @@ -24,7 +24,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType { struct Output { let items: BehaviorRelay<[SearchResultsSection]> - let itemSelected: PublishSubject + let modelSelected: Driver } let items = BehaviorRelay<[SearchResultsSection]>.init(value: []) @@ -42,7 +42,9 @@ class SearchResultsViewModel: ViewModel, ViewModelType { }.disposed(by: rx.disposeBag) - input.searchText.drive { searchText in + + input.searchText.debounce(.milliseconds(500)) + .drive { searchText in self.fetchSearchData(keyword: searchText ?? "") .subscribe { searchResults in @@ -68,6 +70,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType { input.searchType.subscribe { searchType in + self.searchType.accept(searchType.element ?? .audio) switch searchType.element { case .audio: @@ -88,31 +91,18 @@ class SearchResultsViewModel: ViewModel, ViewModelType { self.items.accept([SearchResultsSection.single(title: "", items: self.audioTrackItems.value)]) case .journal: self.items.accept([SearchResultsSection.journal(title: "", items: self.journalItems.value)]) - default: break } }.disposed(by: rx.disposeBag) - -// -// -// -// let musicStyleItem = MusicStyle.init(title: "测试", subTitle: "123", backgroundImage: "") -// let musicStyleSection = MusicStyleSection.init(items: [musicStyleItem, musicStyleItem, musicStyleItem, musicStyleItem], header: "后摇", headerSub: "Post Rock", content: "总在不经意间获得简单朴素且乐趣其中的感怀,这种感怀的妙处在于它没有试图去提炼出任何的真理,他就像我们恬然的谈话里总夹杂着“那我懂你的意思了”,但是否是真的明白,却不然得知。即", isExpand: false) -// -// -// collectionViewItemstems.accept([musicStyleSection]) -// - - input.itemSelected.drive { indexPath in -// guard let sectionItem = self.collectionViewItemstems.value.first?.items[indexPath.row] else { return } -// self.collectionViewSelection.onNext(sectionItem) + input.modelSelected.drive { searchResultsItem in + }.disposed(by: rx.disposeBag) return Output.init(items: items, - itemSelected: itemSelected) + modelSelected: input.modelSelected) } @@ -121,5 +111,5 @@ class SearchResultsViewModel: ViewModel, ViewModelType { .trackActivity(loading) .trackError(error) } - + } diff --git a/IndieMusic/IndieMusic/Modules/Setting/SettingViewMdel.swift b/IndieMusic/IndieMusic/Modules/Setting/SettingViewMdel.swift index 67536f3..2a1bf25 100644 --- a/IndieMusic/IndieMusic/Modules/Setting/SettingViewMdel.swift +++ b/IndieMusic/IndieMusic/Modules/Setting/SettingViewMdel.swift @@ -39,7 +39,7 @@ class SettingViewMdel: ViewModel, ViewModelType { let timing = Setting.init(title: "定时关闭", detail: "", arrowIcon: "setting_arrow") - let cache = Setting.init(title: "清理缓存", detail: "201.4MB", arrowIcon: "setting_arrow") + let cache = Setting.init(title: "清理缓存", detail: "0.00MB", arrowIcon: "setting_arrow") let permission = Setting.init(title: "个人信息与权限", detail: "", arrowIcon: "setting_arrow") diff --git a/IndieMusic/IndieMusic/Networking/Api.swift b/IndieMusic/IndieMusic/Networking/Api.swift index eaae48a..cd22e6e 100644 --- a/IndieMusic/IndieMusic/Networking/Api.swift +++ b/IndieMusic/IndieMusic/Networking/Api.swift @@ -33,7 +33,7 @@ protocol IndieMusicAPI { /// 期刊列表 - func journalList(categoryId: String, journalNoRange: String?, pageNum: Int, pageSize: Int) -> Single<[Journal]> + func journalList(categoryId: String?, journalNoRange: String?, pageNum: Int, pageSize: Int) -> Single<[Journal]> /// 期刊音乐 func journalMusic(journalNo: String) -> Single<[AudioTrack]> @@ -79,6 +79,9 @@ protocol IndieMusicAPI { func searchCategory() -> Single<[SearchCategory]> /// 搜索 func serach(keyword: String) -> Single + + /// 随机播放 + func randomAudioTrack(limit: Int) -> Single<[AudioTrack]> } diff --git a/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift b/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift index 08d0e24..e1f1d33 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/APIConfig.swift @@ -51,6 +51,8 @@ enum APIConfig { case searchCategory case serach(String) + case randomAudioTrack(Int) + } extension APIConfig: TargetType { @@ -124,13 +126,16 @@ extension APIConfig: TargetType { return "luoo-music/search/category" case .serach(let keyword): return "luoo-music/search/fuzzy/\(keyword)" + + case .randomAudioTrack(let limit): + return "luoo-music/song/random/\(limit)" } } var method: Moya.Method { switch self { - case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach: + case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack: return .get case .sendsms, .login, .autoLogin, .editAvatar, .like, .checkVersion, .logout: return .post @@ -144,7 +149,7 @@ extension APIConfig: TargetType { var parameterEncoding: ParameterEncoding { switch self { - case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .sendsms, .imageCheckCode, .login, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .like, .cancelLike, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach: + case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .sendsms, .imageCheckCode, .login, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .like, .cancelLike, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack: return URLEncoding.default case .autoLogin, .editUserInfo, .editAvatar, .checkVersion, .logout, .commentLike: @@ -156,7 +161,7 @@ extension APIConfig: TargetType { var task: Task { var parameters: [String: Any] = [:] switch self { - case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach: + case .wechatAccessToken, .countryCode, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList, .commentList, .subCommentList, .filterMenu, .journalRecommend, .searchCategory, .serach, .randomAudioTrack: return .requestPlain case .login(let dic), .journalList(let dic), .sendsms(let dic), .autoLogin(let dic), .editUserInfo(let dic), .like(let dic), .cancelLike(let dic), .logout(let dic), .checkVersion(let dic), .commentLike(_, let dic): @@ -185,7 +190,7 @@ extension APIConfig: TargetType { var headers : [String : String]? { switch self { - case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo, .logout, .editAvatar, .commentList, .subCommentList, .commentLike, .filterMenu, .journalRecommend, .serach: + case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo, .logout, .editAvatar, .commentList, .subCommentList, .commentLike, .filterMenu, .journalRecommend, .serach, .randomAudioTrack: return ["Authorization": AuthManager.shared.token?.basicToken ?? ""] default: return nil diff --git a/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift b/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift index c4122a0..65c9a1c 100644 --- a/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift +++ b/IndieMusic/IndieMusic/Networking/Rest/RestApi.swift @@ -145,16 +145,20 @@ extension RestApi { } - func journalList(categoryId: String, journalNoRange: String?, pageNum: Int, pageSize: Int) -> Single<[Journal]> { - var dic = ["categoryId": categoryId, - "pageNum": pageNum, + func journalList(categoryId: String?, journalNoRange: String?, pageNum: Int, pageSize: Int) -> Single<[Journal]> { + var dic = ["pageNum": pageNum, "pageSize": pageSize ] as [String : Any] + if let categoryId = categoryId { + dic["categoryId"] = categoryId + } + if let journalNoRange = journalNoRange { dic["journalNoRange"] = journalNoRange } - + print("筛选错误条件\(dic)") + return requestObject(.journalList(dic), with: "data.rows", type: [Journal].self) } @@ -263,4 +267,8 @@ extension RestApi { } + func randomAudioTrack(limit: Int) -> Single<[AudioTrack]> { + return requestObject(.randomAudioTrack(limit), with: "data", type: [AudioTrack].self) + } + } diff --git a/IndieMusic/Podfile b/IndieMusic/Podfile index 3ae80bb..552a601 100644 --- a/IndieMusic/Podfile +++ b/IndieMusic/Podfile @@ -20,7 +20,7 @@ target 'IndieMusic' do pod 'WechatOpenSDK-XCFramework' pod 'SwiftDate' pod 'DateToolsSwift' - pod 'XHLaunchAd' +# pod 'XHLaunchAd' pod 'DZNEmptyDataSet' pod 'SVProgressHUD',:git => 'https://github.com/Fidetro/SVProgressHUD.git' pod 'AttributedString'