Filter module

dev
wenlei 10 months ago
parent ff497c2c87
commit 8f9479a7dc

@ -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()

@ -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

@ -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)
}
}

@ -72,6 +72,8 @@ struct SearchCategory: Codable {
let nameEn: String?
let image: String?
let description: String?
var isExpand: Bool?
}

@ -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)
}

@ -25,6 +25,15 @@ class FilterViewModel: ViewModel, ViewModelType {
let itemSelected = PublishSubject<Filter>()
let items = BehaviorRelay<[FilterSection]>.init(value: [])
var filter: BehaviorRelay<Filter?> = .init(value: nil)
init(filter: BehaviorRelay<Filter?>, 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):
// itemisSelected
for index in items.indices {
if items[index].name == selectedFilter.name {
// celltrue
items[index].isSelected = true
} else {
// cellfalse
items[index].isSelected = false
}
}
// sectionsection
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):
// itemisSelected
for index in items.indices {
if items[index].name == selectedFilter.name {
// celltrue
items[index].isSelected = true
} else {
// cellfalse
items[index].isSelected = false
}
}
// sectionsection
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<FilterMenu> {
return self.provider.filterMenu()

@ -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)

@ -24,7 +24,10 @@ class HomeViewController: TableViewController {
var currentNavBarBgAlpha: CGFloat = 0
let randomAudioTrackTrigger = PublishRelay<Void>.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))
}

@ -16,6 +16,7 @@ class HomeViewModel: ViewModel, ViewModelType {
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let selection: Driver<IndexPath>
let randomAudioTrackTrigger: PublishRelay<Void>
}
struct Output {
@ -25,7 +26,7 @@ class HomeViewModel: ViewModel, ViewModelType {
let selection: Driver<IndexPath>
let itemSelected: PublishSubject<HomeSectionItem>
let footerState: PublishRelay<RxMJRefreshFooterState>
// let filterTrigger: Driver<Filter>
let randomAudioTrack: PublishRelay<[AudioTrack]>
}
@ -34,7 +35,10 @@ class HomeViewModel: ViewModel, ViewModelType {
let categoryId = BehaviorRelay<String>.init(value: "")
let journalNoRange = BehaviorRelay<String?>.init(value: nil)
let randomAudioTrack = PublishRelay<[AudioTrack]>.init()
let filter = BehaviorRelay<Filter?>.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)
}
}

@ -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(),

@ -13,6 +13,7 @@ class JournalDetailViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let viewDidLoad: Observable<Void>
let selection: Driver<IndexPath>
let notiPlayAudioTrack: Observable<Notification>
@ -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)
}
}

@ -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<MusicStyleSection>(
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<MusicStyleSection> {
return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
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<Void> {
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()
}
}

@ -25,10 +25,10 @@ class MusicStyleViewModel: ViewModel, ViewModelType {
}
let itemSelected = PublishSubject<MusicStyle>()
var headerHeight: CGFloat = 506 + BaseDimensions.topHeight
let isExpand = BehaviorRelay<Bool>.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<Journal>.init()
@ -108,4 +123,9 @@ class MusicStyleViewModel: ViewModel, ViewModelType {
.trackActivity(loading)
.trackError(error)
}
func updateHeaderHeight(newHeight: CGFloat) {
headerHeight = newHeight
}
}

@ -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
}
}

@ -16,7 +16,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
let viewWillAppear: ControlEvent<Bool>
let closeButtonTrigger: Driver<Void>
let searchText: Driver<String?>
let itemSelected: Driver<SearchResultsItem>
let modelSelected: Driver<SearchResultsItem>
let searchType: BehaviorRelay<SearchType>
}
@ -24,7 +24,7 @@ class SearchResultsViewModel: ViewModel, ViewModelType {
struct Output {
let items: BehaviorRelay<[SearchResultsSection]>
let itemSelected: PublishSubject<SearchResultsItem>
let modelSelected: Driver<SearchResultsItem>
}
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)
}
}

@ -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")

@ -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<SearchResults>
///
func randomAudioTrack(limit: Int) -> Single<[AudioTrack]>
}

@ -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

@ -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)
}
}

@ -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'

Loading…
Cancel
Save