parent
1062a4c0e3
commit
d14bb84a28
@ -0,0 +1,162 @@
|
||||
//
|
||||
// AlertViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import UIKit
|
||||
|
||||
class AlertViewController: UIViewController {
|
||||
|
||||
let containerView: UIView = {
|
||||
let containerView = UIView.init()
|
||||
containerView.backgroundColor = .white
|
||||
containerView.layer.cornerRadius = 12
|
||||
|
||||
return containerView
|
||||
}()
|
||||
let bottomMenuView: UIView = {
|
||||
let bottomMenuView = UIView.init()
|
||||
|
||||
|
||||
return bottomMenuView
|
||||
}()
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let titleLabel = UILabel.init()
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
|
||||
titleLabel.textColor = .init(hex: 0x323233)
|
||||
|
||||
|
||||
return titleLabel
|
||||
}()
|
||||
|
||||
let detailTextView: UITextView = {
|
||||
let detailTextView = UITextView.init()
|
||||
detailTextView.textAlignment = .center
|
||||
detailTextView.font = UIFont.systemFont(ofSize: 12)
|
||||
detailTextView.textColor = .init(hex: 0x323233)
|
||||
detailTextView.tintColor = UIColor.primaryText()
|
||||
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.alignment = .center
|
||||
paragraphStyle.lineSpacing = 5
|
||||
|
||||
|
||||
return detailTextView
|
||||
}()
|
||||
|
||||
|
||||
let cancelButton: UIButton = {
|
||||
let cancelButton = UIButton.init()
|
||||
cancelButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
|
||||
cancelButton.setTitle("取消", for: .normal)
|
||||
cancelButton.setTitleColor(UIColor.tertiaryText(), for: .normal)
|
||||
cancelButton.addTarget(AlertViewController.self, action: #selector(cancelButtonClicked), for: .touchUpInside)
|
||||
|
||||
return cancelButton
|
||||
}()
|
||||
|
||||
let confirmButton: UIButton = {
|
||||
let confirmButton = UIButton.init()
|
||||
confirmButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
|
||||
confirmButton.setTitle("确定", for: .normal)
|
||||
confirmButton.setTitleColor(UIColor.primaryText(), for: .normal)
|
||||
confirmButton.addTarget(AlertViewController.self, action: #selector(confirmButtonClick), for: .touchUpInside)
|
||||
|
||||
return confirmButton
|
||||
}()
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
|
||||
|
||||
func makeUI() {
|
||||
|
||||
view.addSubview(containerView)
|
||||
containerView.addSubview(titleLabel)
|
||||
containerView.addSubview(detailTextView)
|
||||
|
||||
containerView.addSubview(bottomMenuView)
|
||||
bottomMenuView.addSubview(cancelButton)
|
||||
bottomMenuView.addSubview(confirmButton)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
|
||||
containerView.snp.makeConstraints { make in
|
||||
make.size.equalTo(CGSize.init(width: 311, height: 174))
|
||||
make.center.equalTo(view)
|
||||
}
|
||||
|
||||
bottomMenuView.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView)
|
||||
make.right.equalTo(containerView)
|
||||
make.bottom.equalTo(containerView)
|
||||
make.height.equalTo(48)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(containerView).offset(26)
|
||||
make.left.equalTo(containerView).offset(24)
|
||||
make.right.equalTo(containerView).offset(-24)
|
||||
}
|
||||
|
||||
detailTextView.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView).offset(24)
|
||||
make.right.equalTo(containerView).offset(-24)
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(12)
|
||||
make.height.equalTo(40)
|
||||
}
|
||||
|
||||
cancelButton.snp.makeConstraints { make in
|
||||
make.left.equalTo(bottomMenuView)
|
||||
make.right.equalTo(bottomMenuView.snp.centerX)
|
||||
make.top.equalTo(bottomMenuView)
|
||||
make.bottom.equalTo(bottomMenuView)
|
||||
}
|
||||
|
||||
|
||||
confirmButton.snp.makeConstraints { make in
|
||||
make.right.equalTo(bottomMenuView)
|
||||
make.left.equalTo(bottomMenuView.snp.centerX)
|
||||
make.top.equalTo(bottomMenuView)
|
||||
make.bottom.equalTo(bottomMenuView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc func cancelButtonClicked() {
|
||||
|
||||
}
|
||||
|
||||
@objc func confirmButtonClick() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension AlertViewController: UIViewControllerTransitioningDelegate {
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
|
||||
|
||||
let carePresentationVC = CardPresentationController.init(presentedViewController: presented, presenting: presenting)
|
||||
carePresentationVC.viewType = .tips(false)
|
||||
|
||||
return carePresentationVC
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
//
|
||||
// BadgeButton.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BadgeButton: UIButton {
|
||||
|
||||
/*
|
||||
// Only override draw() if you perform custom drawing.
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
override func draw(_ rect: CGRect) {
|
||||
// Drawing code
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
//
|
||||
// BorderLabel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BorderLabel: UILabel {
|
||||
|
||||
|
||||
var circularFillet = false
|
||||
|
||||
var padding = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
|
||||
override func drawText(in rect: CGRect) {
|
||||
super.drawText(in: rect.inset(by: padding))
|
||||
}
|
||||
|
||||
|
||||
init(padding: UIEdgeInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20), frame: CGRect = CGRect.zero) {
|
||||
self.padding = padding
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override var intrinsicContentSize : CGSize {
|
||||
let superContentSize = super.intrinsicContentSize
|
||||
let width = superContentSize.width + padding.left + padding.right
|
||||
let heigth = superContentSize.height + padding.top + padding.bottom
|
||||
return CGSize(width: width, height: heigth)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if circularFillet == true {
|
||||
let h = self.layer.frame.size.height
|
||||
self.layer.cornerRadius = h / 2
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Comment.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxDataSources
|
||||
|
||||
struct Comment {
|
||||
let avator: String
|
||||
let audioTrackImage: String
|
||||
let name: String
|
||||
let date: Int
|
||||
let comment: String
|
||||
let myComment: String
|
||||
}
|
||||
|
||||
|
||||
struct CommentSection {
|
||||
var items: [Comment]
|
||||
}
|
||||
|
||||
extension CommentSection: SectionModelType {
|
||||
typealias Item = Comment
|
||||
|
||||
init(original: CommentSection, items: [Item]) {
|
||||
self = original
|
||||
self.items = items
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Following.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxDataSources
|
||||
|
||||
struct Following {
|
||||
let avator: String
|
||||
let name: String
|
||||
let detail: String
|
||||
let isFollowing: Bool
|
||||
}
|
||||
|
||||
struct FollowingSection {
|
||||
var items: [Following]
|
||||
}
|
||||
|
||||
extension FollowingSection: SectionModelType {
|
||||
typealias Item = Following
|
||||
|
||||
init(original: FollowingSection, items: [Item]) {
|
||||
self = original
|
||||
self.items = items
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Like.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxDataSources
|
||||
|
||||
struct Like {
|
||||
let avator: String
|
||||
let name: String
|
||||
let content: String
|
||||
let date: Int
|
||||
}
|
||||
|
||||
struct LikeSection {
|
||||
var items: [Like]
|
||||
}
|
||||
|
||||
extension LikeSection: SectionModelType {
|
||||
typealias Item = Like
|
||||
|
||||
init(original: LikeSection, items: [Item]) {
|
||||
self = original
|
||||
self.items = items
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
//
|
||||
// Message.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxDataSources
|
||||
|
||||
struct Message: Codable {
|
||||
let icon: String
|
||||
let title: String
|
||||
let detail: String
|
||||
let isShowBadge: Bool
|
||||
let date: Int
|
||||
}
|
||||
|
||||
struct MessageSection {
|
||||
var items: [Message]
|
||||
}
|
||||
|
||||
extension MessageSection: SectionModelType {
|
||||
typealias Item = Message
|
||||
|
||||
init(original: MessageSection, items: [Item]) {
|
||||
self = original
|
||||
self.items = items
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Timing.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxDataSources
|
||||
|
||||
struct Timing: Codable {
|
||||
let title: String
|
||||
}
|
||||
|
||||
|
||||
struct TimingSection {
|
||||
var items: [Timing]
|
||||
}
|
||||
|
||||
extension TimingSection: SectionModelType {
|
||||
typealias Item = Timing
|
||||
|
||||
init(original: TimingSection, items: [Item]) {
|
||||
self = original
|
||||
self.items = items
|
||||
}
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
//
|
||||
// CommentListViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
class CommentListViewController: TableViewController {
|
||||
let noDataView: MineSingleNoDataView = {
|
||||
let noDataView = MineSingleNoDataView.init()
|
||||
noDataView.isHidden = true
|
||||
|
||||
return noDataView
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
override func makeUI() {
|
||||
super.makeUI()
|
||||
view.backgroundColor = .init(hex: 0xf5f5f5)
|
||||
|
||||
tableView.mj_header = nil
|
||||
tableView.mj_footer = nil
|
||||
|
||||
tableView.register(CommentListViewCell.self, forCellReuseIdentifier: "CommentListViewCell")
|
||||
|
||||
view.addSubview(noDataView)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override func bindViewModel() {
|
||||
super.bindViewModel()
|
||||
|
||||
guard let viewModel = viewModel as? CommentListViewModel else { return }
|
||||
|
||||
let input = CommentListViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
|
||||
|
||||
let output = viewModel.transform(input: input)
|
||||
|
||||
let dataSource = CommentListViewController.dataSource { cell, like in
|
||||
|
||||
}
|
||||
|
||||
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
noDataView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(view)
|
||||
make.height.equalTo(200)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension CommentListViewController {
|
||||
//TODO
|
||||
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, Like) -> Void) -> RxTableViewSectionedReloadDataSource<CommentSection> {
|
||||
return RxTableViewSectionedReloadDataSource<CommentSection>(
|
||||
configureCell: { dataSource, tableView, indexPath, item in
|
||||
let cell: CommentListViewCell = tableView.dequeueReusableCell(withIdentifier: "CommentListViewCell", for: indexPath) as! CommentListViewCell
|
||||
|
||||
|
||||
cell.comment = item
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CommentListViewCell: UITableViewCell {
|
||||
let avatoreView: UIImageView = {
|
||||
let avatoreView = UIImageView.init()
|
||||
avatoreView.layer.cornerRadius = 24
|
||||
avatoreView.layer.masksToBounds = true
|
||||
|
||||
avatoreView.backgroundColor = .red
|
||||
|
||||
return avatoreView
|
||||
}()
|
||||
|
||||
let audioTrackView: UIImageView = {
|
||||
let audioTrackView = UIImageView.init()
|
||||
audioTrackView.layer.cornerRadius = 3
|
||||
audioTrackView.layer.masksToBounds = true
|
||||
audioTrackView.backgroundColor = .red
|
||||
|
||||
return audioTrackView
|
||||
}()
|
||||
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let nameLabel = UILabel.init()
|
||||
nameLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
|
||||
nameLabel.textColor = .primaryText()
|
||||
|
||||
return nameLabel
|
||||
}()
|
||||
|
||||
let tipsLabel: UILabel = {
|
||||
let tipsLabel = UILabel.init()
|
||||
tipsLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
tipsLabel.textColor = .secondaryText()
|
||||
tipsLabel.text = "回复你的评论"
|
||||
tipsLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
tipsLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
|
||||
return tipsLabel
|
||||
}()
|
||||
|
||||
let dateLabel: UILabel = {
|
||||
let dateLabel = UILabel.init()
|
||||
dateLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
dateLabel.textColor = .tertiaryText()
|
||||
|
||||
return dateLabel
|
||||
}()
|
||||
|
||||
let commentLabel: UILabel = {
|
||||
let commentLabel = UILabel.init()
|
||||
commentLabel.font = UIFont.systemFont(ofSize: 14)
|
||||
commentLabel.textColor = .secondaryText()
|
||||
commentLabel.numberOfLines = 0
|
||||
|
||||
return commentLabel
|
||||
}()
|
||||
|
||||
let lineView: UIView = {
|
||||
let lineView = UIView.init()
|
||||
lineView.backgroundColor = .init(hex: 0x787880, alpha: 0.4)
|
||||
return lineView
|
||||
}()
|
||||
|
||||
let myCommentLabel: UILabel = {
|
||||
let myCommentLabel = UILabel.init()
|
||||
myCommentLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
myCommentLabel.textColor = .secondaryText()
|
||||
|
||||
return myCommentLabel
|
||||
}()
|
||||
|
||||
|
||||
let commentButton: UIButton = {
|
||||
let commentButton = UIButton.init()
|
||||
commentButton.setTitle("回复", for: .normal)
|
||||
commentButton.titleLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||
commentButton.setTitleColor(.primary(), for: .normal)
|
||||
|
||||
return commentButton
|
||||
}()
|
||||
|
||||
|
||||
var buttonTapCallback: ((Following) -> ())?
|
||||
|
||||
|
||||
var comment: Comment? {
|
||||
didSet {
|
||||
guard let comment = comment else { return }
|
||||
avatoreView.kf.setImage(with: URL.init(string: comment.avator))
|
||||
audioTrackView.kf.setImage(with: URL.init(string: comment.audioTrackImage))
|
||||
|
||||
nameLabel.text = comment.name
|
||||
dateLabel.text = "\(comment.date)"
|
||||
commentLabel.text = comment.comment
|
||||
myCommentLabel.text = "我的评论:\(comment.myComment)"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
contentView.addSubview(avatoreView)
|
||||
contentView.addSubview(audioTrackView)
|
||||
contentView.addSubview(nameLabel)
|
||||
contentView.addSubview(tipsLabel)
|
||||
contentView.addSubview(dateLabel)
|
||||
contentView.addSubview(commentLabel)
|
||||
contentView.addSubview(lineView)
|
||||
contentView.addSubview(myCommentLabel)
|
||||
contentView.addSubview(commentButton)
|
||||
|
||||
avatoreView.snp.makeConstraints { make in
|
||||
make.left.equalTo(contentView).offset(18)
|
||||
make.top.equalTo(contentView).offset(20)
|
||||
make.size.equalTo(CGSize.init(width: 48, height: 48))
|
||||
}
|
||||
|
||||
audioTrackView.snp.makeConstraints { make in
|
||||
make.right.equalTo(contentView).offset(-18)
|
||||
make.top.equalTo(contentView).offset(23)
|
||||
make.size.equalTo(CGSize.init(width: 48, height: 48))
|
||||
}
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.top.equalTo(avatoreView).offset(3)
|
||||
make.right.equalTo(audioTrackView.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
tipsLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(3)
|
||||
}
|
||||
|
||||
dateLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(tipsLabel.snp.right).offset(6)
|
||||
make.centerY.equalTo(tipsLabel)
|
||||
make.right.equalTo(audioTrackView.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
commentLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.right.equalTo(audioTrackView.snp.left).offset(-15)
|
||||
make.top.equalTo(tipsLabel.snp.bottom).offset(6)
|
||||
}
|
||||
|
||||
lineView.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.width.equalTo(3)
|
||||
make.height.equalTo(15)
|
||||
make.top.equalTo(commentLabel.snp.bottom).offset(14)
|
||||
}
|
||||
|
||||
|
||||
myCommentLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(lineView.snp.right).offset(6)
|
||||
make.top.equalTo(lineView).offset(3)
|
||||
make.right.equalTo(audioTrackView.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
commentButton.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.top.equalTo(myCommentLabel.snp.bottom).offset(18)
|
||||
make.bottom.equalTo(contentView).offset(-12)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// CommentListViewModel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
|
||||
class CommentListViewModel: ViewModel, ViewModelType {
|
||||
|
||||
struct Input {
|
||||
let viewWillAppear: ControlEvent<Bool>
|
||||
|
||||
}
|
||||
|
||||
struct Output {
|
||||
let items: BehaviorRelay<[CommentSection]>
|
||||
}
|
||||
|
||||
let items = BehaviorRelay<[CommentSection]>.init(value: [])
|
||||
|
||||
func transform(input: Input) -> Output {
|
||||
|
||||
input.viewWillAppear.subscribe { (_) in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
let name = Comment.init(avator: "", audioTrackImage: "", name: "123", date: 1000, comment: "这就是生活,也是理想,也是你我的未来", myComment: "太多了,还有草东我也很")
|
||||
|
||||
|
||||
items.accept([CommentSection.init(items: [name, name, name, name])])
|
||||
|
||||
return Output.init(items: items)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,205 @@
|
||||
//
|
||||
// FollowersViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
class FollowersViewController: ViewController, UIScrollViewDelegate {
|
||||
|
||||
let followersTopView: FollowersTopView = {
|
||||
let followersTopView = FollowersTopView.init()
|
||||
|
||||
return followersTopView
|
||||
}()
|
||||
|
||||
var tableView: UITableView = {
|
||||
let tableView = UITableView.init()
|
||||
tableView.register(FollowingViewCell.self, forCellReuseIdentifier: "FollowingViewCell")
|
||||
|
||||
return tableView
|
||||
}()
|
||||
|
||||
|
||||
let noDataView: MineSingleNoDataView = {
|
||||
let noDataView = MineSingleNoDataView.init()
|
||||
noDataView.isHidden = true
|
||||
|
||||
return noDataView
|
||||
}()
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
override func makeUI() {
|
||||
super.makeUI()
|
||||
|
||||
self.navigationItem.title = "粉丝"
|
||||
tableView.rx.setDelegate(self).disposed(by: rx.disposeBag)
|
||||
|
||||
view.addSubview(followersTopView)
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(noDataView)
|
||||
}
|
||||
|
||||
override func bindViewModel() {
|
||||
super.bindViewModel()
|
||||
|
||||
|
||||
|
||||
// self.segmentControl.currentSearchType = .single
|
||||
|
||||
guard let viewModel = viewModel as? FollowersViewModel else { return }
|
||||
|
||||
let input = FollowersViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
|
||||
|
||||
|
||||
|
||||
let output = viewModel.transform(input: input)
|
||||
|
||||
let dataSource = FollowersViewController.dataSource { cell, following in
|
||||
|
||||
}
|
||||
|
||||
output.followersItems.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
// output.blocklistItems.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
|
||||
followersTopView.snp.makeConstraints { make in
|
||||
make.top.equalTo(view)
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.height.equalTo(49)
|
||||
}
|
||||
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.top.equalTo(followersTopView.snp.bottom)
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.bottom.equalTo(view)
|
||||
}
|
||||
|
||||
noDataView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(view)
|
||||
make.height.equalTo(200)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension FollowersViewController {
|
||||
//TODO
|
||||
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, Following) -> Void) -> RxTableViewSectionedReloadDataSource<FollowingSection> {
|
||||
return RxTableViewSectionedReloadDataSource<FollowingSection>(
|
||||
configureCell: { dataSource, tableView, indexPath, item in
|
||||
let cell: FollowingViewCell = tableView.dequeueReusableCell(withIdentifier: "FollowingViewCell", for: indexPath) as! FollowingViewCell
|
||||
|
||||
cell.following = item
|
||||
|
||||
cell.buttonTapCallback = {audioTrack in
|
||||
buttonTapHandler(cell, audioTrack)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FollowersTopView: UIView {
|
||||
lazy var segmentControl: ScrollSegmentView = {
|
||||
|
||||
var style = SegmentStyle()
|
||||
style.showLine = true
|
||||
style.normalTitleColor = UIColor.secondaryText()
|
||||
style.selectedTitleColor = UIColor.primaryText()
|
||||
style.backgroundColor = UIColor.white
|
||||
style.titleSelectFont = UIFont.systemFont(ofSize: 15, weight: .medium)
|
||||
style.titleFont = UIFont.systemFont(ofSize: 15)
|
||||
|
||||
style.scrollLineHeight = 2
|
||||
style.scrollLineColor = .primary()
|
||||
style.coverBackgroundColor = .init(hex: 0x0d0d0d)
|
||||
style.normalborderColor = UIColor.tertiaryText()
|
||||
style.scrollTitle = false
|
||||
style.showCover = false
|
||||
style.normalborderColor = .clear
|
||||
style.lineSpace = 0
|
||||
|
||||
|
||||
|
||||
let segmentControl = ScrollSegmentView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 32), segmentStyle: style, titles: ["我的粉丝", "黑名单"])
|
||||
|
||||
|
||||
return segmentControl
|
||||
}()
|
||||
|
||||
var lineView: UIView = {
|
||||
let lineView = UIView.init()
|
||||
lineView.backgroundColor = .init(hex: 0xF2F3F7)
|
||||
return lineView
|
||||
}()
|
||||
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
backgroundColor = .white
|
||||
addSubview(segmentControl)
|
||||
addSubview(lineView)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
segmentControl.snp.makeConstraints { make in
|
||||
make.width.equalTo(185)
|
||||
make.height.equalTo(44)
|
||||
make.center.equalTo(self)
|
||||
}
|
||||
|
||||
lineView.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.bottom.equalTo(self)
|
||||
make.height.equalTo(6)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
//
|
||||
// FollowersViewModel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class FollowersViewModel: ViewModel, ViewModelType {
|
||||
|
||||
struct Input {
|
||||
let viewWillAppear: ControlEvent<Bool>
|
||||
|
||||
}
|
||||
|
||||
struct Output {
|
||||
let followersItems: BehaviorRelay<[FollowingSection]>
|
||||
let blocklistItems: BehaviorRelay<[FollowingSection]>
|
||||
}
|
||||
|
||||
let followersItems = BehaviorRelay<[FollowingSection]>.init(value: [])
|
||||
let blocklistItems = BehaviorRelay<[FollowingSection]>.init(value: [])
|
||||
|
||||
func transform(input: Input) -> Output {
|
||||
|
||||
input.viewWillAppear.subscribe { (_) in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
let name = Following.init(avator: "", name: "用户名", detail: "签名", isFollowing: false)
|
||||
|
||||
|
||||
followersItems.accept([FollowingSection.init(items: [name, name, name, name])])
|
||||
blocklistItems.accept([FollowingSection.init(items: [name, name, name, name])])
|
||||
|
||||
|
||||
|
||||
|
||||
return Output.init(followersItems: followersItems,
|
||||
blocklistItems: blocklistItems)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,213 @@
|
||||
//
|
||||
// FollowingViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
import Kingfisher
|
||||
|
||||
class FollowingViewController: TableViewController {
|
||||
let noDataView: MineSingleNoDataView = {
|
||||
let noDataView = MineSingleNoDataView.init()
|
||||
noDataView.isHidden = true
|
||||
return noDataView
|
||||
}()
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
override func makeUI() {
|
||||
super.makeUI()
|
||||
view.backgroundColor = .init(hex: 0xf5f5f5)
|
||||
|
||||
tableView.mj_header = nil
|
||||
tableView.mj_footer = nil
|
||||
|
||||
tableView.register(FollowingViewCell.self, forCellReuseIdentifier: "FollowingViewCell")
|
||||
|
||||
view.addSubview(noDataView)
|
||||
}
|
||||
|
||||
|
||||
override func bindViewModel() {
|
||||
super.bindViewModel()
|
||||
|
||||
guard let viewModel = viewModel as? FollowingViewModel else { return }
|
||||
|
||||
let input = FollowingViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||
selection: tableView.rx.itemSelected.asDriver())
|
||||
let output = viewModel.transform(input: input)
|
||||
|
||||
let dataSource = FollowingViewController.dataSource()
|
||||
|
||||
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
|
||||
output.itemSelected.subscribe { sectionItem in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
output.selection.drive { sectionItem in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
noDataView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(view)
|
||||
make.height.equalTo(200)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension FollowingViewController {
|
||||
static func dataSource() -> RxTableViewSectionedReloadDataSource<FollowingSection> {
|
||||
return RxTableViewSectionedReloadDataSource<FollowingSection>(
|
||||
configureCell: { dataSource, tableView, indexPath, item in
|
||||
let cell: FollowingViewCell = tableView.dequeueReusableCell(withIdentifier: "FollowingViewCell", for: indexPath) as! FollowingViewCell
|
||||
|
||||
cell.following = item
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FollowingViewCell: UITableViewCell {
|
||||
let avatoreView: UIImageView = {
|
||||
let avatoreView = UIImageView.init()
|
||||
avatoreView.layer.cornerRadius = 24
|
||||
avatoreView.layer.masksToBounds = true
|
||||
avatoreView.backgroundColor = .red
|
||||
|
||||
return avatoreView
|
||||
}()
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let nameLabel = UILabel.init()
|
||||
nameLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
|
||||
nameLabel.textColor = .primaryText()
|
||||
|
||||
return nameLabel
|
||||
}()
|
||||
|
||||
let detailLabel: UILabel = {
|
||||
let detailLabel = UILabel.init()
|
||||
detailLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
detailLabel.textColor = .tertiaryText()
|
||||
|
||||
return detailLabel
|
||||
}()
|
||||
|
||||
let followingButton: UIButton = {
|
||||
let followingButton = UIButton.init()
|
||||
followingButton.titleLabel?.font = UIFont.systemFont(ofSize: 15)
|
||||
followingButton.setTitleColor(.tertiaryText(), for: .normal)
|
||||
|
||||
followingButton.setTitle("互相关注", for: .normal)
|
||||
followingButton.setTitle("取消", for: .selected)
|
||||
|
||||
followingButton.setContentHuggingPriority(.required, for: .horizontal)
|
||||
followingButton.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
followingButton.addTarget(FollowingViewCell.self, action: #selector(followingButtonClick), for: .touchUpInside)
|
||||
return followingButton
|
||||
}()
|
||||
|
||||
var buttonTapCallback: ((Following) -> ())?
|
||||
|
||||
|
||||
var following: Following? {
|
||||
didSet {
|
||||
guard let following = following else { return }
|
||||
|
||||
avatoreView.kf.setImage(with: URL.init(string: following.avator))
|
||||
|
||||
|
||||
nameLabel.text = following.name
|
||||
detailLabel.text = following.detail
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
|
||||
makeUI()
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func followingButtonClick() {
|
||||
if let buttonTapCallback = self.buttonTapCallback,
|
||||
let following = following {
|
||||
buttonTapCallback(following)
|
||||
}
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
|
||||
|
||||
|
||||
contentView.addSubview(avatoreView)
|
||||
contentView.addSubview(nameLabel)
|
||||
contentView.addSubview(detailLabel)
|
||||
contentView.addSubview(followingButton)
|
||||
|
||||
|
||||
avatoreView.snp.makeConstraints { make in
|
||||
make.size.equalTo(CGSize.init(width: 48, height: 48))
|
||||
make.centerY.equalTo(contentView)
|
||||
make.left.equalTo(contentView).offset(18)
|
||||
make.top.equalTo(contentView).offset(20)
|
||||
make.bottom.equalTo(contentView).offset(-20)
|
||||
}
|
||||
|
||||
followingButton.snp.makeConstraints { make in
|
||||
make.right.equalTo(contentView).offset(-30)
|
||||
make.centerY.equalTo(contentView)
|
||||
}
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.top.equalTo(avatoreView.snp.top).offset(4)
|
||||
make.right.equalTo(followingButton.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
|
||||
detailLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatoreView.snp.right).offset(15)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(2)
|
||||
make.right.equalTo(followingButton.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
//
|
||||
// FollowingViewModel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
class FollowingViewModel: ViewModel, ViewModelType {
|
||||
|
||||
struct Input {
|
||||
let viewWillAppear: ControlEvent<Bool>
|
||||
let selection: Driver<IndexPath>
|
||||
|
||||
}
|
||||
|
||||
struct Output {
|
||||
let items: BehaviorRelay<[FollowingSection]>
|
||||
let selection: Driver<IndexPath>
|
||||
let itemSelected: PublishSubject<Following>
|
||||
|
||||
}
|
||||
|
||||
let itemSelected = PublishSubject<Following>()
|
||||
let items = BehaviorRelay<[FollowingSection]>.init(value: [])
|
||||
|
||||
func transform(input: Input) -> Output {
|
||||
|
||||
input.viewWillAppear.subscribe { (_) in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
let name = Following.init(avator: "", name: "用户名", detail: "签名", isFollowing: false)
|
||||
|
||||
|
||||
items.accept([FollowingSection.init(items: [name, name, name, name])])
|
||||
|
||||
|
||||
input.selection.drive { indexPath in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
return Output.init(items: items,
|
||||
selection: input.selection,
|
||||
itemSelected: itemSelected)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,231 @@
|
||||
//
|
||||
// LikeListViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
class LikeListViewController: TableViewController {
|
||||
|
||||
let noDataView: MineSingleNoDataView = {
|
||||
let noDataView = MineSingleNoDataView.init()
|
||||
noDataView.isHidden = true
|
||||
|
||||
return noDataView
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
}
|
||||
|
||||
override func makeUI() {
|
||||
super.makeUI()
|
||||
view.backgroundColor = .init(hex: 0xf5f5f5)
|
||||
|
||||
tableView.mj_header = nil
|
||||
tableView.mj_footer = nil
|
||||
|
||||
tableView.register(LikeViewCell.self, forCellReuseIdentifier: "LikeViewCell")
|
||||
|
||||
view.addSubview(noDataView)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override func bindViewModel() {
|
||||
super.bindViewModel()
|
||||
|
||||
guard let viewModel = viewModel as? LikeListViewModel else { return }
|
||||
|
||||
let input = LikeListViewModel.Input.init(viewWillAppear: rx.viewWillAppear)
|
||||
|
||||
let output = viewModel.transform(input: input)
|
||||
|
||||
let dataSource = LikeListViewController.dataSource { cell, like in
|
||||
|
||||
}
|
||||
|
||||
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
noDataView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(view)
|
||||
make.height.equalTo(200)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LikeListViewController {
|
||||
//TODO
|
||||
static func dataSource(_ buttonTapHandler: @escaping (UITableViewCell, Like) -> Void) -> RxTableViewSectionedReloadDataSource<LikeSection> {
|
||||
return RxTableViewSectionedReloadDataSource<LikeSection>(
|
||||
configureCell: { dataSource, tableView, indexPath, item in
|
||||
let cell: LikeViewCell = tableView.dequeueReusableCell(withIdentifier: "LikeViewCell", for: indexPath) as! LikeViewCell
|
||||
|
||||
cell.like = item
|
||||
|
||||
cell.buttonTapCallback = { like in
|
||||
buttonTapHandler(cell, like)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class LikeViewCell: UITableViewCell {
|
||||
let avatorView: UIImageView = {
|
||||
let avatorView = UIImageView.init()
|
||||
avatorView.layer.cornerRadius = 24
|
||||
avatorView.layer.masksToBounds = true
|
||||
avatorView.backgroundColor = .red
|
||||
|
||||
return avatorView
|
||||
}()
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let nameLabel = UILabel.init()
|
||||
nameLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
|
||||
nameLabel.textColor = .primaryText()
|
||||
|
||||
return nameLabel
|
||||
}()
|
||||
|
||||
let detailLabel: UILabel = {
|
||||
let detailLabel = UILabel.init()
|
||||
detailLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
detailLabel.textColor = .tertiaryText()
|
||||
|
||||
return detailLabel
|
||||
}()
|
||||
|
||||
let tipsLabel: UILabel = {
|
||||
let tipsLabel = UILabel.init()
|
||||
tipsLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
tipsLabel.textColor = .tertiaryText()
|
||||
tipsLabel.text = "赞了你的评论"
|
||||
|
||||
return tipsLabel
|
||||
}()
|
||||
|
||||
|
||||
|
||||
let dateLabel: UILabel = {
|
||||
let dateLabel = UILabel.init()
|
||||
dateLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
dateLabel.textColor = .tertiaryText()
|
||||
|
||||
dateLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
dateLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
|
||||
return dateLabel
|
||||
}()
|
||||
|
||||
|
||||
let badgeView: UIView = {
|
||||
let badgeView = UIView.init()
|
||||
badgeView.layer.cornerRadius = 2.5
|
||||
badgeView.backgroundColor = .primary()
|
||||
|
||||
return badgeView
|
||||
}()
|
||||
|
||||
|
||||
var like: Like? {
|
||||
didSet {
|
||||
guard let like = like else { return }
|
||||
|
||||
avatorView.image = UIImage.init(named: like.avator)
|
||||
nameLabel.text = like.name
|
||||
detailLabel.text = like.content
|
||||
|
||||
dateLabel.text = "\(like.date)"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var buttonTapCallback: ((Like) -> ())?
|
||||
|
||||
|
||||
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
|
||||
makeUI()
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
func makeUI() {
|
||||
|
||||
|
||||
|
||||
contentView.addSubview(avatorView)
|
||||
contentView.addSubview(nameLabel)
|
||||
contentView.addSubview(tipsLabel)
|
||||
contentView.addSubview(detailLabel)
|
||||
contentView.addSubview(dateLabel)
|
||||
contentView.addSubview(badgeView)
|
||||
|
||||
|
||||
avatorView.snp.makeConstraints { make in
|
||||
make.size.equalTo(CGSize.init(width: 48, height: 48))
|
||||
make.centerY.equalTo(contentView)
|
||||
make.left.equalTo(contentView).offset(18)
|
||||
make.top.equalTo(contentView).offset(20)
|
||||
make.bottom.equalTo(contentView).offset(-20)
|
||||
}
|
||||
|
||||
dateLabel.snp.makeConstraints { make in
|
||||
make.right.equalTo(contentView).offset(-18)
|
||||
make.top.equalTo(avatorView.snp.top).offset(6)
|
||||
}
|
||||
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatorView.snp.right).offset(15)
|
||||
make.top.equalTo(avatorView.snp.top).offset(4)
|
||||
}
|
||||
|
||||
tipsLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel.snp.right).offset(9)
|
||||
make.centerY.equalTo(nameLabel)
|
||||
make.right.equalTo(dateLabel.snp.left).offset(-15)
|
||||
}
|
||||
|
||||
detailLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(avatorView.snp.right).offset(15)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(2)
|
||||
make.right.equalTo(contentView.snp.right).offset(-15)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
//
|
||||
// LikeListViewModel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class LikeListViewModel: ViewModel, ViewModelType {
|
||||
|
||||
struct Input {
|
||||
let viewWillAppear: ControlEvent<Bool>
|
||||
|
||||
}
|
||||
|
||||
struct Output {
|
||||
let items: BehaviorRelay<[LikeSection]>
|
||||
}
|
||||
|
||||
let items = BehaviorRelay<[LikeSection]>.init(value: [])
|
||||
|
||||
func transform(input: Input) -> Output {
|
||||
|
||||
input.viewWillAppear.subscribe { (_) in
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
let name = Like(avator: "", name: "用户名", content: "签名", date: 10000)
|
||||
|
||||
|
||||
items.accept([LikeSection.init(items: [name, name, name, name])])
|
||||
|
||||
return Output.init(items: items)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,602 @@
|
||||
//
|
||||
// PersonalViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
class PersonalViewController: ViewController {
|
||||
let personalHeaderView: PersonalHeaderView = {
|
||||
let personalHeaderView = PersonalHeaderView.init()
|
||||
|
||||
return personalHeaderView
|
||||
}()
|
||||
|
||||
let personalView: PersonalView = {
|
||||
let personalView = PersonalView.init()
|
||||
personalView.tableView.register(SongViewCell.self, forCellReuseIdentifier: "SongViewCell")
|
||||
personalView.collectionView.register(MusicStyleCellView.self, forCellWithReuseIdentifier: "MusicStyleCellView")
|
||||
|
||||
return personalView
|
||||
}()
|
||||
|
||||
let noDataView: MineSingleNoDataView = {
|
||||
let noDataView = MineSingleNoDataView.init()
|
||||
noDataView.isHidden = true
|
||||
|
||||
return noDataView
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func makeUI() {
|
||||
super.makeUI()
|
||||
|
||||
view.addSubview(personalHeaderView)
|
||||
view.addSubview(personalView)
|
||||
view.addSubview(noDataView)
|
||||
|
||||
|
||||
self.personalHeaderView.nameLabel.text = "123"
|
||||
self.personalHeaderView.ipLabel.text = "IP 南京"
|
||||
|
||||
}
|
||||
|
||||
override func bindViewModel() {
|
||||
super.bindViewModel()
|
||||
|
||||
self.personalView.currentSearchType = .single
|
||||
|
||||
guard let viewModel = viewModel as? PersonalViewModel else { return }
|
||||
|
||||
let input = PersonalViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
|
||||
collectionViewSelection: personalView.collectionView.rx.itemSelected.asDriver(),
|
||||
followingControlTrigger: self.personalHeaderView.followersControl.rx.controlEvent(.touchUpInside).asDriver(),
|
||||
followersControlTrigger: self.personalHeaderView.followersControl.rx.controlEvent(.touchUpInside).asDriver(),
|
||||
likeTrigger: self.personalHeaderView.likeControl.rx.controlEvent(.touchUpInside).asDriver(),
|
||||
followingButtonTrigger: self.personalHeaderView.followingButton.rx.controlEvent(.touchUpInside).asDriver(),
|
||||
messageTrigger: self.personalHeaderView.messageButton.rx.controlEvent(.touchUpInside).asDriver())
|
||||
|
||||
|
||||
|
||||
let output = viewModel.transform(input: input)
|
||||
|
||||
let tableViewDataSource = PersonalViewController.tableViewDataSource { cell, audioTrack in
|
||||
let audioMoreActionViewModel = AudioMoreActionViewModel.init(provider: viewModel.provider)
|
||||
|
||||
self.navigator.show(segue: .audioMore(viewModel: audioMoreActionViewModel), sender: self, transition: .navigationPresent(type: .audioMore))
|
||||
|
||||
}
|
||||
let collectionViewDataSource = SearchResultsController.collectionViewDataSource()
|
||||
|
||||
output.collectionViewItems.bind(to: personalView.collectionView.rx.items(dataSource: collectionViewDataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
output.tableViewItems.bind(to: personalView.tableView.rx.items(dataSource: tableViewDataSource)).disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
self.personalHeaderView.followingControl.rx.controlEvent(.touchUpInside).subscribe { _ in
|
||||
let followingViewModel = FollowingViewModel.init(provider: viewModel.provider)
|
||||
|
||||
self.navigator.show(segue: .following(viewModel: followingViewModel), sender: self)
|
||||
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
self.personalHeaderView.followersControl.rx.controlEvent(.touchUpInside).subscribe { _ in
|
||||
let followersViewModel = FollowersViewModel.init(provider: viewModel.provider)
|
||||
|
||||
self.navigator.show(segue: .followers(viewModel: followersViewModel), sender: self)
|
||||
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
self.personalHeaderView.likeControl.rx.controlEvent(.touchUpInside).subscribe { _ in
|
||||
let likeListViewModel = LikeListViewModel.init(provider: viewModel.provider)
|
||||
|
||||
self.navigator.show(segue: .likeList(viewModel: likeListViewModel), sender: self)
|
||||
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
self.personalHeaderView.messageButton.rx.tap.subscribe { _ in
|
||||
let messageViewModel = MessageViewModel.init(provider: viewModel.provider)
|
||||
|
||||
self.navigator.show(segue: .message(viewModel: messageViewModel), sender: self)
|
||||
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
personalHeaderView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.top.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
// make.height.equalTo(BaseDimensions.topHeight + 224)
|
||||
}
|
||||
|
||||
personalView.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(personalHeaderView.snp.bottom)
|
||||
make.bottom.equalTo(view)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension PersonalViewController {
|
||||
//TODO
|
||||
static func tableViewDataSource(_ buttonTapHandler: @escaping (UITableViewCell, AudioTrack) -> Void) -> RxTableViewSectionedReloadDataSource<JournalSection> {
|
||||
return RxTableViewSectionedReloadDataSource<JournalSection>(
|
||||
configureCell: { dataSource, tableView, indexPath, item in
|
||||
let cell: SongViewCell = tableView.dequeueReusableCell(withIdentifier: "SongViewCell", for: indexPath) as! SongViewCell
|
||||
|
||||
cell.audioTrack = item
|
||||
|
||||
cell.buttonTapCallback = {audioTrack in
|
||||
buttonTapHandler(cell, audioTrack)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension PersonalViewController {
|
||||
|
||||
static func collectionViewDataSource() -> RxCollectionViewSectionedReloadDataSource<MusicStyleSection> {
|
||||
return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
|
||||
configureCell: { dataSource, collectionView, indexPath, item in
|
||||
// 配置和返回 cell
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MusicStyleCellView", for: indexPath) as! MusicStyleCellView
|
||||
// 配置 cell
|
||||
cell.titleLabel.text = item.title
|
||||
cell.volLabel.text = item.subTitle
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class PersonalView: UIView {
|
||||
lazy var segmentControl: ScrollSegmentView = {
|
||||
|
||||
var style = SegmentStyle()
|
||||
style.showLine = true
|
||||
style.normalTitleColor = UIColor.secondaryText()
|
||||
style.selectedTitleColor = UIColor.primaryText()
|
||||
style.backgroundColor = UIColor.white
|
||||
style.titleSelectFont = UIFont.systemFont(ofSize: 15, weight: .medium)
|
||||
style.titleFont = UIFont.systemFont(ofSize: 15)
|
||||
|
||||
style.scrollLineHeight = 2
|
||||
style.scrollLineColor = .primary()
|
||||
style.coverBackgroundColor = .init(hex: 0x0d0d0d)
|
||||
style.normalborderColor = UIColor.tertiaryText()
|
||||
style.scrollTitle = false
|
||||
style.showCover = false
|
||||
style.normalborderColor = .clear
|
||||
|
||||
// style.scrollTitle = true
|
||||
style.lineSpace = 0
|
||||
|
||||
|
||||
|
||||
let segmentControl = ScrollSegmentView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 32), segmentStyle: style, titles: ["喜欢的单曲", "收藏期刊"])
|
||||
|
||||
// segmentControl.scrollView.backgroundColor = .red
|
||||
|
||||
return segmentControl
|
||||
}()
|
||||
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = UITableView.init()
|
||||
|
||||
return tableView
|
||||
}()
|
||||
|
||||
let collectionView: UICollectionView = {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.minimumInteritemSpacing = 15
|
||||
layout.minimumLineSpacing = 24
|
||||
|
||||
layout.sectionInset = UIEdgeInsets.init(top: 0, left: 18, bottom: 0, right: 18)
|
||||
|
||||
layout.itemSize = CGSize(width: (BaseDimensions.screenWidth - 18 * 2 - 15) / 2, height: 147)
|
||||
|
||||
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
|
||||
|
||||
collectionView.register(MusicStyleCellView.self, forCellWithReuseIdentifier: "MusicStyleCellView")
|
||||
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
|
||||
|
||||
var currentSearchType: SearchType = .single {
|
||||
didSet {
|
||||
switch currentSearchType {
|
||||
case .single:
|
||||
collectionView.isHidden = true
|
||||
tableView.isHidden = false
|
||||
case .journal:
|
||||
collectionView.isHidden = false
|
||||
tableView.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
makeUI()
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
segmentControl.titleBtnOnClick = { [weak self] (label, index) in
|
||||
if index == 0 {
|
||||
self?.currentSearchType = .single
|
||||
} else {
|
||||
self?.currentSearchType = .journal
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
backgroundColor = .white
|
||||
|
||||
addSubview(segmentControl)
|
||||
addSubview(tableView)
|
||||
addSubview(collectionView)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
segmentControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(self).offset(18)
|
||||
make.top.equalTo(self).offset(0)
|
||||
make.height.equalTo(32)
|
||||
make.width.equalTo(200)
|
||||
}
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.top.equalTo(segmentControl.snp.bottom).offset(24)
|
||||
make.bottom.equalTo(self)
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.top.equalTo(segmentControl.snp.bottom).offset(24)
|
||||
make.bottom.equalTo(self)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
class PersonalHeaderView: UIView {
|
||||
let backgroungView: UIImageView = {
|
||||
let backgroungView = UIImageView.init()
|
||||
backgroungView.backgroundColor = .white
|
||||
|
||||
return backgroungView
|
||||
}()
|
||||
|
||||
|
||||
let containerView: UIView = {
|
||||
let containerView = UIView.init()
|
||||
containerView.layer.cornerRadius = 8
|
||||
containerView.layer.masksToBounds = true
|
||||
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
containerView.backgroundColor = .white
|
||||
|
||||
return containerView
|
||||
}()
|
||||
|
||||
let nameLabel: UILabel = {
|
||||
let nameLabel = UILabel.init()
|
||||
nameLabel.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
|
||||
nameLabel.textColor = .primaryText()
|
||||
|
||||
return nameLabel
|
||||
}()
|
||||
|
||||
let sexView: UIImageView = {
|
||||
let sexView = UIImageView.init()
|
||||
sexView.image = UIImage.init(named: "personal_sex_icon")
|
||||
|
||||
return sexView
|
||||
}()
|
||||
|
||||
let badgeView: UIImageView = {
|
||||
let badgeView = UIImageView.init()
|
||||
badgeView.image = UIImage.init(named: "personal_badge_icon")
|
||||
|
||||
return badgeView
|
||||
}()
|
||||
|
||||
|
||||
let ipLabel: BorderLabel = {
|
||||
let ipLabel = BorderLabel.init(padding: .init(top: 2, left: 6, bottom: 2, right: 6), frame: .zero)
|
||||
ipLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
ipLabel.textColor = UIColor.secondaryText()
|
||||
ipLabel.layer.borderColor = UIColor.separator().cgColor
|
||||
ipLabel.layer.borderWidth = 1
|
||||
ipLabel.layer.cornerRadius = 3
|
||||
|
||||
return ipLabel
|
||||
}()
|
||||
|
||||
let avatorView: UIImageView = {
|
||||
let avatorView = UIImageView.init()
|
||||
avatorView.layer.cornerRadius = 36
|
||||
avatorView.layer.masksToBounds = true
|
||||
|
||||
avatorView.backgroundColor = .red
|
||||
return avatorView
|
||||
}()
|
||||
|
||||
|
||||
let slognLabel: UILabel = {
|
||||
let slognLabel = UILabel.init()
|
||||
slognLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
slognLabel.textColor = .secondaryText()
|
||||
|
||||
slognLabel.text = "这个人很懒 没有留下签名…"
|
||||
|
||||
return slognLabel
|
||||
}()
|
||||
|
||||
|
||||
|
||||
let followingControl: PersonalMenuControl = {
|
||||
let followingControl = PersonalMenuControl.init()
|
||||
followingControl.titleLabel.text = "关注"
|
||||
followingControl.countLabel.text = "0"
|
||||
|
||||
return followingControl
|
||||
}()
|
||||
|
||||
let followersControl: PersonalMenuControl = {
|
||||
let followersControl = PersonalMenuControl.init()
|
||||
followersControl.titleLabel.text = "粉丝"
|
||||
followersControl.countLabel.text = "0"
|
||||
|
||||
return followersControl
|
||||
}()
|
||||
|
||||
let likeControl: PersonalMenuControl = {
|
||||
let likeControl = PersonalMenuControl.init()
|
||||
likeControl.titleLabel.text = "获赞"
|
||||
likeControl.countLabel.text = "0"
|
||||
|
||||
return likeControl
|
||||
}()
|
||||
|
||||
let followingButton: UIButton = {
|
||||
let followingButton = UIButton.init()
|
||||
followingButton.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||||
followingButton.setTitle("关注", for: .normal)
|
||||
followingButton.setTitle("取消", for: .selected)
|
||||
followingButton.setTitleColor(.white, for: .normal)
|
||||
followingButton.setTitleColor(.primaryText(), for: .selected)
|
||||
followingButton.backgroundColor = .primaryText()
|
||||
followingButton.layer.cornerRadius = 16
|
||||
followingButton.layer.masksToBounds = true
|
||||
|
||||
|
||||
return followingButton
|
||||
}()
|
||||
|
||||
|
||||
let messageButton: BadgeButton = {
|
||||
let messageButton = BadgeButton.init()
|
||||
messageButton.setImage(UIImage.init(named: "personal_message_btn"), for: .normal)
|
||||
|
||||
return messageButton
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
addSubview(backgroungView)
|
||||
addSubview(containerView)
|
||||
containerView.addSubview(nameLabel)
|
||||
containerView.addSubview(badgeView)
|
||||
containerView.addSubview(sexView)
|
||||
containerView.addSubview(ipLabel)
|
||||
containerView.addSubview(slognLabel)
|
||||
containerView.addSubview(avatorView)
|
||||
containerView.addSubview(followingControl)
|
||||
containerView.addSubview(followersControl)
|
||||
containerView.addSubview(likeControl)
|
||||
containerView.addSubview(followingButton)
|
||||
containerView.addSubview(messageButton)
|
||||
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroungView.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.top.equalTo(self)
|
||||
make.height.equalTo(261)
|
||||
}
|
||||
|
||||
containerView.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.top.equalTo(self).offset(BaseDimensions.topHeight + 60)
|
||||
make.bottom.equalTo(self)
|
||||
}
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView).offset(18)
|
||||
make.top.equalTo(containerView).offset(18)
|
||||
}
|
||||
|
||||
badgeView.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(nameLabel)
|
||||
make.left.equalTo(nameLabel.snp.right).offset(6)
|
||||
}
|
||||
|
||||
sexView.snp.makeConstraints { make in
|
||||
make.left.equalTo(badgeView.snp.right).offset(6)
|
||||
make.centerY.equalTo(nameLabel)
|
||||
}
|
||||
|
||||
ipLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(nameLabel)
|
||||
make.left.equalTo(sexView.snp.right).offset(6)
|
||||
}
|
||||
|
||||
avatorView.snp.makeConstraints { make in
|
||||
make.right.equalTo(containerView).offset(-18)
|
||||
make.size.equalTo(CGSize.init(width: 72, height: 72))
|
||||
make.top.equalTo(containerView).offset(28)
|
||||
}
|
||||
|
||||
slognLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView).offset(18)
|
||||
make.right.equalTo(avatorView.snp.left).offset(-18)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(3)
|
||||
}
|
||||
|
||||
followingControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView).offset(18)
|
||||
make.top.equalTo(slognLabel.snp.bottom).offset(15)
|
||||
}
|
||||
|
||||
followersControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(followingControl.snp.right).offset(30)
|
||||
make.centerY.equalTo(followingControl)
|
||||
}
|
||||
|
||||
likeControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(followersControl.snp.right).offset(30)
|
||||
make.centerY.equalTo(followingControl)
|
||||
}
|
||||
|
||||
followingButton.snp.makeConstraints { make in
|
||||
make.left.equalTo(containerView).offset(18)
|
||||
make.top.equalTo(followingControl.snp.bottom).offset(24)
|
||||
make.bottom.equalTo(self.snp.bottom).offset(-24)
|
||||
make.size.equalTo(CGSize.init(width: 76, height: 32))
|
||||
}
|
||||
|
||||
messageButton.snp.makeConstraints { make in
|
||||
make.left.equalTo(followingButton.snp.right).offset(30)
|
||||
make.centerY.equalTo(followingButton)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PersonalMenuControl: UIControl {
|
||||
let titleLabel: UILabel = {
|
||||
let titleLabel = UILabel.init()
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
titleLabel.textColor = .secondaryText()
|
||||
return titleLabel
|
||||
}()
|
||||
|
||||
let countLabel: UILabel = {
|
||||
let countLabel = UILabel.init()
|
||||
countLabel.font = UIFont.systemFont(ofSize: 17, weight: .medium)
|
||||
countLabel.textColor = .primaryText()
|
||||
|
||||
return countLabel
|
||||
}()
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(countLabel)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(self)
|
||||
make.top.equalTo(self)
|
||||
make.bottom.equalTo(self)
|
||||
}
|
||||
|
||||
countLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(titleLabel.snp.right).offset(6)
|
||||
make.centerY.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
//
|
||||
// PersonalViewModel.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class PersonalViewModel: ViewModel, ViewModelType {
|
||||
|
||||
struct Input {
|
||||
let viewWillAppear: ControlEvent<Bool>
|
||||
|
||||
let collectionViewSelection: Driver<IndexPath>
|
||||
|
||||
let followingControlTrigger: Driver<Void>
|
||||
let followersControlTrigger: Driver<Void>
|
||||
let likeTrigger: Driver<Void>
|
||||
let followingButtonTrigger: Driver<Void>
|
||||
let messageTrigger: Driver<Void>
|
||||
|
||||
}
|
||||
|
||||
struct Output {
|
||||
let tableViewItems: BehaviorRelay<[JournalSection]>
|
||||
let collectionViewItems: BehaviorRelay<[MusicStyleSection]>
|
||||
|
||||
let collectionViewItemSelected: PublishSubject<MusicStyle>
|
||||
}
|
||||
|
||||
|
||||
let tableViewItems = BehaviorRelay<[JournalSection]>.init(value: [])
|
||||
let collectionViewItemstems = BehaviorRelay<[MusicStyleSection]>.init(value: [])
|
||||
|
||||
let collectionViewSelection = PublishSubject<MusicStyle>()
|
||||
|
||||
|
||||
func transform(input: Input) -> Output {
|
||||
|
||||
|
||||
let journalDetail = JournalDetail.init(audio: "", cover: "", title: "", artist: "", number: "", tags: [], date: 0, content: "", isExpand: false)
|
||||
let item = AudioTrack.init(artists: [], availableMarkets: [""], discNumber: 0, durationMs: 0, explicit: false, externalUrls: ["": ""], id: "", name: "123", previewUrl: "")
|
||||
|
||||
let journalSection = JournalSection.init(items: [item, item, item, item], journalDetail: journalDetail)
|
||||
|
||||
tableViewItems.accept([journalSection])
|
||||
|
||||
|
||||
|
||||
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.collectionViewSelection.drive { indexPath in
|
||||
guard let sectionItem = self.collectionViewItemstems.value.first?.items[indexPath.row] else { return }
|
||||
self.collectionViewSelection.onNext(sectionItem)
|
||||
}.disposed(by: rx.disposeBag)
|
||||
|
||||
|
||||
|
||||
return Output.init(tableViewItems: tableViewItems,
|
||||
collectionViewItems: collectionViewItemstems, collectionViewItemSelected: collectionViewSelection)
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
//
|
||||
// PhotoConfirmViewController.swift
|
||||
// IndieMusic
|
||||
//
|
||||
// Created by WenLei on 2024/1/8.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class PhotoConfirmViewController: UIViewController {
|
||||
let photoControl: PhotoConfirmControl = {
|
||||
let photoControl = PhotoConfirmControl.init()
|
||||
photoControl.titleLabel.text = "从手机相册选择"
|
||||
photoControl.addTarget(PhotoConfirmViewController.self, action: #selector(photoControlClick), for: .touchUpInside)
|
||||
return photoControl
|
||||
}()
|
||||
|
||||
let confirmControl: PhotoConfirmControl = {
|
||||
let confirmControl = PhotoConfirmControl.init()
|
||||
confirmControl.lineView.isHidden = true
|
||||
confirmControl.titleLabel.text = "取消"
|
||||
confirmControl.addTarget(PhotoConfirmViewController.self, action: #selector(confirmControlClick), for: .touchUpInside)
|
||||
|
||||
return confirmControl
|
||||
}()
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
|
||||
func makeUI() {
|
||||
view.addSubview(photoControl)
|
||||
view.addSubview(confirmControl)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
photoControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.height.equalTo(52)
|
||||
make.top.equalTo(12)
|
||||
}
|
||||
|
||||
confirmControl.snp.makeConstraints { make in
|
||||
make.left.equalTo(view)
|
||||
make.right.equalTo(view)
|
||||
make.top.equalTo(photoControl.snp.bottom)
|
||||
make.height.equalTo(52)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@objc func photoControlClick() {
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
@objc func confirmControlClick() {
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PhotoConfirmControl: UIControl {
|
||||
let titleLabel: UILabel = {
|
||||
let titleLabel = UILabel.init()
|
||||
titleLabel.font = UIFont.systemFont(ofSize: 15)
|
||||
|
||||
return titleLabel
|
||||
}()
|
||||
|
||||
let lineView: UIView = {
|
||||
let lineView = UIView.init()
|
||||
lineView.backgroundColor = .separator()
|
||||
return lineView
|
||||
}()
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
makeUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func makeUI() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(lineView)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalTo(self)
|
||||
}
|
||||
|
||||
lineView.snp.makeConstraints { make in
|
||||
make.bottom.equalTo(self)
|
||||
make.left.equalTo(self)
|
||||
make.right.equalTo(self)
|
||||
make.height.equalTo(1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "personal_badge_icon.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.0 KiB |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "personal_message_btn.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 823 B |
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "personal_sex_icon.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 731 B |
Loading…
Reference in new issue