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