diff --git a/IndieMusic/IndieMusic.xcodeproj/project.pbxproj b/IndieMusic/IndieMusic.xcodeproj/project.pbxproj index ce431c6..cf111e3 100644 --- a/IndieMusic/IndieMusic.xcodeproj/project.pbxproj +++ b/IndieMusic/IndieMusic.xcodeproj/project.pbxproj @@ -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 = ""; }; 77C9B9C12B4AB6C10006C83F /* AboutViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewModel.swift; sourceTree = ""; }; 77C9B9C32B4AB75E0006C83F /* CacheViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheViewModel.swift; sourceTree = ""; }; + 77C9B9C52B4B81890006C83F /* Timing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timing.swift; sourceTree = ""; }; + 77C9B9C72B4B8E200006C83F /* AlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = ""; }; + 77C9B9C92B4B90D40006C83F /* PhotoConfirmViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoConfirmViewController.swift; sourceTree = ""; }; + 77C9B9CC2B4B93F40006C83F /* PersonalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalViewController.swift; sourceTree = ""; }; + 77C9B9CE2B4B94020006C83F /* PersonalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalViewModel.swift; sourceTree = ""; }; + 77C9B9D02B4B99600006C83F /* BadgeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeButton.swift; sourceTree = ""; }; + 77C9B9D22B4B9B180006C83F /* BorderLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderLabel.swift; sourceTree = ""; }; + 77C9B9D42B4BBD6A0006C83F /* FollowingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingViewController.swift; sourceTree = ""; }; + 77C9B9D62B4BBD780006C83F /* FollowingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingViewModel.swift; sourceTree = ""; }; + 77C9B9D82B4BBFA60006C83F /* Following.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Following.swift; sourceTree = ""; }; + 77C9B9DA2B4BC40F0006C83F /* FollowersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersViewController.swift; sourceTree = ""; }; + 77C9B9DC2B4BC5D20006C83F /* FollowersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersViewModel.swift; sourceTree = ""; }; + 77C9B9DE2B4BCE300006C83F /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; + 77C9B9E02B4BDAEA0006C83F /* LikeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeListViewController.swift; sourceTree = ""; }; + 77C9B9E22B4BDAFA0006C83F /* LikeListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeListViewModel.swift; sourceTree = ""; }; + 77C9B9E42B4BDB4C0006C83F /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = ""; }; + 77C9B9E82B4BE04C0006C83F /* CommentListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListViewController.swift; sourceTree = ""; }; + 77C9B9EA2B4BE7E50006C83F /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; + 77C9B9EC2B4BEA610006C83F /* CommentListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListViewModel.swift; sourceTree = ""; }; 77FA0B272B0B3E1E00404C5E /* Journal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Journal.swift; sourceTree = ""; }; 77FA0B292B0B5F0D00404C5E /* AudioMoreActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMoreActionView.swift; sourceTree = ""; }; 77FA0B2B2B0C480B00404C5E /* AudioMoreActionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMoreActionViewModel.swift; sourceTree = ""; }; @@ -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 = ""; @@ -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 = ""; @@ -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 = ""; @@ -706,6 +754,23 @@ path = "RxSwift+Extension"; sourceTree = ""; }; + 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 = ""; + }; 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 */, diff --git a/IndieMusic/IndieMusic/Application/Navigator.swift b/IndieMusic/IndieMusic/Application/Navigator.swift index 0cb481f..2a2425b 100644 --- a/IndieMusic/IndieMusic/Application/Navigator.swift +++ b/IndieMusic/IndieMusic/Application/Navigator.swift @@ -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 diff --git a/IndieMusic/IndieMusic/Common/AlertViewController.swift b/IndieMusic/IndieMusic/Common/AlertViewController.swift new file mode 100644 index 0000000..2474724 --- /dev/null +++ b/IndieMusic/IndieMusic/Common/AlertViewController.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Common/BadgeButton.swift b/IndieMusic/IndieMusic/Common/BadgeButton.swift new file mode 100644 index 0000000..0e70d0d --- /dev/null +++ b/IndieMusic/IndieMusic/Common/BadgeButton.swift @@ -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 + } + */ + +} diff --git a/IndieMusic/IndieMusic/Common/BorderLabel.swift b/IndieMusic/IndieMusic/Common/BorderLabel.swift new file mode 100644 index 0000000..452fbcc --- /dev/null +++ b/IndieMusic/IndieMusic/Common/BorderLabel.swift @@ -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 + } + + + } + +} + diff --git a/IndieMusic/IndieMusic/Common/PresentationController.swift b/IndieMusic/IndieMusic/Common/PresentationController.swift index fa113bd..4e39b9e 100644 --- a/IndieMusic/IndieMusic/Common/PresentationController.swift +++ b/IndieMusic/IndieMusic/Common/PresentationController.swift @@ -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 diff --git a/IndieMusic/IndieMusic/Models/Comment.swift b/IndieMusic/IndieMusic/Models/Comment.swift new file mode 100644 index 0000000..9123ff2 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Comment.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Models/Following.swift b/IndieMusic/IndieMusic/Models/Following.swift new file mode 100644 index 0000000..4dd41b2 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Following.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Models/Like.swift b/IndieMusic/IndieMusic/Models/Like.swift new file mode 100644 index 0000000..ce98d99 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Like.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Models/Message.swift b/IndieMusic/IndieMusic/Models/Message.swift new file mode 100644 index 0000000..1aebe16 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Message.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Models/Timing.swift b/IndieMusic/IndieMusic/Models/Timing.swift new file mode 100644 index 0000000..9575b19 --- /dev/null +++ b/IndieMusic/IndieMusic/Models/Timing.swift @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Modules/JournalDetail/CommentView.swift b/IndieMusic/IndieMusic/Modules/JournalDetail/CommentView.swift index 4f5b4eb..9469018 100644 --- a/IndieMusic/IndieMusic/Modules/JournalDetail/CommentView.swift +++ b/IndieMusic/IndieMusic/Modules/JournalDetail/CommentView.swift @@ -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) } } diff --git a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift index e2b5ab2..f5a5369 100644 --- a/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift +++ b/IndieMusic/IndieMusic/Modules/Login/PhoneCodeController.swift @@ -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) diff --git a/IndieMusic/IndieMusic/Modules/Message/MessageViewController.swift b/IndieMusic/IndieMusic/Modules/Message/MessageViewController.swift index bfce4e2..0492d4e 100644 --- a/IndieMusic/IndieMusic/Modules/Message/MessageViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Message/MessageViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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 { + return RxCollectionViewSectionedReloadDataSource( + 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) + } + + } + + + + } diff --git a/IndieMusic/IndieMusic/Modules/Message/MessageViewModel.swift b/IndieMusic/IndieMusic/Modules/Message/MessageViewModel.swift index 66b959b..0f479c0 100644 --- a/IndieMusic/IndieMusic/Modules/Message/MessageViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Message/MessageViewModel.swift @@ -9,86 +9,63 @@ import Foundation import RxSwift import RxCocoa + class MessageViewModel: ViewModel, ViewModelType { struct Input { let viewWillAppear: ControlEvent - let selection: Driver - - let dropButtonTrigger: Driver - let playButtonTrigger: Driver - let shareButtonTrigger: Driver -// let moreButtonTrigger: Driver + + let tableViewSelection: Driver + let collectionViewSelection: Driver } struct Output { - let items: BehaviorRelay<[JournalSection]> - let selection: Driver - let itemSelected: PublishSubject - - let journalDetail: PublishSubject + let tableViewItems: BehaviorRelay<[MessageSection]> + let collectionViewItems: BehaviorRelay<[MessageSection]> - let currentPlaying: PublishSubject - let dowloadState: Driver - let isLike: BehaviorRelay - let isExpand: BehaviorRelay + let tableViewItemSelected: PublishSubject + let collectionViewItemSelected: PublishSubject } - let itemSelected = PublishSubject() - let items = BehaviorRelay<[JournalSection]>.init(value: []) - - let isExpand = BehaviorRelay.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() + let collectionViewSelection = PublishSubject() - }.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.init() - let currentPlaying: PublishSubject = .init() - let dowloadState: Driver = .just(0) - let isLike = BehaviorRelay.init(value: false) - - let isLick = BehaviorRelay.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) + } } - diff --git a/IndieMusic/IndieMusic/Modules/Mine/MineJournalViewModel.swift b/IndieMusic/IndieMusic/Modules/Mine/MineJournalViewModel.swift index 1c08cf6..655e3ae 100644 --- a/IndieMusic/IndieMusic/Modules/Mine/MineJournalViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Mine/MineJournalViewModel.swift @@ -58,3 +58,4 @@ class MineJournalViewModel: ViewModel, ViewModelType { } } + diff --git a/IndieMusic/IndieMusic/Modules/Personal/CommentListViewController.swift b/IndieMusic/IndieMusic/Modules/Personal/CommentListViewController.swift new file mode 100644 index 0000000..698ec3d --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/CommentListViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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) + } + + + + + + } + + + +} diff --git a/IndieMusic/IndieMusic/Modules/Personal/CommentListViewModel.swift b/IndieMusic/IndieMusic/Modules/Personal/CommentListViewModel.swift new file mode 100644 index 0000000..417d040 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/CommentListViewModel.swift @@ -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 + + } + + 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) + } + +} + + diff --git a/IndieMusic/IndieMusic/Modules/Personal/FollowersViewController.swift b/IndieMusic/IndieMusic/Modules/Personal/FollowersViewController.swift new file mode 100644 index 0000000..9322726 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/FollowersViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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) + } + + } + +} diff --git a/IndieMusic/IndieMusic/Modules/Personal/FollowersViewModel.swift b/IndieMusic/IndieMusic/Modules/Personal/FollowersViewModel.swift new file mode 100644 index 0000000..970ce61 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/FollowersViewModel.swift @@ -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 + + } + + 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) + } + +} + + diff --git a/IndieMusic/IndieMusic/Modules/Personal/FollowingViewController.swift b/IndieMusic/IndieMusic/Modules/Personal/FollowingViewController.swift new file mode 100644 index 0000000..044a65a --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/FollowingViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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) + } + + } +} diff --git a/IndieMusic/IndieMusic/Modules/Personal/FollowingViewModel.swift b/IndieMusic/IndieMusic/Modules/Personal/FollowingViewModel.swift new file mode 100644 index 0000000..4bab2a1 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/FollowingViewModel.swift @@ -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 + let selection: Driver + + } + + struct Output { + let items: BehaviorRelay<[FollowingSection]> + let selection: Driver + let itemSelected: PublishSubject + + } + + let itemSelected = PublishSubject() + 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) + } + +} + + diff --git a/IndieMusic/IndieMusic/Modules/Personal/LikeListViewController.swift b/IndieMusic/IndieMusic/Modules/Personal/LikeListViewController.swift new file mode 100644 index 0000000..e4d87c7 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/LikeListViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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) + } + + + + } + +} diff --git a/IndieMusic/IndieMusic/Modules/Personal/LikeListViewModel.swift b/IndieMusic/IndieMusic/Modules/Personal/LikeListViewModel.swift new file mode 100644 index 0000000..ba98468 --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/LikeListViewModel.swift @@ -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 + + } + + 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) + } + +} + + diff --git a/IndieMusic/IndieMusic/Modules/Personal/PersonalViewController.swift b/IndieMusic/IndieMusic/Modules/Personal/PersonalViewController.swift new file mode 100644 index 0000000..4d84c6a --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/PersonalViewController.swift @@ -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 { + return RxTableViewSectionedReloadDataSource( + 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 { + return RxCollectionViewSectionedReloadDataSource( + 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) + } + } +} diff --git a/IndieMusic/IndieMusic/Modules/Personal/PersonalViewModel.swift b/IndieMusic/IndieMusic/Modules/Personal/PersonalViewModel.swift new file mode 100644 index 0000000..8aa0f4d --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Personal/PersonalViewModel.swift @@ -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 + + let collectionViewSelection: Driver + + let followingControlTrigger: Driver + let followersControlTrigger: Driver + let likeTrigger: Driver + let followingButtonTrigger: Driver + let messageTrigger: Driver + + } + + struct Output { + let tableViewItems: BehaviorRelay<[JournalSection]> + let collectionViewItems: BehaviorRelay<[MusicStyleSection]> + + let collectionViewItemSelected: PublishSubject + } + + + let tableViewItems = BehaviorRelay<[JournalSection]>.init(value: []) + let collectionViewItemstems = BehaviorRelay<[MusicStyleSection]>.init(value: []) + + let collectionViewSelection = PublishSubject() + + + 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) + + } + +} diff --git a/IndieMusic/IndieMusic/Modules/Setting/PhotoConfirmViewController.swift b/IndieMusic/IndieMusic/Modules/Setting/PhotoConfirmViewController.swift new file mode 100644 index 0000000..7e7ac1a --- /dev/null +++ b/IndieMusic/IndieMusic/Modules/Setting/PhotoConfirmViewController.swift @@ -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) + } + + } +} diff --git a/IndieMusic/IndieMusic/Modules/Setting/SettingViewController.swift b/IndieMusic/IndieMusic/Modules/Setting/SettingViewController.swift index 0e74c04..9d719d7 100644 --- a/IndieMusic/IndieMusic/Modules/Setting/SettingViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Setting/SettingViewController.swift @@ -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 } diff --git a/IndieMusic/IndieMusic/Modules/Setting/TimingViewController.swift b/IndieMusic/IndieMusic/Modules/Setting/TimingViewController.swift index 5c593f7..1a4b5c1 100644 --- a/IndieMusic/IndieMusic/Modules/Setting/TimingViewController.swift +++ b/IndieMusic/IndieMusic/Modules/Setting/TimingViewController.swift @@ -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 { + return RxCollectionViewSectionedReloadDataSource( + 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) + } + + } + } diff --git a/IndieMusic/IndieMusic/Modules/Setting/TimingViewModel.swift b/IndieMusic/IndieMusic/Modules/Setting/TimingViewModel.swift index 717811d..f0bae06 100644 --- a/IndieMusic/IndieMusic/Modules/Setting/TimingViewModel.swift +++ b/IndieMusic/IndieMusic/Modules/Setting/TimingViewModel.swift @@ -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 - let itemSelected: PublishSubject + let itemSelected: PublishSubject } - let itemSelected = PublishSubject() - let items = BehaviorRelay<[SettingSection]>.init(value: []) + let itemSelected = PublishSubject() + 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, diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/Contents.json new file mode 100644 index 0000000..f5ba212 --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/Contents.json @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/personal_badge_icon.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/personal_badge_icon.svg new file mode 100644 index 0000000..b8ec6f3 --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_badge_icon.imageset/personal_badge_icon.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/Contents.json new file mode 100644 index 0000000..db669da --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/Contents.json @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/personal_message_btn.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/personal_message_btn.svg new file mode 100644 index 0000000..428675b --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_message_btn.imageset/personal_message_btn.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/Contents.json b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/Contents.json new file mode 100644 index 0000000..3a7e7ef --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/Contents.json @@ -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 + } +} diff --git a/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/personal_sex_icon.svg b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/personal_sex_icon.svg new file mode 100644 index 0000000..8bab838 --- /dev/null +++ b/IndieMusic/IndieMusic/Resources/Assets.xcassets/personal/personal_sex_icon.imageset/personal_sex_icon.svg @@ -0,0 +1,5 @@ + + + + +