Handle home navigation bar click events

dev
wenlei 12 months ago
parent 5054f916c8
commit 3a3794d868

@ -20,6 +20,8 @@
770228F82B5A224500E07F7A /* NSLayoutConstraint+Multiplier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228F52B5A224500E07F7A /* NSLayoutConstraint+Multiplier.swift */; };
770228F92B5A224500E07F7A /* SliderControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228F62B5A224500E07F7A /* SliderControl.swift */; };
770F6CA22B64F1DE0082F53A /* Carousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770F6CA12B64F1DE0082F53A /* Carousel.swift */; };
771233EC2B6B6476009FAF01 /* UIButton+IndieMusic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 771233EB2B6B6476009FAF01 /* UIButton+IndieMusic.swift */; };
771233EE2B6B8CE6009FAF01 /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 771233ED2B6B8CE6009FAF01 /* NavigationBar.swift */; };
77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77165D732B464493002AE0A5 /* BarButtonItem.swift */; };
7736FF442B4CECF2008D5DAD /* CommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7736FF432B4CECF2008D5DAD /* CommentViewModel.swift */; };
7736FF462B4CF0E6008D5DAD /* CommentDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7736FF452B4CF0E6008D5DAD /* CommentDetailViewController.swift */; };
@ -252,6 +254,8 @@
770228F52B5A224500E07F7A /* NSLayoutConstraint+Multiplier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraint+Multiplier.swift"; sourceTree = "<group>"; };
770228F62B5A224500E07F7A /* SliderControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderControl.swift; sourceTree = "<group>"; };
770F6CA12B64F1DE0082F53A /* Carousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Carousel.swift; sourceTree = "<group>"; };
771233EB2B6B6476009FAF01 /* UIButton+IndieMusic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+IndieMusic.swift"; sourceTree = "<group>"; };
771233ED2B6B8CE6009FAF01 /* NavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
77165D732B464493002AE0A5 /* BarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonItem.swift; sourceTree = "<group>"; };
7736FF432B4CECF2008D5DAD /* CommentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentViewModel.swift; sourceTree = "<group>"; };
7736FF452B4CF0E6008D5DAD /* CommentDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailViewController.swift; sourceTree = "<group>"; };
@ -679,6 +683,7 @@
isa = PBXGroup;
children = (
778B8AB62AF8ED280034AFD4 /* NavigationController.swift */,
771233ED2B6B8CE6009FAF01 /* NavigationBar.swift */,
778B8AB32AF8ED270034AFD4 /* PresentationController.swift */,
778B8AB82AF8ED280034AFD4 /* TableView.swift */,
778B8AB92AF8ED280034AFD4 /* TableViewController.swift */,
@ -721,6 +726,7 @@
778B8AA32AF8ED0E0034AFD4 /* RxSwift+Extension */,
778B8AA62AF8ED0E0034AFD4 /* TextView.swift */,
7751D37B2B4516BE00F1F2BD /* UILabel+IndieMusic.swift */,
771233EB2B6B6476009FAF01 /* UIButton+IndieMusic.swift */,
77620D842B673AFB00798861 /* String+IndieMusic.swift */,
778B8AA72AF8ED0E0034AFD4 /* UIColor+IndieMusic.swift */,
778B8AA02AF8ED0E0034AFD4 /* UIImage+IndieMusic.swift */,
@ -1442,6 +1448,7 @@
778B8A6E2AF8ECD30034AFD4 /* RestApi.swift in Sources */,
77C9B9D52B4BBD6A0006C83F /* FollowingViewController.swift in Sources */,
778B8ABC2AF8ED280034AFD4 /* PresentationController.swift in Sources */,
771233EE2B6B8CE6009FAF01 /* NavigationBar.swift in Sources */,
77FA0B3E2B0C573600404C5E /* Filter.swift in Sources */,
77165D742B464493002AE0A5 /* BarButtonItem.swift in Sources */,
7751D3662B42BE7F00F1F2BD /* FeedbackViewController.swift in Sources */,
@ -1452,6 +1459,7 @@
77620D902B68DDA000798861 /* EditSexViewController.swift in Sources */,
774A18142B07329600F56DF1 /* MultiUserAvatarView.swift in Sources */,
778B8AAA2AF8ED0E0034AFD4 /* AVPlayer.swift in Sources */,
771233EC2B6B6476009FAF01 /* UIButton+IndieMusic.swift in Sources */,
77A60D7C2B5B976900D4E435 /* ResourceLoader.swift in Sources */,
77FA0B442B0DFABD00404C5E /* LoginView.swift in Sources */,
77FB7A7D2B4A4FD400B64030 /* MessageViewModel.swift in Sources */,

@ -0,0 +1,43 @@
//
// NavigationBar.swift
// IndieMusic
//
// Created by WenLei on 2024/2/1.
//
import Foundation
protocol NavigationBarTouchDelegate: AnyObject {
func navigationBar(_ navigationBar: NavigationBar, hitTest point: CGPoint, with event: UIEvent?)
}
class NavigationBar: UINavigationBar {
weak var touchDelegate: NavigationBarTouchDelegate?
private var isHitTestHandled = false
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let playButtonPoint = convert(point, to: self)
if self.point(inside: playButtonPoint, with: event) && !isHitTestHandled {
isHitTestHandled = true
touchDelegate?.navigationBar(self, hitTest: point, with: event)
DispatchQueue.main.async {
self.isHitTestHandled = false
}
return self
}
return super.hitTest(point, with: event)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

@ -13,10 +13,13 @@ protocol DefaultNavigation {
}
class NavigationController: UINavigationController {
lazy var pullToDismissTransition: PullToDismissTransition = PullToDismissTransition(viewController: self)
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
super.init(navigationBarClass: NavigationBar.self, toolbarClass: nil)
self.viewControllers = [rootViewController]
modalPresentationStyle = .custom
setup()
}
@ -28,6 +31,8 @@ class NavigationController: UINavigationController {
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@ -63,6 +68,8 @@ class NavigationController: UINavigationController {
func setup() {
interactivePopGestureRecognizer?.delegate = self
}
@objc fileprivate func back()
@ -125,6 +132,8 @@ extension NavigationController {
@objc func backAction() {
self.popViewController(animated: true)
}
}

@ -0,0 +1,23 @@
//
// UIButton+IndieMusic.swift
// IndieMusic
//
// Created by WenLei on 2024/2/1.
//
import UIKit
extension UIButton {
func setBackgroundColor(color: UIColor, forState: UIControl.State) {
self.clipsToBounds = true // add this to maintain corner radius
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
if let context = UIGraphicsGetCurrentContext() {
context.setFillColor(color.cgColor)
context.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
let colorImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.setBackgroundImage(colorImage, for: forState)
}
}
}

@ -134,10 +134,10 @@ extension UserDefaults {
enum defaultKeys: String {
case userToken
case UUID
case userID
case userName
case expirationDate
case UUID
}
}
@ -147,8 +147,6 @@ extension UserDefaults {
enum defaultKeys: String {
/// Debug
case isOpenDebug
///
case currentBaseUrl
}
}
@ -161,12 +159,20 @@ extension UserDefaults {
case isPrivacyPolicyAllowed
}
}
}
func removeAllDefaultKeys() {
UserDefaults.AccountInfo.remove(forKey: .userID)
func removeAllDefaultKeys() {
UserDefaults.AccountInfo.remove(forKey: .userToken)
UserDefaults.AccountInfo.remove(forKey: .UUID)
UserDefaults.AccountInfo.remove(forKey: .userID)
UserDefaults.AccountInfo.remove(forKey: .userName)
UserDefaults.AccountInfo.remove(forKey: .expirationDate)
}
}

@ -121,6 +121,12 @@ class AuthManager {
return #"{"d": "Apple","t":"\#(device)","s": "\#(sysVersion)", "v":"\#(appVersion)"}"#
}
func logout() {
UserDefaults.standard.removeAllDefaultKeys()
AuthManager.removeToken()
}
}

@ -51,11 +51,6 @@ class LibsManager: NSObject {
SVProgressHUD.setMaximumDismissTimeInterval(1)
}
func setupKeyBoard() {
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.enableAutoToolbar = true
}
func setupWeChatSDK() {
WXApi.startLog(by: .detail) { (log) in

@ -8,7 +8,7 @@
import Foundation
import RxDataSources
enum FollowersType {
enum FollowersType: Codable {
case followers
case blackList
}

@ -6,12 +6,60 @@
//
import Foundation
import RxDataSources
struct PersonInfo {
let avatar: String
let name: String
let slogn: String
let day: String
let sex: String
enum PersonInfoLikeType: Codable {
case audio
case journal
var description: String {
switch self {
case .audio:
return "收藏的单曲"
case .journal:
return "收藏期刊"
}
}
}
enum PersonInfoSection {
case audio(personInfoLikeType: PersonInfoLikeType, items: [PersonInfoItem])
case journal(personInfoLikeType: PersonInfoLikeType, items: [PersonInfoItem])
}
enum PersonInfoItem {
case audioItem(model: AudioTrack)
case journaItem(model: Journal)
}
extension PersonInfoSection: SectionModelType {
typealias Item = PersonInfoItem
var items: [PersonInfoItem] {
switch self {
case .audio(_, let items):
return items.map { $0 }
case .journal(_, let items):
return items.map { $0 }
}
}
init(original: PersonInfoSection, items: [PersonInfoItem]) {
switch original {
case .audio(_, let items):
self = .audio(personInfoLikeType: .audio, items: items)
case .journal(_, let items):
self = .journal(personInfoLikeType: .journal, items: items)
}
}
}

@ -8,7 +8,7 @@
import Foundation
import RxDataSources
enum SearchType {
enum SearchType: Codable {
case single
case journal
}

@ -31,6 +31,17 @@ enum SexType: Int, Codable {
return "保密"
}
}
var image: String {
switch self {
case .male:
return "mine_male_icon"
case .female:
return "mine_female_icon"
case .secret:
return "保密"
}
}
}

@ -567,3 +567,56 @@ class HomeNoLoginView: UIView {
}
class HomePagerView: UIView {
var pagerView: FSPagerView = {
let pagerView = FSPagerView.init()
pagerView.automaticSlidingInterval = 10.0
pagerView.isInfinite = true
pagerView.register(FSPagerViewCell.self, forCellWithReuseIdentifier: "bannerCell")
pagerView.layer.cornerRadius = 5
pagerView.layer.masksToBounds = true
return pagerView
}()
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func makeUI() {
addSubview(pagerView)
}
override func layoutSubviews() {
super.layoutSubviews()
pagerView.snp.makeConstraints { make in
make.edges.equalTo(self)
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
super.hitTest(point, with: event)
}
}

@ -14,48 +14,55 @@ import Kingfisher
import ETNavBarTransparent
class HomeViewController: TableViewController {
var pagerView: FSPagerView = {
let pagerView = FSPagerView.init()
pagerView.automaticSlidingInterval = 10.0
pagerView.isInfinite = true
pagerView.register(FSPagerViewCell.self, forCellWithReuseIdentifier: "bannerCell")
pagerView.layer.cornerRadius = 5
pagerView.layer.masksToBounds = true
var homePagerView: HomePagerView = {
let homePagerView = HomePagerView.init()
return pagerView
return homePagerView
}()
var carouselItems = [Carousel]()
var currentNavBarBgAlpha: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationItem.title = "期刊推荐"
if let navigationBar = self.navigationController?.navigationBar as? NavigationBar {
navigationBar.touchDelegate = self
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateNav(navBarBgAlpha: currentNavBarBgAlpha)
self.navigationItem.leftBarButtonItem = nil
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(true, animated: true)
}
}
override func makeUI() {
super.makeUI()
let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: BaseDimensions.screenWidth, height: 120))
pagerView.frame = CGRect.init(x: 18, y: 0, width: BaseDimensions.screenWidth - 18 * 2, height: 120)
pagerView.backgroundColor = .init(hex: 0xf5f5f5)
pagerView.dataSource = self
pagerView.delegate = self
homePagerView.frame = CGRect.init(x: 18, y: 0, width: BaseDimensions.screenWidth - 18 * 2, height: 120)
homePagerView.pagerView.dataSource = self
homePagerView.pagerView.delegate = self
headerView.addSubview(pagerView)
headerView.addSubview(homePagerView)
tableView.tableHeaderView = headerView
tableView.separatorColor = .clear
@ -125,7 +132,7 @@ class HomeViewController: TableViewController {
output.carouselItems.subscribe { carouselItems in
self.carouselItems = carouselItems
self.pagerView.reloadData()
self.homePagerView.pagerView.reloadData()
}.disposed(by: rx.disposeBag)
}
@ -133,8 +140,12 @@ class HomeViewController: TableViewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.snp.remakeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(view).offset(-BaseDimensions.statusBarHeight)
make.bottom.equalTo(view)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
@ -148,6 +159,27 @@ class HomeViewController: TableViewController {
}
func updateNav(navBarBgAlpha: CGFloat) {
self.navBarBgAlpha = navBarBgAlpha
self.currentNavBarBgAlpha = navBarBgAlpha
// UINavigationBarAppearance
let appearance = UINavigationBarAppearance()
//
appearance.titleTextAttributes = [.foregroundColor: UIColor.init(hex: 0x000000, alpha: navBarBgAlpha)] // ]
appearance.shadowColor = .clear
//
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
//
navigationController?.navigationBar.compactAppearance = appearance
}
}
@ -155,6 +187,8 @@ extension HomeViewController: FSPagerViewDelegate {
func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) {
pagerView.deselectItem(at: index, animated: true)
print("pagerView Click \(index)")
}
}
@ -239,13 +273,25 @@ extension HomeViewController {
}
extension HomeViewController {
// override func scrollViewDidScroll(_ scrollView: UIScrollView) {
// var delta = scrollView.contentOffset.y / CGFloat(64) + 1
// delta = CGFloat.maximum(delta, 0)
// navBarBgAlpha = CGFloat.minimum(delta, 1)
// print("\(CGFloat.minimum(delta, 1)))")
//
// }
extension HomeViewController: NavigationBarTouchDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var delta = scrollView.contentOffset.y / CGFloat(BaseDimensions.topHeight)
delta = CGFloat.maximum(delta, 0)
print("导航栏\(CGFloat.minimum(delta, 1)))")
updateNav(navBarBgAlpha: CGFloat.minimum(delta, 1))
}
func navigationBar(_ navigationBar: NavigationBar, hitTest point: CGPoint, with event: UIEvent?) {
let pagerViewPoint = view.convert(point, to: homePagerView)
if homePagerView.point(inside: pagerViewPoint, with: event) {
homePagerView.pagerView.selectItem(at: homePagerView.pagerView.currentIndex, animated: true)
homePagerView.pagerView.delegate?.pagerView?(homePagerView.pagerView, didHighlightItemAt: homePagerView.pagerView.currentIndex)
homePagerView.pagerView.delegate?.pagerView?(homePagerView.pagerView, didSelectItemAt: homePagerView.pagerView.currentIndex)
}
print("导航栏点击")
}
}

@ -71,11 +71,6 @@ class LoginViewController: ViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(false, animated: true)
}
}

@ -12,9 +12,11 @@ class MineHeaderView: UIView {
let iconView = UIImageView.init()
iconView.layer.cornerRadius = 32
iconView.layer.masksToBounds = true
iconView.layer.borderColor = UIColor.white.cgColor
iconView.layer.borderWidth = 1
iconView.image = UIImage.init(named: "mine_avatar_icon")
iconView.backgroundColor = .init(hex: 0xf5f5f5)
return iconView
}()
@ -53,16 +55,16 @@ class MineHeaderView: UIView {
var user: User? {
didSet {
guard let user = user else { return }
iconView.kf.setImage(with: URL.init(string: user.avatar ?? ""))
nameButton.setTitle(user.nickName, for: .normal)
iconView.kf.setImage(with: URL.init(string: user?.avatar ?? ""), placeholder: UIImage.init(named: "mine_avatar_icon"))
nameButton.setTitle(user?.nickName ?? "立即登录", for: .normal)
tipsLabel.text = user.signature ?? "独立 不独于世"
tipsLabel.text = user?.signature ?? "独立 不独于世"
mineMenuView.followingView.detailLabel.text = "\(user.followCount ?? 0)"
mineMenuView.followersView.detailLabel.text = "\(user.fansCount ?? 0)"
mineMenuView.likeView.detailLabel.text = "\(user.thumbUpCount ?? 0)"
mineMenuView.commentView.detailLabel.text = "\(user.commentReplyCount ?? 0)"
mineMenuView.followingView.detailLabel.text = "\(user?.followCount ?? 0)"
mineMenuView.followersView.detailLabel.text = "\(user?.fansCount ?? 0)"
mineMenuView.likeView.detailLabel.text = "\(user?.thumbUpCount ?? 0)"
mineMenuView.commentView.detailLabel.text = "\(user?.commentReplyCount ?? 0)"
}
}

@ -38,6 +38,9 @@ class MineViewController: TableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(true, animated: true)
}
}
@ -174,6 +177,8 @@ class MineViewController: TableViewController {
}
}

@ -22,19 +22,19 @@ class MineViewModel: ViewModel, ViewModelType {
let items: BehaviorRelay<[MineSection]>
let selection: Driver<IndexPath>
let itemSelected: PublishSubject<Mine>
let user: PublishSubject<User>
let user: BehaviorRelay<User?>
}
let itemSelected = PublishSubject<Mine>()
let items = BehaviorRelay<[MineSection]>.init(value: [])
let user = PublishSubject<User>()
let user = BehaviorRelay<User?>.init(value: nil)
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
self.requestUserInfoData().subscribe { user in
self.user.onNext(user)
self.user.accept(user)
let single = Mine.init(icon: "mine_like_img", title: "单曲", detail: "\(user.songCount ?? 0)", isPlaying: false)
let journal = Mine.init(icon: "mine_journal_img", title: "期刊", detail: "\(user.journalCount ?? 0)", isPlaying: false)
@ -48,6 +48,15 @@ class MineViewModel: ViewModel, ViewModelType {
} onError: { error in
self.user.accept(nil)
let single = Mine.init(icon: "mine_like_img", title: "单曲", detail: "共0首", isPlaying: false)
let journal = Mine.init(icon: "mine_journal_img", title: "期刊", detail: "共0期", isPlaying: false)
let download = Mine.init(icon: "mine_download_img", title: "下载", detail: "共0首", isPlaying: false)
self.items.accept([MineSection.init(items: [single, journal, download])])
}.disposed(by: self.rx.disposeBag)
@ -56,11 +65,6 @@ class MineViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
let single = Mine.init(icon: "mine_like_img", title: "单曲", detail: "共0首", isPlaying: false)
let journal = Mine.init(icon: "mine_journal_img", title: "期刊", detail: "共0期", isPlaying: false)
let download = Mine.init(icon: "mine_download_img", title: "下载", detail: "共0首", isPlaying: false)
items.accept([MineSection.init(items: [single, journal, download])])

@ -93,18 +93,28 @@ class FollowersViewController: ViewController, UIScrollViewDelegate {
let input = FollowersViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: refresh,
footerRefresh: footerRefreshTrigger,
followersType: self.followersType)
followersType: self.followersType,
modelSelected: tableView.rx.modelSelected(User.self).asDriver())
let output = viewModel.transform(input: input)
let dataSource = FollowersViewController.dataSource { [weak self] cell, user in
viewModel.itemSelected.onNext(user)
}
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
output.modelSelected.drive { [weak self] user in
guard let userID = user.id else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
}.disposed(by: rx.disposeBag)
}

@ -16,6 +16,7 @@ class FollowersViewModel: ViewModel, ViewModelType {
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let followersType: BehaviorRelay<FollowersType>
let modelSelected: Driver<User>
}
@ -23,6 +24,7 @@ class FollowersViewModel: ViewModel, ViewModelType {
let followersItems: BehaviorRelay<[FollowersSection]>
let blocklistItems: BehaviorRelay<[FollowersSection]>
let items: BehaviorRelay<[FollowersSection]>
let modelSelected: Driver<User>
}
@ -144,15 +146,15 @@ class FollowersViewModel: ViewModel, ViewModelType {
switch user.relation {
case .notFollowing:
case .notFollowing, .no:
self.requestLike(objectId: userID, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { _ in
// updatedUsers[0].items[index].relation = .mutualFollowing
//
// if let items = updatedUsers.first?.items {
// self.items.accept([FollowingSection.init(items: items)])
// }
updatedUsers[0].items[index].relation = .mutualFollowing
if let items = updatedUsers.first?.items {
self.items.accept([FollowersSection.init(followersType: self.followersType.value, items: items)])
}
} onError: { error in
@ -163,11 +165,11 @@ class FollowersViewModel: ViewModel, ViewModelType {
self.requestCancelLike(objectId: userID, collectType: followersType == .followers ? .following : .addBlockList)
.subscribe { _ in
// updatedUsers[0].items[index].relation = .notFollowing
//
// if let items = updatedUsers.first?.items {
// self.items.accept([FollowingSection.init(items: items)])
// }
updatedUsers[0].items[index].relation = .notFollowing
if let items = updatedUsers.first?.items {
self.items.accept([FollowersSection.init(followersType: self.followersType.value, items: items)])
}
} onError: { error in
@ -192,7 +194,8 @@ class FollowersViewModel: ViewModel, ViewModelType {
return Output.init(followersItems: followersItems,
blocklistItems: blocklistItems,
items: self.items)
items: self.items,
modelSelected: input.modelSelected)
}

@ -57,6 +57,9 @@ class FollowingViewController: TableViewController {
let dataSource = FollowingViewController.dataSource { cell, user in
viewModel.itemSelected.onNext(user)
}
output.items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
@ -65,11 +68,23 @@ class FollowingViewController: TableViewController {
output.itemSelected.subscribe { [weak self] sectionItem in
guard let userID = sectionItem.element?.id else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
}.disposed(by: rx.disposeBag)
output.selection.drive { [weak self] sectionItem in
output.selection.drive { [weak self] selection in
guard let sectionItem = output.items.value.first?.items[selection.row] else { return }
guard let userID = sectionItem.id else { return }
let personalViewModel = PersonalViewModel.init(userID: userID, provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
}.disposed(by: rx.disposeBag)

@ -11,19 +11,32 @@ import RxCocoa
import RxDataSources
class PersonalViewController: ViewController {
let personalHeaderView: PersonalHeaderView = {
let personalHeaderView = PersonalHeaderView.init()
return personalHeaderView
}()
var personalHeaderView: PersonalHeaderView?
let personalView: PersonalView = {
let personalView = PersonalView.init()
personalView.tableView.register(SongViewCell.self, forCellReuseIdentifier: "SongViewCell")
personalView.collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let viewModel = self.viewModel as? PersonalViewModel
return self.createSingleColumnSection()
}
let collectionView = UICollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.register(JournalViewCell.self, forCellWithReuseIdentifier: "JournalViewCell")
collectionView.register(JournalAudioViewCell.self, forCellWithReuseIdentifier: "JournalAudioViewCell")
collectionView.register(PersonalHeaderView.self, forSupplementaryViewOfKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "PersonalHeaderView")
collectionView.backgroundColor = .white
return personalView
return collectionView
}()
let noDataView: MineSingleNoDataView = {
let noDataView = MineSingleNoDataView.init()
@ -32,6 +45,29 @@ class PersonalViewController: ViewController {
return noDataView
}()
let personInfoLikeType = BehaviorRelay<PersonInfoLikeType>.init(value: .audio)
let headerRefreshTrigger = PublishSubject<Void>()
let footerRefreshTrigger = PublishSubject<Void>()
let isHeaderLoading = BehaviorRelay<(RefreshType, Bool)>.init(value: (.refresh, false))
let isFooterLoading = BehaviorRelay<(RefreshType, Bool)>.init(value: (.loadMore, false))
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navBarBgAlpha = 0
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navBarBgAlpha = 1
}
override func viewDidLoad() {
super.viewDidLoad()
@ -43,81 +79,163 @@ class PersonalViewController: ViewController {
override func makeUI() {
super.makeUI()
view.addSubview(personalHeaderView)
view.addSubview(personalView)
// view.addSubview(personalHeaderView)
view.addSubview(collectionView)
view.addSubview(noDataView)
self.personalHeaderView.nameLabel.text = "123"
self.personalHeaderView.ipLabel.text = "IP 南京"
collectionView.rx.refresh.subscribe { refreshType in
switch refreshType.element {
case .refresh:
self.headerRefreshTrigger.onNext(())
case .loadMore where AuthManager.shared.token?.isValid == true:
self.footerRefreshTrigger.onNext(())
default: break
}
}.disposed(by: rx.disposeBag)
}
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())
headerRefresh: headerRefreshTrigger,
footerRefresh: footerRefreshTrigger,
personInfoLikeType: personInfoLikeType,
selection: collectionView.rx.itemSelected.asDriver(),
followingButtonTrigger: headerRefreshTrigger.asDriver(onErrorJustReturn: ()),
messageButtonTrigger: headerRefreshTrigger.asDriver(onErrorJustReturn: ())
)
let output = viewModel.transform(input: input)
let tableViewDataSource = PersonalViewController.tableViewDataSource { [weak self] cell, audioTrack in
// let audioMoreActionViewModel = AudioMoreActionViewModel.init(audioTrack: audioTrack, provider: viewModel.provider)
let dataSource = RxCollectionViewSectionedReloadDataSource<PersonInfoSection>(
configureCell: { dataSource, collectionView, indexPath, item in
switch dataSource[indexPath] {
case .audioItem(let audioTrack):
let cell: JournalAudioViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalAudioViewCell", for: indexPath) as! JournalAudioViewCell
cell.audioTrack = audioTrack
cell.buttonTapCallback = { [weak self] audioTrack in
let audioMoreActionViewModel = AudioMoreActionViewModel.init(audioTrack: viewModel.needRefresh, provider: viewModel.provider)
self?.navigator.show(segue: .audioMore(viewModel: audioMoreActionViewModel), sender: self, transition: .navigationPresent(type: .audioMore))
}
return cell
case .journaItem(let journalDetail):
let cell: JournalViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
cell.journal = journalDetail
return cell
}
},
configureSupplementaryView: { [weak self] dataSource, collectionView, kind, indexPath in
guard kind == UICollectionView.elementKindSectionHeader, let self = self else {
return UICollectionReusableView()
}
let resuableView: PersonalHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "PersonalHeaderView", for: indexPath) as! PersonalHeaderView
self.personalHeaderView = resuableView
let section = dataSource.sectionModels.first.value
// resuableView.user = viewModel.us
// resuableView.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?.navigator.show(segue: .audioMore(viewModel: audioMoreActionViewModel), sender: self, transition: .navigationPresent(type: .audioMore))
// resuableView.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)
//
// resuableView.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)
}
resuableView.followingButton.rx.tap.subscribe { _ in
viewModel.followingButtonTrigger.accept(())
}.disposed(by: rx.disposeBag)
resuableView.messageButton.rx.tap.subscribe { _ in
viewModel.messageButtonTrigger.accept(())
}.disposed(by: rx.disposeBag)
return resuableView
}
)
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 { [weak self] _ in
let followingViewModel = FollowingViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .following(viewModel: followingViewModel), sender: self)
}.disposed(by: rx.disposeBag)
output.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: rx.disposeBag)
self.personalHeaderView.followersControl.rx.controlEvent(.touchUpInside).subscribe { [weak self] _ 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 { [weak self] _ in
let likeListViewModel = LikeListViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .likeList(viewModel: likeListViewModel), sender: self)
output.user.subscribe { user in
self.personalHeaderView?.user = user
}.disposed(by: rx.disposeBag)
self.personalHeaderView.messageButton.rx.tap.subscribe { [weak self] _ in
let messageViewModel = MessageViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .message(viewModel: messageViewModel), sender: self)
}.disposed(by: rx.disposeBag)
// self.personalHeaderView.followingControl.rx.controlEvent(.touchUpInside).subscribe { [weak self] _ 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 { [weak self] _ 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 { [weak self] _ 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 { [weak self] _ in
// let messageViewModel = MessageViewModel.init(provider: viewModel.provider)
//
// self?.navigator.show(segue: .message(viewModel: messageViewModel), sender: self)
//
// }.disposed(by: rx.disposeBag)
@ -127,17 +245,10 @@ class PersonalViewController: ViewController {
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
collectionView.snp.makeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(personalHeaderView.snp.bottom)
make.top.equalTo(view).offset(-BaseDimensions.topHeight)
make.bottom.equalTo(view)
}
}
@ -146,41 +257,77 @@ class PersonalViewController: ViewController {
extension PersonalViewController {
//TODO
static func tableViewDataSource(_ buttonTapHandler: @escaping (UITableViewCell, AudioTrack) -> Void) -> RxTableViewSectionedReloadDataSource<JournalSection> {
return RxTableViewSectionedReloadDataSource<JournalSection>(
configureCell: { dataSource, tableView, indexPath, item in
let cell: SongViewCell = tableView.dequeueReusableCell(withIdentifier: "SongViewCell", for: indexPath) as! SongViewCell
// cell.audioTrack = item
cell.buttonTapCallback = {audioTrack in
buttonTapHandler(cell, audioTrack)
}
return cell
}
)
func createSingleColumnSection() -> NSCollectionLayoutSection {
let singleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
let singleColumnItem = NSCollectionLayoutItem(layoutSize: singleColumnItemSize)
let singleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: singleColumnItemSize, subitems: [singleColumnItem])
let singleColumnSection = NSCollectionLayoutSection(group: singleColumnGroup)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(329 + BaseDimensions.statusBarHeight))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
singleColumnSection.boundarySupplementaryItems = [header]
return singleColumnSection
}
func createDoubleColumnSection() -> NSCollectionLayoutSection {
let doubleColumnItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(147))
let doubleColumnItem = NSCollectionLayoutItem(layoutSize: doubleColumnItemSize)
let doubleColumnGroup = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100)), subitem: doubleColumnItem, count: 2)
doubleColumnGroup.interItemSpacing = .fixed(10)
let doubleColumnSection = NSCollectionLayoutSection(group: doubleColumnGroup)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
doubleColumnSection.boundarySupplementaryItems = [header]
doubleColumnSection.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 18)
return doubleColumnSection
}
}
extension PersonalViewController {
//TODO
// static func tableViewDataSource(_ buttonTapHandler: @escaping (UITableViewCell, AudioTrack) -> Void) -> RxTableViewSectionedReloadDataSource<JournalSection> {
// return RxTableViewSectionedReloadDataSource<JournalSection>(
// configureCell: { dataSource, tableView, indexPath, item in
// let cell: SongViewCell = tableView.dequeueReusableCell(withIdentifier: "SongViewCell", for: indexPath) as! SongViewCell
//
//// cell.audioTrack = item
//
// cell.buttonTapCallback = {audioTrack in
// buttonTapHandler(cell, audioTrack)
// }
//
// return cell
// }
// )
// }
}
extension PersonalViewController {
static func collectionViewDataSource() -> RxCollectionViewSectionedReloadDataSource<MusicStyleSection> {
return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
configureCell: { dataSource, collectionView, indexPath, item in
// cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
// cell
cell.titleLabel.text = item.title
cell.volLabel.text = item.subTitle
return cell
}
)
}
// static func collectionViewDataSource() -> RxCollectionViewSectionedReloadDataSource<MusicStyleSection> {
// return RxCollectionViewSectionedReloadDataSource<MusicStyleSection>(
// configureCell: { dataSource, collectionView, indexPath, item in
// // cell
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JournalViewCell", for: indexPath) as! JournalViewCell
// // cell
// cell.titleLabel.text = item.title
// cell.volLabel.text = item.subTitle
//
// return cell
// }
// )
// }
}
@ -321,12 +468,16 @@ class PersonalView: UIView {
}
class PersonalHeaderView: UIView {
let backgroungView: UIImageView = {
let backgroungView = UIImageView.init()
backgroungView.backgroundColor = .white
class PersonalHeaderView: UICollectionReusableView {
var blurEffectView: UIImageView = {
let titleImageView = UIImageView.init()
let blurEffect = UIBlurEffect(style: .regular)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
return backgroungView
titleImageView.addSubview(blurEffectView)
return titleImageView
}()
@ -336,7 +487,7 @@ class PersonalHeaderView: UIView {
containerView.layer.masksToBounds = true
containerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
containerView.backgroundColor = .white
return containerView
}()
@ -350,14 +501,14 @@ class PersonalHeaderView: UIView {
let sexView: UIImageView = {
let sexView = UIImageView.init()
sexView.image = UIImage.init(named: "personal_sex_icon")
// 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")
// badgeView.image = UIImage.init(named: "personal_badge_icon")
return badgeView
}()
@ -370,6 +521,7 @@ class PersonalHeaderView: UIView {
ipLabel.layer.borderColor = UIColor.separator().cgColor
ipLabel.layer.borderWidth = 1
ipLabel.layer.cornerRadius = 3
ipLabel.isHidden = true
return ipLabel
}()
@ -427,10 +579,12 @@ class PersonalHeaderView: UIView {
followingButton.setTitle("取消", for: .selected)
followingButton.setTitleColor(.white, for: .normal)
followingButton.setTitleColor(.primaryText(), for: .selected)
followingButton.backgroundColor = .primaryText()
followingButton.setBackgroundColor(color: .primaryText(), forState: .normal)
followingButton.setBackgroundColor(color: .white, forState: .selected)
followingButton.layer.cornerRadius = 16
followingButton.layer.masksToBounds = true
return followingButton
}()
@ -443,6 +597,93 @@ class PersonalHeaderView: UIView {
return messageButton
}()
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
}()
var user: User? {
didSet {
guard let user = user else { return }
blurEffectView.kf.setImage(with: URL.init(string: user.avatar ?? ""))
avatarView.kf.setImage(with: URL.init(string: user.avatar ?? ""))
nameLabel.text = user.nickName
slognLabel.addAttributed(attributedes: [("\(user.ipLocation ?? "") | ", [NSAttributedString.Key.foregroundColor: UIColor.secondaryText()]),
("\(user.signature ?? "这个人很懒 没有留下签名…")", [NSAttributedString.Key.foregroundColor: UIColor.tertiaryText()])
])
followingControl.titleLabel.text = "关注"
followingControl.countLabel.text = "\(user.followCount ?? 0)"
followersControl.countLabel.text = "\(user.fansCount ?? 0)"
likeControl.countLabel.text = "\(user.thumbUpCount ?? 0)"
sexView.image = UIImage.init(named: user.sex?.image ?? "")
switch user.relation {
case .notFollowing:
followingButton.setTitle("关注", for: .normal)
followingButton.setTitle("取消", for: .selected)
updateButtonState(isSelcted: false)
case .following:
followingButton.setTitle("关注", for: .normal)
followingButton.setTitle("取消", for: .selected)
updateButtonState(isSelcted: true)
case .mutualFollowing:
followingButton.setTitle("互相关注", for: .normal)
followingButton.setTitle("取消", for: .selected)
followingButton.isSelected = true
updateButtonState(isSelcted: true)
case .blockedByMe:
followingButton.setTitle("互相关注", for: .normal)
followingButton.setTitle("解除黑名单", for: .selected)
followingButton.isSelected = true
updateButtonState(isSelcted: true)
case .blockedByOther:
break
default: break
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
@ -454,8 +695,10 @@ class PersonalHeaderView: UIView {
}
func makeUI() {
addSubview(backgroungView)
addSubview(blurEffectView)
addSubview(containerView)
addSubview(segmentControl)
containerView.addSubview(nameLabel)
containerView.addSubview(badgeView)
containerView.addSubview(sexView)
@ -473,20 +716,30 @@ class PersonalHeaderView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
backgroungView.snp.makeConstraints { make in
blurEffectView.snp.makeConstraints { make in
make.left.equalTo(self)
make.right.equalTo(self)
make.top.equalTo(self)
make.height.equalTo(261)
}
segmentControl.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.right.equalTo(self).offset(-18)
make.height.equalTo(74)
make.bottom.equalTo(self)
}
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)
make.bottom.equalTo(segmentControl.snp.top)
}
nameLabel.snp.makeConstraints { make in
make.left.equalTo(containerView).offset(18)
make.top.equalTo(containerView).offset(18)
@ -518,9 +771,8 @@ class PersonalHeaderView: UIView {
make.right.equalTo(avatarView.snp.left).offset(-18)
make.top.equalTo(nameLabel.snp.bottom).offset(3)
}
followingControl.snp.makeConstraints { make in
make.left.equalTo(containerView).offset(18)
make.left.equalTo(containerView.snp.left).offset(18)
make.top.equalTo(slognLabel.snp.bottom).offset(15)
}
@ -537,7 +789,7 @@ class PersonalHeaderView: UIView {
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.bottom.equalTo(containerView.snp.bottom).offset(-24)
make.size.equalTo(CGSize.init(width: 76, height: 32))
}
@ -547,6 +799,19 @@ class PersonalHeaderView: UIView {
}
}
func updateButtonState(isSelcted: Bool) {
followingButton.isSelected = isSelcted
followingButton.layer.borderWidth = 1
if isSelcted {
self.followingButton.layer.borderColor = UIColor.separator().cgColor
} else {
self.followingButton.layer.borderColor = UIColor.primaryText().cgColor
}
}
}
@ -595,7 +860,8 @@ class PersonalMenuControl: UIControl {
countLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel.snp.right).offset(6)
make.centerY.equalTo(self)
make.top.equalTo(self)
make.bottom.equalTo(self)
make.right.equalTo(self)
}
}

@ -13,60 +13,298 @@ class PersonalViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let personInfoLikeType: BehaviorRelay<PersonInfoLikeType>
let selection: Driver<IndexPath>
let collectionViewSelection: Driver<IndexPath>
let followingControlTrigger: Driver<Void>
let followersControlTrigger: Driver<Void>
let likeTrigger: Driver<Void>
let followingButtonTrigger: Driver<Void>
let messageTrigger: Driver<Void>
let messageButtonTrigger: Driver<Void>
}
struct Output {
let tableViewItems: BehaviorRelay<[JournalSection]>
let collectionViewItems: BehaviorRelay<[MusicStyleSection]>
let audioItems: BehaviorRelay<[PersonInfoSection]>
let journalItems: BehaviorRelay<[PersonInfoSection]>
let items: BehaviorRelay<[PersonInfoSection]>
let user: BehaviorRelay<User?>
let collectionViewItemSelected: PublishSubject<MusicStyle>
}
}
let tableViewItems = BehaviorRelay<[JournalSection]>.init(value: [])
let collectionViewItemstems = BehaviorRelay<[MusicStyleSection]>.init(value: [])
let collectionViewSelection = PublishSubject<MusicStyle>()
let audioItems = BehaviorRelay<[PersonInfoSection]>.init(value: [])
let journalItems = BehaviorRelay<[PersonInfoSection]>.init(value: [])
let items = BehaviorRelay<[PersonInfoSection]>.init(value: [])
let itemSelected = PublishSubject<PersonInfoItem>()
let user = BehaviorRelay<User?>.init(value: nil)
let personInfoLikeType = BehaviorRelay<PersonInfoLikeType>.init(value: .audio)
let needRefresh: BehaviorRelay<AudioTrack?> = .init(value: nil)
let followingButtonTrigger = PublishRelay<Void>.init()
let messageButtonTrigger = PublishRelay<Void>.init()
var userID: String = ""
init(userID: String, provider: IndieMusicAPI) {
super.init(provider: provider)
self.userID = userID
}
func transform(input: Input) -> Output {
input.viewWillAppear.subscribe { (_) in
self.requestUserData(userID: self.userID)
.subscribe { user in
self.user.accept(user)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
// 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])
input.selection.drive { indexPath in
guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
self.itemSelected.onNext(sectionItem)
}.disposed(by: rx.disposeBag)
input.headerRefresh.withLatestFrom(input.personInfoLikeType)
.flatMapLatest({ [weak self] (personInfoLikeType) -> Observable<[PersonInfoItem]> in
guard let self = self else { return Observable.just([]) }
self.page = 1
if personInfoLikeType == .audio {
return self.requestCollectSongList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
.trackActivity(self.headerLoading)
} else {
return self.requestJournalCollectList(userId: UserDefaults.AccountInfo.string(forKey: .userID) ?? "", page: self.page, size: 10)
.trackActivity(self.headerLoading)
}
})
.subscribe(onNext: { (items) in
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
}).disposed(by: rx.disposeBag)
input.footerRefresh.withLatestFrom(input.personInfoLikeType)
.flatMapLatest({ [weak self] (personInfoLikeType) -> Observable<[PersonInfoItem]> in
guard let self = self else { return Observable.just([]) }
self.page += 1
if personInfoLikeType == .audio {
return self.requestCollectSongList(userId: self.userID, page: self.page, size: 10)
.trackActivity(self.headerLoading)
} else {
return self.requestJournalCollectList(userId: self.userID, page: self.page, size: 10)
.trackActivity(self.headerLoading)
}
})
.subscribe(onNext: { (items) in
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
var arr = self.items.value.first?.items
arr?.append(contentsOf: items)
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: arr ?? [])])
}).disposed(by: rx.disposeBag)
let musicStyleItem = MusicStyle.init(title: "测试", subTitle: "123", backgroundImage: "")
let musicStyleSection = MusicStyleSection.init(items: [musicStyleItem, musicStyleItem, musicStyleItem, musicStyleItem], header: "后摇", headerSub: "Post Rock", content: "总在不经意间获得简单朴素且乐趣其中的感怀,这种感怀的妙处在于它没有试图去提炼出任何的真理,他就像我们恬然的谈话里总夹杂着“那我懂你的意思了”,但是否是真的明白,却不然得知。即", isExpand: false)
collectionViewItemstems.accept([musicStyleSection])
input.personInfoLikeType.subscribe { personInfoLikeType in
print("personInfoLikeType \(personInfoLikeType)")
switch personInfoLikeType.element {
case .audio:
self.requestCollectSongList(userId: self.userID, page: self.page, size: 10)
.subscribe { items in
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
//
case .journal:
self.requestJournalCollectList(userId: self.userID, page: self.page, size: 10)
.subscribe { items in
self.items.accept([PersonInfoSection.audio(personInfoLikeType: self.personInfoLikeType.value, items: items)])
} onError: { error in
}.disposed(by: self.rx.disposeBag)
input.collectionViewSelection.drive { indexPath in
guard let sectionItem = self.collectionViewItemstems.value.first?.items[indexPath.row] else { return }
self.collectionViewSelection.onNext(sectionItem)
default: break
}
}.disposed(by: rx.disposeBag)
let cuttentItems = Observable.merge(audioItems.asObservable(), journalItems.asObservable())
cuttentItems.subscribe { personInfoSection in
self.items.accept(personInfoSection)
}.disposed(by: rx.disposeBag)
return Output.init(tableViewItems: tableViewItems,
collectionViewItems: collectionViewItemstems, collectionViewItemSelected: collectionViewSelection)
followingButtonTrigger
.subscribe { _ in
guard let userID = self.user.value?.id else { return }
switch self.user.value?.relation {
case .notFollowing:
self.requestLike(objectId: userID, collectType: .following)
.flatMap { _ -> Observable<User> in
// requestLikerequestUserData
self.updateUserRelationStatus(relation: .following)
return self.requestUserData(userID: userID)
}
.subscribe(onNext: { user in
self.user.accept(user)
}, onError: { error in
//
}).disposed(by: self.rx.disposeBag)
case .following:
self.requestCancelLike(objectId: userID, collectType: .following)
.flatMap { _ -> Observable<User> in
// requestLikerequestUserData
self.updateUserRelationStatus(relation: .notFollowing)
return self.requestUserData(userID: userID)
}
.subscribe(onNext: { user in
self.user.accept(user)
}, onError: { error in
//
}).disposed(by: self.rx.disposeBag)
case .mutualFollowing:
self.requestCancelLike(objectId: userID, collectType: .following)
.flatMap { _ -> Observable<User> in
// requestLikerequestUserData
self.updateUserRelationStatus(relation: .notFollowing)
return self.requestUserData(userID: userID)
}
.subscribe(onNext: { user in
self.user.accept(user)
}, onError: { error in
//
}).disposed(by: self.rx.disposeBag)
case .blockedByMe:
self.requestCancelLike(objectId: userID, collectType: .addBlockList)
.subscribe { _ in
self.updateUserRelationStatus(relation: .following)
} onError: { error in
}.disposed(by: self.rx.disposeBag)
case .blockedByOther:
break
default: break
}
}.disposed(by: rx.disposeBag)
messageButtonTrigger.subscribe(onNext: { _ in
}).disposed(by: rx.disposeBag)
return Output.init(audioItems: audioItems,
journalItems: journalItems,
items: items,
user: user)
}
func updateUserRelationStatus(relation: UserRelationStatus) {
var currentUser = self.user.value
currentUser?.relation = relation
self.user.accept(currentUser)
}
func requestUserData(userID: String) -> Observable<User> {
return self.provider.otherUserInfo(userID: userID)
.trackActivity(loading)
.trackError(error)
}
func requestCollectSongList(userId: String, page: Int, size: Int) -> Observable<[PersonInfoItem]> {
return self.provider.collectSongList(userId: userId, page: page, size: size)
.trackActivity(loading)
.trackError(error)
.asObservable()
.map { audioTracks in
audioTracks.map { audioTrack in
return PersonInfoItem.audioItem(model: audioTrack)
}
}
}
func requestJournalCollectList(userId: String, page: Int, size: Int) -> Observable<[PersonInfoItem]> {
return self.provider.journalCollectList(userId: userId, page: page, size: size)
.trackActivity(loading)
.trackError(error)
.asObservable()
.map { journals in
journals.map { journal in
return PersonInfoItem.journaItem(model: journal)
}
}
}
func requestLike(objectId: String, collectType: LikeType) -> Observable<Void> {
return self.provider.like(objectId: objectId, collectType: collectType.rawValue)
.trackActivity(loading)
.trackError(error)
}
func requestCancelLike(objectId: String, collectType: LikeType) -> Observable<Void> {
return self.provider.cancelLike(objectId: objectId, collectType: collectType.rawValue)
.trackActivity(loading)
.trackError(error)
}
}

@ -37,6 +37,10 @@ class SearchViewController: ViewController, UIScrollViewDelegate {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let tabbar = self.tabBarController as? HomeTabBarController {
tabbar.showAllBar(true, animated: true)
}
}
override func viewWillDisappear(_ animated: Bool) {

@ -9,6 +9,7 @@ import UIKit
import RxSwift
import RxCocoa
import RxDataSources
import SVProgressHUD
class SettingViewController: TableViewController {
let footerView: SettingViewFooterView = {
@ -33,7 +34,10 @@ class SettingViewController: TableViewController {
tableView.sectionHeaderHeight = 9
tableView.sectionFooterHeight = 0
tableView.register(SettingViewCell.self, forCellReuseIdentifier: "SettingViewCell")
tableView.tableFooterView = footerView
if AuthManager.shared.token?.isValid == true {
tableView.tableFooterView = footerView
}
}
@ -94,9 +98,9 @@ class SettingViewController: TableViewController {
let thanksViewModel = ThanksViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .thanks(viewModel: thanksViewModel), sender: self)
case .version:
let personalViewModel = PersonalViewModel.init(provider: viewModel.provider)
self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
case .version: break
// let personalViewModel = PersonalViewModel.init(provider: viewModel.provider)
// self?.navigator.show(segue: .personal(viewModel: personalViewModel), sender: self)
case .setting(_):
break
}
@ -107,6 +111,44 @@ class SettingViewController: TableViewController {
self.footerView.logoutButton.rx.tap.subscribe { [weak self]_ in
let agree = AlertViewController.init()
agree.titleLabel.text = "雀乐"
agree.cancelButton.setTitle("取消", for: .normal)
agree.confirmButton.setTitle("确定", for: .normal)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
paragraphStyle.lineSpacing = 5
agree.detailTextView.text = "确定退出当前账号吗"
agree.cancelClosures = {[weak self] in
}
agree.confirmClosures = {[weak self] in
guard let self = self else { return }
viewModel.requestLogout().subscribe { [weak self] _ in
self?.tableView.tableFooterView = nil
self?.dismiss(animated: true, completion: {
AuthManager.shared.logout()
SVProgressHUD.showText(withStatus: "退出成功")
self?.navigator.pop(sender: self)
})
} onError: { error in
}.disposed(by: self.rx.disposeBag)
}
self?.present(agree, animated: true)
}.disposed(by: rx.disposeBag)

@ -63,4 +63,11 @@ class SettingViewMdel: ViewModel, ViewModelType {
modelSelected: input.modelSelected)
}
func requestLogout() -> Observable<Void> {
return provider.logout()
.trackActivity(loading)
.trackError(error)
}
}

@ -20,6 +20,8 @@ protocol IndieMusicAPI {
func imageCheckCode(deviceId: String) -> Single<Data>
///
func loginOrRegister(mobile: String, mobileCheckCode: String, deviceId: String, deviceBrand: String) -> Single<String>
/// 退
func logout() -> Single<Void>
/// Token
func autoLogin() -> Single<String>
///
@ -38,7 +40,7 @@ protocol IndieMusicAPI {
///
func carousel() -> Single<[Carousel]>
///
func otherUserInfo(userID: Int) -> Single<User>
func otherUserInfo(userID: String) -> Single<User>
///
func like(objectId: String, collectType: String) -> Single<Void>
///

@ -19,6 +19,7 @@ enum APIConfig {
case countryCode
case imageCheckCode(String)
case login([String: Any])
case logout([String: Any])
case sendsms([String: Any])
case autoLogin([String: Any])
case getUserInfo
@ -27,7 +28,7 @@ enum APIConfig {
case journalList([String: Any])
case journalMusic(String)
case carousel
case otherUserInfo(Int)
case otherUserInfo(String)
case like([String: Any])
case cancelLike([String: Any])
case single(String)
@ -58,6 +59,8 @@ extension APIConfig: TargetType {
return "user/user/imageCheckCode/\(deviceId)"
case .login:
return "user/user/appLogin"
case .logout:
return "user/user/logout"
case .sendsms:
return "user/user/sendsms/"
case .autoLogin:
@ -107,7 +110,7 @@ extension APIConfig: TargetType {
switch self {
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList:
return .get
case .sendsms, .login, .autoLogin, .editAvatar, .like, .checkVersion:
case .sendsms, .login, .autoLogin, .editAvatar, .like, .checkVersion, .logout:
return .post
case .editUserInfo:
return .put
@ -122,7 +125,7 @@ extension APIConfig: TargetType {
case .wechatAccessToken, .journalList, .journalMusic, .countryCode, .sendsms, .imageCheckCode, .login, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .like, .cancelLike, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList:
return URLEncoding.default
case .autoLogin, .editUserInfo, .editAvatar, .checkVersion:
case .autoLogin, .editUserInfo, .editAvatar, .checkVersion, .logout:
return JSONEncoding.default
}
}
@ -134,13 +137,13 @@ extension APIConfig: TargetType {
case .wechatAccessToken, .countryCode, .journalList, .journalMusic, .imageCheckCode, .getUserInfo, .carousel, .otherUserInfo, .single, .journal, .messageList, .collectSongList, .journalCollectList, .followingList, .followerList, .blackList:
return .requestPlain
case .login(let dic), .sendsms(let dic), .autoLogin(let dic), .editUserInfo(let dic), .like(let dic), .cancelLike(let dic), .checkVersion(let dic):
case .login(let dic), .sendsms(let dic), .autoLogin(let dic), .editUserInfo(let dic), .like(let dic), .cancelLike(let dic), .logout(let dic), .checkVersion(let dic):
parameters = dic
return .requestParameters(parameters: parameters, encoding: parameterEncoding)
case .editAvatar(let imageData):
var formData = [MultipartFormData(provider: .data(imageData), name: "file", fileName: Date.init().description, mimeType: "image/png")]
let formData = [MultipartFormData(provider: .data(imageData), name: "file", fileName: Date.init().description, mimeType: "image/png")]
return .uploadMultipart(formData)
}
}
@ -160,12 +163,8 @@ extension APIConfig: TargetType {
var headers : [String : String]? {
switch self {
case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo:
case .autoLogin, .getUserInfo, .journalList, .journalMusic, .otherUserInfo, .like, .cancelLike, .single, .journal, .collectSongList, .journalCollectList, .followingList, .messageList, .followerList, .blackList, .editUserInfo, .logout, .editAvatar:
return ["Authorization": AuthManager.shared.token?.basicToken ?? ""]
case .editAvatar:
return ["Authorization": AuthManager.shared.token?.basicToken ?? "",
]
default:
return nil
}

@ -99,6 +99,10 @@ extension RestApi {
return requestObject(.login(dic), with: "data", type: String.self)
}
func logout() -> Single<Void> {
return requestWithoutMapping(.logout(["": ""])).map { _ in }
}
func autoLogin() -> Single<String> {
return requestObject(.autoLogin(["": ""]), with: "data", type: String.self)
}
@ -162,7 +166,7 @@ extension RestApi {
}
func otherUserInfo(userID: Int) -> Single<User> {
func otherUserInfo(userID: String) -> Single<User> {
return requestObject(.otherUserInfo(userID), with: "data", type: User.self)
}

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "mine_avator_icon.svg",
"filename" : "mine_avatar_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},

@ -0,0 +1,4 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="32" cy="32" r="32" fill="#F2F3F7" style="fill:#F2F3F7;fill:color(display-p3 0.9500 0.9540 0.9700);fill-opacity:1;"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.8393 21.9951C31.8393 19.4743 33.3103 17.3561 35.6212 16.7475C36.0559 16.6329 36.5108 16.572 36.9807 16.572C38.9172 16.572 40.6054 17.6104 41.5604 19.1534C41.589 19.1993 42.8997 21.1443 42.8997 24.0457C42.8997 24.8639 42.797 25.6587 42.6056 26.4162C42.5981 26.4526 42.5945 26.49 42.5945 26.5291C42.5945 26.8398 42.8441 27.0917 43.1521 27.0917C43.3233 27.0917 43.4759 27.0143 43.5776 26.8911C43.5915 26.8762 43.6035 26.8604 43.6147 26.8435C44.6338 25.4982 45.2387 23.8181 45.2387 21.9951C45.2387 17.5795 41.69 14 37.3126 14C37.2747 14 37.2368 14 37.1989 14.0018C36.2084 14.015 35.2612 14.2122 34.3902 14.5606C31.4586 15.7336 29.3867 18.6186 29.3867 21.9951C29.3867 25.6954 32.2769 29.535 34.3097 31.5659C34.4152 31.6705 34.5206 31.7741 34.6287 31.8776C35.1171 32.346 35.6202 32.7825 36.1344 33.1874C36.2945 33.3133 36.4563 33.4364 36.6182 33.5569C36.841 33.7248 36.9843 33.9925 36.9843 34.2938C36.9843 34.8681 36.4772 35.2629 35.9264 35.2025C33.8325 34.9068 31.8708 34.194 30.1266 33.1492C29.8344 32.9737 29.5477 32.79 29.2683 32.595H29.2674C27.4788 31.3589 25.9555 29.759 24.8059 27.9005C23.3465 25.5421 22.4855 22.7685 22.4374 19.7944C22.4365 19.7076 22.4356 19.6198 22.4356 19.5323C22.4356 18.5209 22.5299 17.5311 22.7094 16.572C21.1786 18.4547 20.0688 20.6993 19.5279 23.1594C19.2836 24.2677 19.1551 25.4209 19.1551 26.6029C19.1551 29.4324 19.8912 32.0885 21.1805 34.3863C19.4723 33.1724 18.0147 31.623 16.9032 29.8317C16.4943 29.174 16.1318 28.4845 15.822 27.7662C15.7674 28.2999 15.7396 28.8419 15.7396 29.3904C15.7396 31.4569 16.1327 33.4309 16.8466 35.2407C17.5154 36.934 18.4652 38.4827 19.6379 39.8298C19.1431 39.7701 18.6418 39.7338 18.1341 39.7216C18.0074 39.7188 17.8806 39.7169 17.754 39.7169C16.4601 39.7169 15.2023 39.8746 14 40.1731C16.867 40.9391 19.4112 42.5045 21.3867 44.6176C24.2113 47.6058 28.3527 50 33.8944 50C39.0674 50 48 46.85 48 39.8737C48 37.253 46.6931 34.4117 42.9115 32.6973C36.6286 29.8491 31.8393 25.8574 31.8393 21.9951Z" fill="black" fill-opacity="0.95" style="fill:black;fill-opacity:0.95;"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -1,4 +0,0 @@
<svg width="68" height="68" viewBox="0 0 68 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="34" cy="34" r="33" fill="#F2F3F7" stroke="white" style="fill:#F2F3F7;fill:color(display-p3 0.9500 0.9540 0.9700);fill-opacity:1;stroke:white;stroke-opacity:1;" stroke-width="2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.8393 23.9951C33.8393 21.4743 35.3103 19.3561 37.6212 18.7475C38.0559 18.6329 38.5108 18.572 38.9807 18.572C40.9172 18.572 42.6054 19.6104 43.5604 21.1534C43.589 21.1993 44.8997 23.1443 44.8997 26.0457C44.8997 26.8639 44.797 27.6587 44.6056 28.4162C44.5981 28.4526 44.5945 28.49 44.5945 28.5291C44.5945 28.8398 44.8441 29.0917 45.1521 29.0917C45.3233 29.0917 45.4759 29.0143 45.5776 28.8911C45.5915 28.8762 45.6035 28.8604 45.6147 28.8435C46.6338 27.4982 47.2387 25.8181 47.2387 23.9951C47.2387 19.5795 43.69 16 39.3126 16C39.2747 16 39.2368 16 39.1989 16.0018C38.2084 16.015 37.2612 16.2122 36.3902 16.5606C33.4586 17.7336 31.3867 20.6186 31.3867 23.9951C31.3867 27.6954 34.2769 31.535 36.3097 33.5659C36.4152 33.6705 36.5206 33.7741 36.6287 33.8776C37.1171 34.346 37.6202 34.7825 38.1344 35.1874C38.2945 35.3133 38.4563 35.4364 38.6182 35.5569C38.841 35.7248 38.9843 35.9925 38.9843 36.2938C38.9843 36.8681 38.4772 37.2629 37.9264 37.2025C35.8325 36.9068 33.8708 36.194 32.1266 35.1492C31.8344 34.9737 31.5477 34.79 31.2683 34.595H31.2674C29.4788 33.3589 27.9555 31.759 26.8059 29.9005C25.3465 27.5421 24.4855 24.7685 24.4374 21.7944C24.4365 21.7076 24.4356 21.6198 24.4356 21.5323C24.4356 20.5209 24.5299 19.5311 24.7094 18.572C23.1786 20.4547 22.0688 22.6993 21.5279 25.1594C21.2836 26.2677 21.1551 27.4209 21.1551 28.6029C21.1551 31.4324 21.8912 34.0885 23.1805 36.3863C21.4723 35.1724 20.0147 33.623 18.9032 31.8317C18.4943 31.174 18.1318 30.4845 17.822 29.7662C17.7674 30.2999 17.7396 30.8419 17.7396 31.3904C17.7396 33.4569 18.1327 35.4309 18.8466 37.2407C19.5154 38.934 20.4652 40.4827 21.6379 41.8298C21.1431 41.7701 20.6418 41.7338 20.1341 41.7216C20.0074 41.7188 19.8806 41.7169 19.754 41.7169C18.4601 41.7169 17.2023 41.8746 16 42.1731C18.867 42.9391 21.4112 44.5045 23.3867 46.6176C26.2113 49.6058 30.3527 52 35.8944 52C41.0674 52 50 48.85 50 41.8737C50 39.253 48.6931 36.4117 44.9115 34.6973C38.6286 31.8491 33.8393 27.8574 33.8393 23.9951Z" fill="black" fill-opacity="0.95" style="fill:black;fill-opacity:0.95;"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "mine_female.icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1214 4.12891C17.297 4.12891 19.8713 6.70327 19.8714 9.8789V9.87891C19.8714 11.4665 19.2269 12.9051 18.1872 13.9448C17.1475 14.9845 15.709 15.6289 14.1214 15.6289C12.8039 15.6289 11.59 15.1858 10.6204 14.4406L9.52519 15.5358L11.1165 17.1271C11.4094 17.42 11.4094 17.8949 11.1165 18.1878C10.8236 18.4807 10.3487 18.4807 10.0558 18.1878L8.46454 16.5965L6.8736 18.1875C6.58071 18.4803 6.10583 18.4804 5.81294 18.1875C5.52004 17.8946 5.52004 17.4197 5.81293 17.1268L7.40388 15.5358L5.81318 13.9451C5.52028 13.6522 5.52028 13.1774 5.81318 12.8845C6.10607 12.5916 6.58094 12.5916 6.87384 12.8845L8.46453 14.4752L9.5597 13.38C8.81442 12.4104 8.37134 11.1964 8.37135 9.87891C8.37134 6.70327 10.9457 4.12892 14.1214 4.12891ZM14.1213 5.62891C11.7741 5.62892 9.87134 7.53171 9.87135 9.8789L9.87135 9.87891C9.87133 12.2261 11.7741 14.1289 14.1214 14.1289H14.1214C15.2951 14.1289 16.3566 13.654 17.1266 12.8841C17.8965 12.1142 18.3714 11.0527 18.3714 9.87891C18.3713 7.53169 16.4685 5.62891 14.1213 5.62891Z" fill="#B44343" style="fill:#B44343;fill:color(display-p3 0.7059 0.2627 0.2627);fill-opacity:1;"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "mine_male.icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.12865 13.8683C4.12868 17.0439 6.70303 19.6183 9.87865 19.6183L9.87866 19.6183C13.0543 19.6183 15.6287 17.0439 15.6287 13.8683C15.6287 12.5508 15.1856 11.3368 14.4403 10.3672L17.25 7.55747L17.25 9.74695C17.25 10.1612 17.5858 10.4969 18 10.4969C18.4142 10.4969 18.75 10.1612 18.75 9.74694L18.75 5.74695C18.75 5.33274 18.4142 4.99695 18 4.99695L14 4.99695C13.5858 4.99695 13.25 5.33274 13.25 5.74695C13.25 6.16116 13.5858 6.49695 14 6.49695L16.1892 6.49695L13.3796 9.30658C12.41 8.56134 11.1961 8.11828 9.87865 8.11828C8.29103 8.11825 6.8525 8.76271 5.81279 9.80242C4.77307 10.8421 4.12865 12.2807 4.12865 13.8683L4.12865 13.8683ZM14.1286 13.8683C14.1287 16.2155 12.2259 18.1183 9.87865 18.1183C7.53147 18.1183 5.62867 16.2155 5.62865 13.8683C5.62865 12.6945 6.10353 11.633 6.87345 10.8631C7.64337 10.0932 8.70485 9.61826 9.87864 9.61828H9.87865C12.2259 9.61828 14.1287 11.5211 14.1287 13.8683L14.1286 13.8683Z" fill="black" fill-opacity="0.95" style="fill:black;fill-opacity:0.95;"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -30,6 +30,7 @@ target 'IndieMusic' do
pod 'PINCache'
pod 'DeviceKit', '~> 5.2'
pod 'FSPopoverView'
pod 'JXPagingView/Paging'
pod 'NSObject+Rx'

Loading…
Cancel
Save