Message, personal information, comment list module building

dev
wenlei 1 year ago
parent 1062a4c0e3
commit d14bb84a28

@ -112,6 +112,25 @@
77C9B9C02B4AB5B50006C83F /* PrivacyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9BF2B4AB5B50006C83F /* PrivacyViewModel.swift */; };
77C9B9C22B4AB6C10006C83F /* AboutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9C12B4AB6C10006C83F /* AboutViewModel.swift */; };
77C9B9C42B4AB75E0006C83F /* CacheViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9C32B4AB75E0006C83F /* CacheViewModel.swift */; };
77C9B9C62B4B81890006C83F /* Timing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9C52B4B81890006C83F /* Timing.swift */; };
77C9B9C82B4B8E200006C83F /* AlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9C72B4B8E200006C83F /* AlertViewController.swift */; };
77C9B9CA2B4B90D40006C83F /* PhotoConfirmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9C92B4B90D40006C83F /* PhotoConfirmViewController.swift */; };
77C9B9CD2B4B93F40006C83F /* PersonalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9CC2B4B93F40006C83F /* PersonalViewController.swift */; };
77C9B9CF2B4B94020006C83F /* PersonalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9CE2B4B94020006C83F /* PersonalViewModel.swift */; };
77C9B9D12B4B99600006C83F /* BadgeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9D02B4B99600006C83F /* BadgeButton.swift */; };
77C9B9D32B4B9B180006C83F /* BorderLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9D22B4B9B180006C83F /* BorderLabel.swift */; };
77C9B9D52B4BBD6A0006C83F /* FollowingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9D42B4BBD6A0006C83F /* FollowingViewController.swift */; };
77C9B9D72B4BBD780006C83F /* FollowingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9D62B4BBD780006C83F /* FollowingViewModel.swift */; };
77C9B9D92B4BBFA60006C83F /* Following.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9D82B4BBFA60006C83F /* Following.swift */; };
77C9B9DB2B4BC40F0006C83F /* FollowersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9DA2B4BC40F0006C83F /* FollowersViewController.swift */; };
77C9B9DD2B4BC5D20006C83F /* FollowersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9DC2B4BC5D20006C83F /* FollowersViewModel.swift */; };
77C9B9DF2B4BCE300006C83F /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9DE2B4BCE300006C83F /* Message.swift */; };
77C9B9E12B4BDAEA0006C83F /* LikeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9E02B4BDAEA0006C83F /* LikeListViewController.swift */; };
77C9B9E32B4BDAFA0006C83F /* LikeListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9E22B4BDAFA0006C83F /* LikeListViewModel.swift */; };
77C9B9E52B4BDB4C0006C83F /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9E42B4BDB4C0006C83F /* Like.swift */; };
77C9B9E92B4BE04C0006C83F /* CommentListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9E82B4BE04C0006C83F /* CommentListViewController.swift */; };
77C9B9EB2B4BE7E50006C83F /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9EA2B4BE7E50006C83F /* Comment.swift */; };
77C9B9ED2B4BEA610006C83F /* CommentListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C9B9EC2B4BEA610006C83F /* CommentListViewModel.swift */; };
77FA0B282B0B3E1E00404C5E /* Journal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FA0B272B0B3E1E00404C5E /* Journal.swift */; };
77FA0B2A2B0B5F0D00404C5E /* AudioMoreActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FA0B292B0B5F0D00404C5E /* AudioMoreActionView.swift */; };
77FA0B2C2B0C480B00404C5E /* AudioMoreActionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FA0B2B2B0C480B00404C5E /* AudioMoreActionViewModel.swift */; };
@ -283,6 +302,25 @@
77C9B9BF2B4AB5B50006C83F /* PrivacyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyViewModel.swift; sourceTree = "<group>"; };
77C9B9C12B4AB6C10006C83F /* AboutViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewModel.swift; sourceTree = "<group>"; };
77C9B9C32B4AB75E0006C83F /* CacheViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheViewModel.swift; sourceTree = "<group>"; };
77C9B9C52B4B81890006C83F /* Timing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timing.swift; sourceTree = "<group>"; };
77C9B9C72B4B8E200006C83F /* AlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = "<group>"; };
77C9B9C92B4B90D40006C83F /* PhotoConfirmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoConfirmViewController.swift; sourceTree = "<group>"; };
77C9B9CC2B4B93F40006C83F /* PersonalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalViewController.swift; sourceTree = "<group>"; };
77C9B9CE2B4B94020006C83F /* PersonalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalViewModel.swift; sourceTree = "<group>"; };
77C9B9D02B4B99600006C83F /* BadgeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeButton.swift; sourceTree = "<group>"; };
77C9B9D22B4B9B180006C83F /* BorderLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderLabel.swift; sourceTree = "<group>"; };
77C9B9D42B4BBD6A0006C83F /* FollowingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingViewController.swift; sourceTree = "<group>"; };
77C9B9D62B4BBD780006C83F /* FollowingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingViewModel.swift; sourceTree = "<group>"; };
77C9B9D82B4BBFA60006C83F /* Following.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Following.swift; sourceTree = "<group>"; };
77C9B9DA2B4BC40F0006C83F /* FollowersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersViewController.swift; sourceTree = "<group>"; };
77C9B9DC2B4BC5D20006C83F /* FollowersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersViewModel.swift; sourceTree = "<group>"; };
77C9B9DE2B4BCE300006C83F /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
77C9B9E02B4BDAEA0006C83F /* LikeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeListViewController.swift; sourceTree = "<group>"; };
77C9B9E22B4BDAFA0006C83F /* LikeListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeListViewModel.swift; sourceTree = "<group>"; };
77C9B9E42B4BDB4C0006C83F /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = "<group>"; };
77C9B9E82B4BE04C0006C83F /* CommentListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListViewController.swift; sourceTree = "<group>"; };
77C9B9EA2B4BE7E50006C83F /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
77C9B9EC2B4BEA610006C83F /* CommentListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListViewModel.swift; sourceTree = "<group>"; };
77FA0B272B0B3E1E00404C5E /* Journal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Journal.swift; sourceTree = "<group>"; };
77FA0B292B0B5F0D00404C5E /* AudioMoreActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMoreActionView.swift; sourceTree = "<group>"; };
77FA0B2B2B0C480B00404C5E /* AudioMoreActionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMoreActionViewModel.swift; sourceTree = "<group>"; };
@ -477,6 +515,9 @@
774A17F62B04932100F56DF1 /* SegmentControl.swift */,
77FA0B3F2B0D8E9300404C5E /* LayoutableButton.swift */,
77FB7A732B49944E00B64030 /* ScrollSegmentView.swift */,
77C9B9C72B4B8E200006C83F /* AlertViewController.swift */,
77C9B9D02B4B99600006C83F /* BadgeButton.swift */,
77C9B9D22B4B9B180006C83F /* BorderLabel.swift */,
);
path = Common;
sourceTree = "<group>";
@ -540,6 +581,11 @@
7751D35D2B42B61100F1F2BD /* PersonInfo.swift */,
7751D3712B43B9ED00F1F2BD /* Search.swift */,
7751D3752B43E8D200F1F2BD /* MusicStyle.swift */,
77C9B9C52B4B81890006C83F /* Timing.swift */,
77C9B9D82B4BBFA60006C83F /* Following.swift */,
77C9B9DE2B4BCE300006C83F /* Message.swift */,
77C9B9E42B4BDB4C0006C83F /* Like.swift */,
77C9B9EA2B4BE7E50006C83F /* Comment.swift */,
);
path = Models;
sourceTree = "<group>";
@ -547,6 +593,7 @@
778B8A582AF8EAED0034AFD4 /* Modules */ = {
isa = PBXGroup;
children = (
77C9B9CB2B4B93D10006C83F /* Personal */,
77FB7A792B4A4FB600B64030 /* Message */,
774A180C2B06FFF500F56DF1 /* JournalDetail */,
7743999C2AFA18B0006F8EEA /* Player */,
@ -680,6 +727,7 @@
77C9B9C12B4AB6C10006C83F /* AboutViewModel.swift */,
7751D36F2B43A4FC00F1F2BD /* CacheViewController.swift */,
77C9B9C32B4AB75E0006C83F /* CacheViewModel.swift */,
77C9B9C92B4B90D40006C83F /* PhotoConfirmViewController.swift */,
);
path = Setting;
sourceTree = "<group>";
@ -706,6 +754,23 @@
path = "RxSwift+Extension";
sourceTree = "<group>";
};
77C9B9CB2B4B93D10006C83F /* Personal */ = {
isa = PBXGroup;
children = (
77C9B9CC2B4B93F40006C83F /* PersonalViewController.swift */,
77C9B9CE2B4B94020006C83F /* PersonalViewModel.swift */,
77C9B9D42B4BBD6A0006C83F /* FollowingViewController.swift */,
77C9B9D62B4BBD780006C83F /* FollowingViewModel.swift */,
77C9B9DA2B4BC40F0006C83F /* FollowersViewController.swift */,
77C9B9DC2B4BC5D20006C83F /* FollowersViewModel.swift */,
77C9B9E02B4BDAEA0006C83F /* LikeListViewController.swift */,
77C9B9E22B4BDAFA0006C83F /* LikeListViewModel.swift */,
77C9B9E82B4BE04C0006C83F /* CommentListViewController.swift */,
77C9B9EC2B4BEA610006C83F /* CommentListViewModel.swift */,
);
path = Personal;
sourceTree = "<group>";
};
77FB7A792B4A4FB600B64030 /* Message */ = {
isa = PBXGroup;
children = (
@ -978,12 +1043,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
77C9B9CF2B4B94020006C83F /* PersonalViewModel.swift in Sources */,
77FA0B422B0DFAA000404C5E /* LoginViewController.swift in Sources */,
7751D3702B43A4FC00F1F2BD /* CacheViewController.swift in Sources */,
7751D3642B42BC2E00F1F2BD /* AccountViewModel.swift in Sources */,
778B8A5D2AF8EC610034AFD4 /* Application.swift in Sources */,
7751D3622B42BC0C00F1F2BD /* AccountViewController.swift in Sources */,
7751D3542B42AE0E00F1F2BD /* SettingViewMdel.swift in Sources */,
77C9B9E12B4BDAEA0006C83F /* LikeListViewController.swift in Sources */,
774399A62AFE036A006F8EEA /* PlayerView.swift in Sources */,
778B8A5F2AF8ECAB0034AFD4 /* Navigator.swift in Sources */,
77FA0B302B0C4D5A00404C5E /* ShareActionController.swift in Sources */,
@ -1004,12 +1071,16 @@
77FAF7622B07434A00FC2CA1 /* AudioMoreActionController.swift in Sources */,
774A18122B07327C00F56DF1 /* CommentCountButton.swift in Sources */,
7751D36C2B439F0000F1F2BD /* AboutViewController.swift in Sources */,
77C9B9DF2B4BCE300006C83F /* Message.swift in Sources */,
77FA0B2C2B0C480B00404C5E /* AudioMoreActionViewModel.swift in Sources */,
778B8AC22AF8ED280034AFD4 /* TableViewController.swift in Sources */,
77FA0B562B0F4ABF00404C5E /* MineSingleController.swift in Sources */,
778B8A9D2AF8ECFC0034AFD4 /* LibsManager.swift in Sources */,
77FA0B4C2B0EF8BB00404C5E /* LoginViewModel.swift in Sources */,
77C9B9C62B4B81890006C83F /* Timing.swift in Sources */,
77C9B9EB2B4BE7E50006C83F /* Comment.swift in Sources */,
778B8A832AF8ECE50034AFD4 /* HomeViewController.swift in Sources */,
77C9B9CA2B4B90D40006C83F /* PhotoConfirmViewController.swift in Sources */,
778B8ABE2AF8ED280034AFD4 /* ViewModel.swift in Sources */,
77FA0B322B0C4E8E00404C5E /* ShareActionView.swift in Sources */,
7751D3782B43EA1200F1F2BD /* SearchResultsController.swift in Sources */,
@ -1027,16 +1098,23 @@
778B8A992AF8ECFC0034AFD4 /* Reachability.swift in Sources */,
774A18092B06120000F56DF1 /* Home.swift in Sources */,
77FB7A742B49944E00B64030 /* ScrollSegmentView.swift in Sources */,
77C9B9E52B4BDB4C0006C83F /* Like.swift in Sources */,
778B8ABF2AF8ED280034AFD4 /* NavigationController.swift in Sources */,
774399AA2AFE3170006F8EEA /* PaddingLabel.swift in Sources */,
77FB7A702B48074600B64030 /* MusicStyleViewModel.swift in Sources */,
7751D3502B42ABBF00F1F2BD /* SettingViewController.swift in Sources */,
77FA0B4E2B0EF8C700404C5E /* PhoneCodeViewModel.swift in Sources */,
778B8A822AF8ECE50034AFD4 /* HomeViewModel.swift in Sources */,
77C9B9E32B4BDAFA0006C83F /* LikeListViewModel.swift in Sources */,
778B8AAD2AF8ED0E0034AFD4 /* RxMJRefresh.swift in Sources */,
7751D3742B43C9FA00F1F2BD /* MusicStyleViewController.swift in Sources */,
778B8A922AF8ECF20034AFD4 /* AudioTrack.swift in Sources */,
77C9B9D32B4B9B180006C83F /* BorderLabel.swift in Sources */,
77C9B9E92B4BE04C0006C83F /* CommentListViewController.swift in Sources */,
77C9B9ED2B4BEA610006C83F /* CommentListViewModel.swift in Sources */,
77C9B9C82B4B8E200006C83F /* AlertViewController.swift in Sources */,
778B8A862AF8ECE50034AFD4 /* SearchViewModel.swift in Sources */,
77C9B9DD2B4BC5D20006C83F /* FollowersViewModel.swift in Sources */,
778B8A802AF8ECE50034AFD4 /* MineViewController.swift in Sources */,
778B8A8F2AF8ECF20034AFD4 /* EmptyModel.swift in Sources */,
778B8ABD2AF8ED280034AFD4 /* View.swift in Sources */,
@ -1064,6 +1142,8 @@
77FA0B502B0EFEF400404C5E /* PhoneCodeView.swift in Sources */,
778B8A812AF8ECE50034AFD4 /* HomeTabBarViewModel.swift in Sources */,
77FA0B4A2B0EE8F000404C5E /* PhoneCodeController.swift in Sources */,
77C9B9D92B4BBFA60006C83F /* Following.swift in Sources */,
77C9B9DB2B4BC40F0006C83F /* FollowersViewController.swift in Sources */,
778B8AC02AF8ED280034AFD4 /* ViewController.swift in Sources */,
7751D3862B45409000F1F2BD /* NSNotification+IndieMusic.swift in Sources */,
774399A02AFA1968006F8EEA /* PlayerTabBar.swift in Sources */,
@ -1080,13 +1160,16 @@
778B8A622AF8ECC20034AFD4 /* ErrorTracker.swift in Sources */,
774A18102B070A6900F56DF1 /* SongViewCell.swift in Sources */,
7751D3762B43E8D200F1F2BD /* MusicStyle.swift in Sources */,
77C9B9D72B4BBD780006C83F /* FollowingViewModel.swift in Sources */,
778B8AA92AF8ED0E0034AFD4 /* UIImage+IndieMusic.swift in Sources */,
77C9B9D12B4B99600006C83F /* BadgeButton.swift in Sources */,
778B8A7F2AF8ECE50034AFD4 /* MineViewModel.swift in Sources */,
778B8A9A2AF8ECFC0034AFD4 /* AudioManager.swift in Sources */,
774A180E2B07000C00F56DF1 /* JournalDetailView.swift in Sources */,
778B8A842AF8ECE50034AFD4 /* HomeTabBarController.swift in Sources */,
77FA0B3A2B0C55D900404C5E /* FilterViewModel.swift in Sources */,
778B8A9E2AF8ECFC0034AFD4 /* ThemeManager.swift in Sources */,
77C9B9CD2B4B93F40006C83F /* PersonalViewController.swift in Sources */,
7751D3682B42E96200F1F2BD /* ThanksViewController.swift in Sources */,
778B8A702AF8ECD30034AFD4 /* ErrorResponse.swift in Sources */,
77FA0B2A2B0B5F0D00404C5E /* AudioMoreActionView.swift in Sources */,
@ -1101,6 +1184,7 @@
77FA0B382B0C54C700404C5E /* FilterViewController.swift in Sources */,
77FA0B522B0F3BC700404C5E /* MineView.swift in Sources */,
778B8A6E2AF8ECD30034AFD4 /* RestApi.swift in Sources */,
77C9B9D52B4BBD6A0006C83F /* FollowingViewController.swift in Sources */,
778B8ABC2AF8ED280034AFD4 /* PresentationController.swift in Sources */,
77FA0B3E2B0C573600404C5E /* Filter.swift in Sources */,
77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */,

@ -40,8 +40,15 @@ class Navigator {
case about(viewModel: AboutViewModel)
case cache(viewModel: CacheViewModel)
case personal(viewModel: PersonalViewModel)
case following(viewModel: FollowingViewModel)
case followers(viewModel: FollowersViewModel)
case likeList(viewModel: LikeListViewModel)
case commentList(viewModel: CommentListViewModel)
case searchResults(viewModel: SearchResultsViewModel)
case photoConfirm
case alert
case test
case safari(URL)
case safariController(URL)
@ -116,7 +123,22 @@ class Navigator {
return CacheViewController.init(viewModel: viewModel, navigator: self)
case .privacy(viewModel: let viewModel):
return PrivacyViewController.init(viewModel: viewModel, navigator: self)
case .personal(viewModel: let viewModel):
return PersonalViewController.init(viewModel: viewModel, navigator: self)
case .following(viewModel: let viewModel):
return FollowingViewController.init(viewModel: viewModel, navigator: self)
case .followers(viewModel: let viewModel):
return FollowersViewController.init(viewModel: viewModel, navigator: self)
case .likeList(viewModel: let viewModel):
return LikeListViewController.init(viewModel: viewModel, navigator: self)
case .commentList(viewModel: let viewModel):
return CommentListViewController.init(viewModel: viewModel, navigator: self)
case .photoConfirm:
return PhotoConfirmViewController.init()
case .alert:
return AlertViewController.init()
case .test:
let test = UIViewController.init()
test.view.backgroundColor = .red

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

@ -12,6 +12,8 @@ enum PresentationViewType {
case filter
case audioMore
case share
case timing
case photoConfirm
}
class PresentationController: UIPresentationController {
@ -65,6 +67,13 @@ class CardPresentationController: PresentationController {
case .share:
return containerView.bounds
.inset(by: UIEdgeInsets(top: containerView.bounds.height - 181 - BaseDimensions.bottomHeight, left: 0, bottom: 0, right: 0))
case .timing:
return containerView.bounds
.inset(by: UIEdgeInsets(top: containerView.bounds.height - 205 - BaseDimensions.bottomHeight, left: 0, bottom: 0, right: 0))
case .photoConfirm:
return containerView.bounds
.inset(by: UIEdgeInsets(top: containerView.bounds.height - 112 - BaseDimensions.bottomHeight, left: 0, bottom: 0, right: 0))
case .tips:
return containerView.bounds
@ -109,8 +118,17 @@ class CardPresentationController: PresentationController {
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
case .timing:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
case .photoConfirm:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
case .tips(let isTapDismiss):
coordinator.animate(alongsideTransition: { [weak self] _ in

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

@ -10,16 +10,40 @@ import UIKit
class CommentHeaderView: UIView {
var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .medium)
titleLabel.textColor = .primaryText()
titleLabel.text = "精彩评论"
return titleLabel
}()
var segmentControl: SegmentControl = {
let segmentControl = SegmentControl.init()
segmentControl.items = ["热门", "最新"]
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
}()
override init(frame: CGRect) {
@ -50,7 +74,7 @@ class CommentHeaderView: UIView {
}
segmentControl.snp.makeConstraints { make in
make.right.equalTo(self).offset(0)
make.right.equalTo(self).offset(-18)
make.centerY.equalTo(self)
}
}

@ -66,7 +66,19 @@ class PhoneCodeController: ViewController {
boxInputView.textDidChangeblock = { (text, isFinish) in
if isFinish {
let agress = AgreementViewController.init()
let agress = AlertViewController.init()
agress.titleLabel.text = "阅读并同意以下协议"
agress.cancelButton.setTitle("取消", for: .normal)
agress.confirmButton.setTitle("已阅读并同意", for: .normal)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
paragraphStyle.lineSpacing = 5
agress.detailTextView.addAttributed(attributedes: [("已阅读并同意 ", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle]),
("《用户协议隐私政策》", [NSAttributedString.Key.foregroundColor: UIColor.primaryText(), NSAttributedString.Key.link: URL.init(string: Configs.App.aggrementUrl) ?? "", NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12, weight: .medium)]),
(" 并授权获取本机号码", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText(), NSAttributedString.Key.paragraphStyle: paragraphStyle])
])
self.present(agress, animated: true)

@ -6,14 +6,433 @@
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class MessageViewController: ViewController {
class MessageViewController: ViewController, UIScrollViewDelegate {
let messageTopView: MessageTopView = {
let messageTopView = MessageTopView.init()
return messageTopView
}()
var tableView: UITableView = {
let tableView = UITableView.init()
tableView.register(MessageCellView.self, forCellReuseIdentifier: "MessageCellView")
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(messageTopView)
view.addSubview(tableView)
view.addSubview(noDataView)
}
override func bindViewModel() {
super.bindViewModel()
// self.segmentControl.currentSearchType = .single
guard let viewModel = viewModel as? MessageViewModel else { return }
let input = MessageViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
tableViewSelection: tableView.rx.itemSelected.asDriver(),
collectionViewSelection: tableView.rx.itemSelected.asDriver())
let output = viewModel.transform(input: input)
let dataSource = MessageViewController.tableViewDataSource()
output.tableViewItems.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.tableViewItemSelected.subscribe { message in
let commentListViewModel = CommentListViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .commentList(viewModel: commentListViewModel), sender: self)
}.disposed(by: rx.disposeBag)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
messageTopView.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(messageTopView.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 MessageViewController {
//TODO
static func tableViewDataSource() -> RxTableViewSectionedReloadDataSource<MessageSection> {
return RxTableViewSectionedReloadDataSource<MessageSection>(
configureCell: { dataSource, tableView, indexPath, item in
let cell: MessageCellView = tableView.dequeueReusableCell(withIdentifier: "MessageCellView", for: indexPath) as! MessageCellView
cell.message = item
return cell
}
)
}
}
extension MessageViewController {
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 MessageTopView: 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)
}
}
}
class MessageCellView: 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 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 message: Message? {
didSet {
guard let message = message else { return }
avatorView.image = UIImage.init(named: message.icon)
nameLabel.text = message.title
detailLabel.text = message.detail
dateLabel.text = "\(message.date)"
}
}
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(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)
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)
}
}
}
class MessageActivitiesViewCell: UITableViewCell {
let titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.layer.cornerRadius = 3
titleImageView.layer.masksToBounds = true
return titleImageView
}()
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.numberOfLines = 2
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
titleLabel.textColor = .primaryText()
return titleLabel
}()
let dateLabel: UILabel = {
let dateLabel = UILabel.init()
dateLabel.font = UIFont.systemFont(ofSize: 12)
dateLabel.textColor = .tertiaryText()
return dateLabel
}()
let containerView: UIView = {
let containerView = UIView.init()
containerView.layer.cornerRadius = 3
containerView.layer.masksToBounds = true
containerView.backgroundColor = .white
return containerView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
contentView.addSubview(containerView)
containerView.addSubview(titleImageView)
containerView.addSubview(titleLabel)
containerView.addSubview(dateLabel)
containerView.snp.makeConstraints { make in
make.left.equalTo(contentView).offset(18)
make.right.equalTo(contentView).offset(-18)
make.top.equalTo(contentView).offset(12)
make.bottom.equalTo(contentView)
}
titleImageView.snp.makeConstraints { make in
make.left.equalTo(containerView)
make.right.equalTo(containerView)
make.top.equalTo(containerView)
make.height.equalTo(170)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.right.equalTo(containerView).offset(-12)
make.top.equalTo(titleImageView.snp.bottom).offset(10)
}
dateLabel.snp.makeConstraints { make in
make.left.equalTo(containerView).offset(12)
make.top.equalTo(titleLabel.snp.bottom).offset(12)
make.bottom.equalTo(containerView).offset(-12)
}
}
}

@ -9,86 +9,63 @@ import Foundation
import RxSwift
import RxCocoa
class MessageViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let selection: Driver<IndexPath>
let dropButtonTrigger: Driver<Void>
let playButtonTrigger: Driver<Void>
let shareButtonTrigger: Driver<Void>
// let moreButtonTrigger: Driver<AudioTrack>
let tableViewSelection: Driver<IndexPath>
let collectionViewSelection: Driver<IndexPath>
}
struct Output {
let items: BehaviorRelay<[JournalSection]>
let selection: Driver<IndexPath>
let itemSelected: PublishSubject<AudioTrack>
let journalDetail: PublishSubject<JournalDetail>
let tableViewItems: BehaviorRelay<[MessageSection]>
let collectionViewItems: BehaviorRelay<[MessageSection]>
let currentPlaying: PublishSubject<AudioTrack>
let dowloadState: Driver<Float>
let isLike: BehaviorRelay<Bool>
let isExpand: BehaviorRelay<Bool>
let tableViewItemSelected: PublishSubject<Message>
let collectionViewItemSelected: PublishSubject<Message>
}
let itemSelected = PublishSubject<AudioTrack>()
let items = BehaviorRelay<[JournalSection]>.init(value: [])
let isExpand = BehaviorRelay<Bool>.init(value: false)
let tableViewItems = BehaviorRelay<[MessageSection]>.init(value: [])
let collectionViewItemstems = BehaviorRelay<[MessageSection]>.init(value: [])
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
}.disposed(by: rx.disposeBag)
input.dropButtonTrigger.drive { _ in
self.isExpand.accept(!self.isExpand.value)
let tableViewSelection = PublishSubject<Message>()
let collectionViewSelection = PublishSubject<Message>()
}.disposed(by: rx.disposeBag)
func transform(input: Input) -> Output {
let item = Message.init(icon: "", title: "评论", detail: "Mkle 回复了你的评论", isShowBadge: true, date: 1000000)
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: "")
tableViewItems.accept([MessageSection.init(items: [item, item, item, item])])
let journalSection = JournalSection.init(items: [item, item, item, item], journalDetail: journalDetail)
items.accept([journalSection])
collectionViewItemstems.accept([MessageSection.init(items: [item, item, item, item])])
input.selection.drive { indexPath in
guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
self.itemSelected.onNext(sectionItem)
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)
let journal = PublishSubject<JournalDetail>.init()
let currentPlaying: PublishSubject<AudioTrack> = .init()
let dowloadState: Driver<Float> = .just(0)
let isLike = BehaviorRelay<Bool>.init(value: false)
let isLick = BehaviorRelay<Bool>.init(value: false)
input.tableViewSelection.drive { indexPath in
guard let sectionItem = self.tableViewItems.value.first?.items[indexPath.row] else { return }
self.tableViewSelection.onNext(sectionItem)
}.disposed(by: rx.disposeBag)
return Output.init(items: items,
selection: input.selection,
itemSelected: itemSelected,
journalDetail: journal,
currentPlaying: currentPlaying,
dowloadState: dowloadState,
isLike: isLike,
isExpand: isExpand
)
return Output.init(tableViewItems: tableViewItems,
collectionViewItems: collectionViewItemstems,
tableViewItemSelected: tableViewSelection,
collectionViewItemSelected: collectionViewSelection)
}
}

@ -58,3 +58,4 @@ class MineJournalViewModel: ViewModel, ViewModelType {
}
}

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

@ -28,6 +28,9 @@ class SettingViewController: TableViewController {
super.makeUI()
view.backgroundColor = .init(hex: 0xf5f5f5)
tableView.mj_header = nil
tableView.mj_footer = nil
tableView.register(SettingViewCell.self, forCellReuseIdentifier: "SettingViewCell")
tableView.tableFooterView = footerView
@ -70,7 +73,7 @@ class SettingViewController: TableViewController {
case .timing(let setting):
let timingViewModel = TimingViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .timing(viewModel: timingViewModel), sender: self)
self.navigator.show(segue: .timing(viewModel: timingViewModel), sender: self, transition: .navigationPresent(type: .timing))
case .cache(let setting):
let cacheViewModel = CacheViewModel.init(provider: viewModel.provider)
@ -93,8 +96,9 @@ class SettingViewController: TableViewController {
self.navigator.show(segue: .thanks(viewModel: thanksViewModel), sender: self)
case .version(let setting):
break
let personalViewModel = PersonalViewModel.init(provider: viewModel.provider)
self.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
case .setting(_):
break
}

@ -6,35 +6,26 @@
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class TimingViewController: ViewController {
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
return titleLabel
let timingView: TimingView = {
let timingView = TimingView.init()
timingView.isHidden = true
return timingView
}()
let switchControl: UIControl = {
let switchControl = UIControl.init()
let customTimingView: CustomTimingView = {
let customTimingView = CustomTimingView.init()
customTimingView.isHidden = false
return switchControl
return customTimingView
}()
let collectionView: UICollectionView = {
let collectionView = UICollectionView.init()
return collectionView
}()
let button: UIButton = {
let button = UIButton.init()
button.setTitle("自定义", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
@ -45,11 +36,11 @@ class TimingViewController: ViewController {
override func makeUI() {
super.makeUI()
view.backgroundColor = .init(hex: 0xf3f3f3)
view.addSubview(titleLabel)
view.addSubview(switchControl)
view.addSubview(collectionView)
view.addSubview(button)
view.addSubview(timingView)
view.addSubview(customTimingView)
}
@ -58,32 +49,321 @@ class TimingViewController: ViewController {
super.bindViewModel()
guard let viewModel = viewModel as? TimingViewModel else { return }
let input = TimingViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
selection: timingView.collectionView.rx.itemSelected.asDriver())
let output = viewModel.transform(input: input)
let dataSource = TimingViewController.dataSource()
output.items.bind(to: timingView.collectionView.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()
timingView.snp.makeConstraints { make in
make.edges.equalTo(view)
}
customTimingView.snp.makeConstraints { make in
make.edges.equalTo(view)
}
}
}
extension TimingViewController {
static func dataSource() -> RxCollectionViewSectionedReloadDataSource<TimingSection> {
return RxCollectionViewSectionedReloadDataSource<TimingSection>(
configureCell: { dataSource, collectionView, indexPath, item in
// cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TimingViewCell", for: indexPath) as! TimingViewCell
// cell
cell.titleLabel.text = item.title
return cell
}
)
}
}
extension TimingViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let carePresentationVC = CardPresentationController.init(presentedViewController: presented, presenting: presenting)
carePresentationVC.viewType = .timing
return carePresentationVC
}
}
class TimingView: UIView {
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.text = "定时关闭"
return titleLabel
}()
let countLabel: UILabel = {
let countLabel = UILabel.init()
countLabel.font = UIFont.systemFont(ofSize: 15)
countLabel.text = "min"
countLabel.textColor = .tertiaryText()
return countLabel
}()
let switchControl: UISwitch = {
let switchControl = UISwitch.init()
return switchControl
}()
var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets.init(top: 0, left: 20, bottom: 0, right: 20)
layout.itemSize = CGSize(width: 54, height: 54)
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 16
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(TimingViewCell.self, forCellWithReuseIdentifier: "TimingViewCell")
collectionView.showsHorizontalScrollIndicator = false
collectionView.backgroundColor = .clear
return collectionView
}()
let customButton: UIButton = {
let customButton = UIButton.init()
customButton.setTitle("自定义", for: .normal)
customButton.setTitleColor(.primaryText(), for: .normal)
customButton.titleLabel?.font = UIFont.systemFont(ofSize: 15)
return customButton
}()
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)
addSubview(switchControl)
addSubview(collectionView)
addSubview(customButton)
}
override func layoutSubviews() {
super.layoutSubviews()
switchControl.snp.makeConstraints { make in
make.right.equalTo(view).offset(-18)
make.top.equalTo(view).offset(19)
make.right.equalTo(self).offset(-18)
make.top.equalTo(self).offset(19)
// make.size.equalTo(CGSize.init(width: 51, height: 31))
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(view).offset(18)
make.top.equalTo(view).offset(24)
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(24)
}
countLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel.snp.right).offset(12)
make.centerY.equalTo(titleLabel)
}
collectionView.snp.makeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(view).offset(80)
make.left.equalTo(self)
make.right.equalTo(self)
make.top.equalTo(self).offset(80)
make.height.equalTo(54)
}
button.snp.makeConstraints { make in
make.centerX.equalTo(view)
customButton.snp.makeConstraints { make in
make.centerX.equalTo(self)
make.top.equalTo(collectionView.snp.bottom).offset(30)
}
}
}
class TimingViewCell: UICollectionViewCell {
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.textAlignment = .center
return titleLabel
}()
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 27
contentView.layer.masksToBounds = true
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.center.equalTo(contentView)
}
}
}
class CustomTimingView: UIView {
let titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.textAlignment = .center
titleLabel.text = "自定义时间"
return titleLabel
}()
let containerView: UIView = {
let containerView = UIView.init()
containerView.layer.cornerRadius = 25
containerView.backgroundColor = .white
return containerView
}()
let textField: UITextField = {
let textField = UITextField.init()
textField.font = UIFont.systemFont(ofSize: 15)
textField.placeholder = "请输入时间"
return textField
}()
let measurementLabel: UILabel = {
let measurementLabel = UILabel.init()
measurementLabel.font = UIFont.systemFont(ofSize: 15, weight: .medium)
measurementLabel.textColor = .primary()
measurementLabel.setContentHuggingPriority(.required, for: .horizontal)
measurementLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
measurementLabel.text = "分钟"
return measurementLabel
}()
let confirmButton: UIButton = {
let confirmButton = UIButton.init()
confirmButton.setTitle("确定", for: .normal)
confirmButton.setTitleColor(.primaryText(), for: .normal)
confirmButton.titleLabel?.font = UIFont.systemFont(ofSize: 15)
return confirmButton
}()
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(containerView)
containerView.addSubview(measurementLabel)
containerView.addSubview(textField)
addSubview(confirmButton)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.top.equalTo(self).offset(24)
}
containerView.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.right.equalTo(self).offset(-18)
make.height.equalTo(50)
make.top.equalTo(titleLabel.snp.bottom).offset(26)
}
measurementLabel.snp.makeConstraints { make in
make.right.equalTo(containerView.snp.right).offset(-15)
make.centerY.equalTo(containerView)
}
textField.snp.makeConstraints { make in
make.right.equalTo(measurementLabel.snp.left).offset(-15)
make.left.equalTo(containerView).offset(15)
make.top.equalTo(containerView)
make.bottom.equalTo(containerView)
}
confirmButton.snp.makeConstraints { make in
make.top.equalTo(containerView.snp.bottom).offset(43)
make.centerX.equalTo(self)
}
}
}

@ -8,6 +8,8 @@
import Foundation
import RxSwift
import RxCocoa
import RxDataSources
class TimingViewModel: ViewModel, ViewModelType {
@ -18,14 +20,14 @@ class TimingViewModel: ViewModel, ViewModelType {
}
struct Output {
let items: BehaviorRelay<[SettingSection]>
let items: BehaviorRelay<[TimingSection]>
let selection: Driver<IndexPath>
let itemSelected: PublishSubject<Setting>
let itemSelected: PublishSubject<Timing>
}
let itemSelected = PublishSubject<Setting>()
let items = BehaviorRelay<[SettingSection]>.init(value: [])
let itemSelected = PublishSubject<Timing>()
let items = BehaviorRelay<[TimingSection]>.init(value: [])
func transform(input: Input) -> Output {
@ -33,41 +35,31 @@ class TimingViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
let phone = Setting.init(title: "手机号", detail: "去绑定", arrowIcon: "setting_arrow")
let wechatBinding = Setting.init(title: "绑定微信", detail: "", arrowIcon: "setting_arrow")
let privacy = Setting.init(title: "注销账户", detail: "", arrowIcon: "setting_arrow")
let time0 = Timing.init(title: "15")
let time1 = Timing.init(title: "30")
let time2 = Timing.init(title: "60")
let time3 = Timing.init(title: "90")
let time4 = Timing.init(title: "120")
//TODO
items.accept([SettingSection.init(items: [.setting(phone), .setting(wechatBinding), .setting(privacy)])])
items.accept([TimingSection.init(items: [time0, time1, time2, time3, time4])])
input.selection.drive { indexPath in
guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
switch sectionItem {
case .about(let setting):
// case .account(let setting):
// case .privacy(let setting):
// case .timing(let setting):
// case .cache(let setting):
// case .permission(let setting):
// case .feedback(let setting):
// case .about(let setting):
// case .contributors(let setting):
// case .version(let setting):
self.itemSelected.onNext(setting)
self.itemSelected.onNext(sectionItem)
default: break
}
}.disposed(by: rx.disposeBag)
return Output.init(items: items,
selection: input.selection,

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

@ -0,0 +1,36 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0.57735C8.6188 0.220085 9.3812 0.220085 10 0.57735L15.7942 3.92265C16.413 4.27992 16.7942 4.94017 16.7942 5.6547V12.3453C16.7942 13.0598 16.413 13.7201 15.7942 14.0774L10 17.4226C9.3812 17.7799 8.6188 17.7799 8 17.4226L2.20577 14.0773C1.58697 13.7201 1.20577 13.0598 1.20577 12.3453V5.6547C1.20577 4.94017 1.58697 4.27992 2.20577 3.92265L8 0.57735Z" fill="url(#paint0_linear_73_703)"/>
<path d="M7.60529 8.67712L7.6055 8.67731C8.66564 9.60123 8.97496 10.3848 8.97496 10.8561V15.1691H8.97488H8.97473H8.97458H8.97442H8.97427H8.97411H8.97396H8.9738H8.97365H8.97349H8.97333H8.97318H8.97302H8.97286H8.9727H8.97254H8.97238H8.97222H8.97206H8.9719H8.97174H8.97158H8.97142H8.97125H8.97109H8.97093H8.97076H8.9706H8.97043H8.97027H8.9701H8.96993H8.96977H8.9696H8.96943H8.96926H8.96909H8.96892H8.96876H8.96859H8.96841H8.96824H8.96807H8.9679H8.96773H8.96755H8.96738H8.96721H8.96703H8.96686H8.96668H8.96651H8.96633H8.96615H8.96598H8.9658H8.96562H8.96544H8.96526H8.96508H8.9649H8.96472H8.96458C8.97725 13.9245 8.31895 13.0006 7.16672 12.0244C6.53902 11.443 5.85313 10.588 5.84849 9.39255V5.68973H5.88271C5.88886 6.24283 6.09836 6.79088 6.4138 7.29724C6.73407 7.81133 7.1644 8.28386 7.60529 8.67712Z" fill="url(#paint1_linear_73_703)" stroke="url(#paint2_radial_73_703)" stroke-width="0.05"/>
<path d="M10.3947 8.67712L10.3945 8.67731C9.33436 9.60123 9.02504 10.3848 9.02504 10.8561V15.1691H9.02512H9.02527H9.02542H9.02558H9.02573H9.02589H9.02604H9.0262H9.02635H9.02651H9.02667H9.02682H9.02698H9.02714H9.0273H9.02746H9.02762H9.02778H9.02794H9.0281H9.02826H9.02842H9.02858H9.02875H9.02891H9.02907H9.02924H9.0294H9.02957H9.02973H9.0299H9.03007H9.03023H9.0304H9.03057H9.03074H9.03091H9.03108H9.03124H9.03141H9.03159H9.03176H9.03193H9.0321H9.03227H9.03245H9.03262H9.03279H9.03297H9.03314H9.03332H9.03349H9.03367H9.03385H9.03402H9.0342H9.03438H9.03456H9.03474H9.03492H9.0351H9.03528H9.03542C9.02275 13.9245 9.68105 13.0006 10.8333 12.0244C11.461 11.443 12.1469 10.588 12.1515 9.39255V5.68973H12.1173C12.1111 6.24283 11.9016 6.79088 11.5862 7.29724C11.2659 7.81133 10.8356 8.28386 10.3947 8.67712Z" fill="url(#paint3_linear_73_703)" stroke="url(#paint4_radial_73_703)" stroke-width="0.05"/>
<circle cx="8.99999" cy="5.18822" r="1.69706" fill="url(#paint5_linear_73_703)" stroke="url(#paint6_radial_73_703)" stroke-width="0.1"/>
<defs>
<linearGradient id="paint0_linear_73_703" x1="3.17647" y1="3.63971" x2="14.4" y2="16.65" gradientUnits="userSpaceOnUse">
<stop stop-color="#030303"/>
<stop offset="1" stop-color="#2F3137"/>
</linearGradient>
<linearGradient id="paint1_linear_73_703" x1="6.28672" y1="6.15837" x2="11.4344" y2="13.373" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF3DB"/>
<stop offset="1" stop-color="#E1A345"/>
</linearGradient>
<radialGradient id="paint2_radial_73_703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(5.83672 7.25297) rotate(71.6312) scale(4.9979 1.93558)">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint3_linear_73_703" x1="11.7133" y1="6.15837" x2="6.56556" y2="13.373" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF4DF"/>
<stop offset="1" stop-color="#955A02"/>
</linearGradient>
<radialGradient id="paint4_radial_73_703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.15 5.85002) rotate(104.257) scale(6.3415 2.45593)">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint5_linear_73_703" x1="7.64992" y1="3.59997" x2="7.87657" y2="6.85938" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF2D8"/>
<stop offset="1" stop-color="#BA8C47"/>
</linearGradient>
<radialGradient id="paint6_radial_73_703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7.26749 4.02351) rotate(45.1104) scale(2.45486 1.58941)">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

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

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 11.7429C21.0035 13.1004 20.6863 14.4397 20.0743 15.6514C19.3485 17.1035 18.2329 18.3249 16.8522 19.1787C15.4716 20.0325 13.8805 20.4851 12.2571 20.4857C10.8996 20.4893 9.56034 20.1721 8.34856 19.56L3 21L4.44 15.6514C3.82793 14.4397 3.51075 13.1004 3.51428 11.7429C3.51491 10.1195 3.96748 8.52844 4.82131 7.14779C5.67513 5.76714 6.89648 4.65147 8.34856 3.92574C9.56034 3.31367 10.8996 2.99649 12.2571 3.00003H12.7714C14.9153 3.11831 16.9402 4.02321 18.4585 5.54148C19.9768 7.05975 20.8817 9.08469 21 11.2286V11.7429Z" stroke="#17171A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="15.5" cy="12.5" r="1.5" fill="#17171A"/>
<circle cx="9.5" cy="12.5" r="1.5" fill="#17171A"/>
</svg>

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

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.7678 10.2322L17.6569 6.34309" stroke="black" stroke-opacity="0.95" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 10L18 6L14 6" stroke="black" stroke-opacity="0.95" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.8787 14.1213C14.8787 11.3599 12.6401 9.12132 9.87865 9.12132C8.49795 9.1213 7.24794 9.68097 6.34312 10.5858C5.4383 11.4906 4.87865 12.7406 4.87865 14.1213C4.87868 16.8827 7.11725 19.1213 9.87865 19.1213C12.6401 19.1213 14.8787 16.8827 14.8787 14.1213Z" stroke="black" stroke-opacity="0.95" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 731 B

Loading…
Cancel
Save