Docking journal interface

dev
wenlei 1 year ago
parent 858e76cf34
commit 118a3cf6e6

@ -15,6 +15,7 @@
770228EB2B55174D00E07F7A /* InternationalNumberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228EA2B55174D00E07F7A /* InternationalNumberViewModel.swift */; };
770228ED2B55284F00E07F7A /* Login.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228EC2B55284F00E07F7A /* Login.swift */; };
770228EF2B56142E00E07F7A /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228EE2B56142E00E07F7A /* User.swift */; };
770228F12B57AD2C00E07F7A /* RefreshLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 770228F02B57AD2C00E07F7A /* RefreshLoadingView.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 */; };
@ -216,6 +217,7 @@
770228EA2B55174D00E07F7A /* InternationalNumberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternationalNumberViewModel.swift; sourceTree = "<group>"; };
770228EC2B55284F00E07F7A /* Login.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Login.swift; sourceTree = "<group>"; };
770228EE2B56142E00E07F7A /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
770228F02B57AD2C00E07F7A /* RefreshLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshLoadingView.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>"; };
@ -565,6 +567,7 @@
77C9B9D22B4B9B180006C83F /* BorderLabel.swift */,
77A659CE2B4FDC4100B408C3 /* PullToDismissable.swift */,
77A659D02B4FDC5200B408C3 /* PullToDismissTransition.swift */,
770228F02B57AD2C00E07F7A /* RefreshLoadingView.swift */,
);
path = Common;
sourceTree = "<group>";
@ -1208,6 +1211,7 @@
77FA0B502B0EFEF400404C5E /* PhoneCodeView.swift in Sources */,
778B8A812AF8ECE50034AFD4 /* HomeTabBarViewModel.swift in Sources */,
77FA0B4A2B0EE8F000404C5E /* PhoneCodeController.swift in Sources */,
770228F12B57AD2C00E07F7A /* RefreshLoadingView.swift in Sources */,
77C9B9D92B4BBFA60006C83F /* Following.swift in Sources */,
77C9B9DB2B4BC40F0006C83F /* FollowersViewController.swift in Sources */,
778B8AC02AF8ED280034AFD4 /* ViewController.swift in Sources */,

@ -47,7 +47,7 @@ class PresentationController: UIPresentationController {
}
}
class CardPresentationController: PresentationController {
class CardPresentationController: PresentationController, UIGestureRecognizerDelegate {
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
@ -127,16 +127,22 @@ class CardPresentationController: PresentationController {
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .audioMore:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .share:
@ -144,32 +150,40 @@ class CardPresentationController: PresentationController {
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .timing:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .photoConfirm:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .audioTrackList:
coordinator.animate(alongsideTransition: { [weak self] _ in
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
case .tips(let isTapDismiss):
@ -178,8 +192,10 @@ class CardPresentationController: PresentationController {
self?.containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.8)
}, completion: nil)
let tapGes = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
self.containerView?.addGestureRecognizer(tapGes)
let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(dismiss))
tapGesture.delegate = self
self.containerView?.addGestureRecognizer(tapGesture)
}
}
@ -197,6 +213,19 @@ class CardPresentationController: PresentationController {
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
let touchPoint = touch.location(in: containerView)
//
if let presentedView = presentedView, presentedView.frame.contains(touchPoint) {
//
return false
} else {
//
return true
}
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()

@ -0,0 +1,140 @@
//
// RefreshLoadingView.swift
// IndieMusic
//
// Created by WenLei on 2024/1/17.
//
import UIKit
import MJRefresh
import Lottie
class RefreshLoadingView: UIView {
private let animationSize: CGFloat = 60.0
private var animationView: LottieAnimationView!
override init(frame: CGRect) {
super.init(frame: frame)
self.animationView = createAnimationView()
addSubview(animationView)
layoutFrame()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createAnimationView() -> LottieAnimationView {
let animationView = LottieAnimationView()
animationView.backgroundColor = .clear
animationView.frame = CGRect(x: 0.0, y: 0.0, width: animationSize, height: animationSize)
animationView.loopMode = .loop
animationView.animation = LottieAnimation.named("loading_header")
animationView.backgroundColor = .red
return animationView
}
private func layoutFrame() {
animationView.center = center
}
// MARK: - Display
func displayIndicator(precent: CGFloat) {
// Implement your logic here
}
func startAnimation() {
animationView.play()
}
func stopAnimation() {
animationView.stop()
}
}
class RefreshHeader: MJRefreshStateHeader {
let gifLoadingView: RefreshLoadingView = {
let refreshLoadingView = RefreshLoadingView.init()
return refreshLoadingView
}()
override init(frame: CGRect) {
super.init(frame: frame)
lastUpdatedTimeLabel?.isHidden = true
stateLabel?.isHidden = true
addSubview(self.gifLoadingView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var state: MJRefreshState {
didSet {
switch state {
case .idle:
if oldValue == .refreshing {
gifLoadingView.alpha = 1.0
if state != .idle { return }
gifLoadingView.alpha = 1.0
gifLoadingView.stopAnimation()
} else {
gifLoadingView.stopAnimation()
}
case .pulling:
gifLoadingView.stopAnimation()
case .refreshing:
gifLoadingView.startAnimation()
default:
break
}
}
}
override func prepare() {
super.prepare()
mj_h = 70.0
}
override func placeSubviews() {
super.placeSubviews()
let centerX = mj_w * 0.5
let centerY = mj_h * 0.5
gifLoadingView.center = CGPoint(x: centerX, y: centerY)
}
override func scrollViewContentOffsetDidChange(_ change: [AnyHashable : Any]?) {
super.scrollViewContentOffsetDidChange(change)
if let change = change as? [String: CGPoint],
let new = change["new"] {
let percent = -new.y / mj_h
gifLoadingView.displayIndicator(precent: percent)
}
}
override func scrollViewContentSizeDidChange(_ change: [AnyHashable : Any]?) {
super.scrollViewContentSizeDidChange(change)
// Additional implementation if needed
}
override func scrollViewPanStateDidChange(_ change: [AnyHashable : Any]?) {
super.scrollViewPanStateDidChange(change)
// Additional implementation if needed
}
func backInitState() {
// Implement the initialization state logic here
}
}

@ -48,6 +48,34 @@ class TableViewController: ViewController, UIScrollViewDelegate {
make.edges.equalTo(view)
}
}
override func updateUI() {
super.updateUI()
}
override func bindViewModel() {
super.bindViewModel()
viewModel?.headerLoading.asObservable().subscribe(onNext: { [weak self] headerLoading in
self?.isHeaderLoading.accept((.refresh, headerLoading))
}).disposed(by: rx.disposeBag)
viewModel?.footerLoading.asObservable().subscribe(onNext: { [weak self] footerLoading in
self?.isFooterLoading.accept((.loadMore, footerLoading))
}).disposed(by: rx.disposeBag)
let updateEmptyDataSet = Observable.of(isLoading.mapToVoid().asObservable(), emptyDataSetImageTintColor.mapToVoid(), languageChanged.asObservable()).merge()
updateEmptyDataSet.subscribe(onNext: { [weak self] () in
self?.tableView.reloadEmptyDataSet()
}).disposed(by: rx.disposeBag)
tableView.rx.refresh.subscribe { refreshType in
switch refreshType.element {
@ -69,23 +97,7 @@ class TableViewController: ViewController, UIScrollViewDelegate {
}
override func updateUI() {
super.updateUI()
}
override func bindViewModel() {
super.bindViewModel()
// viewModel?.headerLoading.asObservable().bind(to: isHeaderLoading).disposed(by: rx.disposeBag)
// viewModel?.footerLoading.asObservable().bind(to: isFooterLoading).disposed(by: rx.disposeBag)
// let updateEmptyDataSet = Observable.of(isLoading.mapToVoid().asObservable(), emptyDataSetImageTintColor.mapToVoid(), languageChanged.asObservable()).merge()
// updateEmptyDataSet.subscribe(onNext: { [weak self] () in
// self?.tableView.reloadEmptyDataSet()
// }).disposed(by: rx.disposeBag)
}
@ -119,7 +131,7 @@ extension TableViewController: UITableViewDelegate {
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
print("test")
print("scrollViewWillEndDragging")
}
}

@ -144,7 +144,8 @@ class ViewController: UIViewController, Navigatable {
}
func makeUI() {
view.backgroundColor = .white
updateUI()
}
@ -163,7 +164,6 @@ class ViewController: UIViewController, Navigatable {
}
func updateUI() {
view.backgroundColor = .white
}

@ -23,7 +23,7 @@ extension Reactive where Base: UITableView {
let source = Observable<RefreshType>.create { observer in
if base.mj_header == nil {
base.mj_header = MJRefreshNormalHeader.init(refreshingBlock: {
base.mj_header = RefreshHeader.init(refreshingBlock: {
observer.on(.next(.refresh))
})
}

@ -12,11 +12,20 @@ import RxDataSources
struct HomeJournal: Codable {
let vol: String?
// let vol: String?
// let title: String?
// let subTitle: String?
// let comment: String?
// let commentCount: Int?
let id: String?
let journalNo: String?
let title: String?
let subTitle: String?
let comment: String?
let commentCount: Int?
let content: String?
let image: String?
let commentArray: [String]?
}
enum HomeSectionModel {

@ -58,9 +58,9 @@ class HomeTabBarViewModel: ViewModel, ViewModelType {
func renewalToken() {
self.provider.autoLogin().subscribe { login in
self.provider.autoLogin().subscribe { token in
print("autoLogin: success")
AuthManager.setToken(token: Token.init(isValid: true, basicToken: login.token))
AuthManager.setToken(token: Token.init(isValid: true, basicToken: token))
} onFailure: { error in
print("autoLogin: error")

@ -109,7 +109,6 @@ class HomeViewCell: UITableViewCell {
var titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.backgroundColor = .blue
return titleImageView
}()
@ -119,7 +118,6 @@ class HomeViewCell: UITableViewCell {
titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .medium)
titleLabel.textColor = .white
titleLabel.numberOfLines = 2
titleLabel.textColor = .primaryText()
return titleLabel
}()
@ -173,10 +171,16 @@ class HomeViewCell: UITableViewCell {
var homeJournal: HomeJournal? {
didSet {
titleLabel.text = homeJournal?.title
subTitleLabel.text = homeJournal?.subTitle
commentLabel.text = homeJournal?.comment
guard let homeJournal = homeJournal else { return }
titleLabel.text = homeJournal.title
subTitleLabel.text = homeJournal.content ?? "test"
commentLabel.text = homeJournal.content ?? "test"
homeNumberView.numberLabel.text = homeJournal.journalNo
titleImageView.kf.setImage(with: URL.init(string: homeJournal.image ?? ""))
multiUserAvatarView.homeJournal = homeJournal
}
}
@ -293,12 +297,15 @@ class HomeViewCell: UITableViewCell {
class HomeSectionView: UIView {
var titleLabel: UILabel = {
let titleLabel = UILabel.init()
titleLabel.font = UIFont.systemFont(ofSize: 28)
titleLabel.text = "最近期刊"
return titleLabel
var titleButton: UIButton = {
let titleButton = UIButton.init()
titleButton.titleLabel?.font = UIFont.systemFont(ofSize: 28)
// titleLabel.text = ""
titleButton.setImage(UIImage.init(named: "home_title_btn"), for: .normal)
titleButton.setContentCompressionResistancePriority(.required, for: .horizontal)
titleButton.setContentHuggingPriority(.required, for: .horizontal)
return titleButton
}()
let theFMButton: UIButton = {
@ -333,7 +340,7 @@ class HomeSectionView: UIView {
func makeUI() {
backgroundColor = .white
addSubview(titleLabel)
addSubview(titleButton)
addSubview(theFMButton)
addSubview(filterButton)
@ -371,10 +378,12 @@ class HomeSectionView: UIView {
make.centerY.equalTo(self)
}
titleLabel.snp.makeConstraints { make in
titleButton.snp.makeConstraints { make in
make.left.equalTo(self).offset(18)
make.centerY.equalTo(self)
make.right.equalTo(theFMButton.snp.left).offset(-10)
// make.top.equalTo(self).offset(18)
// make.bottom.equalTo(self).offset(-12)
// make.right.equalTo(theFMButton.snp.left).offset(-10)
}
}

@ -70,9 +70,12 @@ class HomeViewController: TableViewController {
override func bindViewModel() {
super.bindViewModel()
let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
guard let viewModel = viewModel as? HomeViewModel else { return }
let input = HomeViewModel.Input.init(viewWillAppear: rx.viewWillAppear,
headerRefresh: headerRefreshTrigger,
footerRefresh: footerRefreshTrigger,
selection: tableView.rx.itemSelected.asDriver())
let output = viewModel.transform(input: input)
@ -186,6 +189,10 @@ extension HomeViewController {
return 338
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 64
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = HomeSectionView.init()

@ -13,6 +13,8 @@ class HomeViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let headerRefresh: Observable<Void>
let footerRefresh: Observable<Void>
let selection: Driver<IndexPath>
}
@ -25,33 +27,78 @@ class HomeViewModel: ViewModel, ViewModelType {
}
let itemSelected = PublishSubject<HomeSectionItem>()
let items = BehaviorRelay<[HomeSectionModel]>.init(value: [])
func transform(input: Input) -> Output {
let elements = BehaviorRelay<[HomeSectionModel]>(value: [])
input.viewWillAppear.subscribe { (_) in
}.disposed(by: rx.disposeBag)
input.headerRefresh.flatMapLatest({ [weak self] () -> Observable<[HomeSectionModel]> in
guard let self = self else { return Observable.just([]) }
self.page = 0
return self.request(style: "all", language: "", pageNum: self.page, pageSize: 10)
.trackActivity(self.headerLoading)
})
.subscribe(onNext: { (items) in
elements.accept(items)
}).disposed(by: rx.disposeBag)
input.footerRefresh.flatMapLatest({ [weak self] () -> Observable<[HomeSectionModel]> in
guard let self = self else { return Observable.just([]) }
self.page += 1
return self.request(style: "all", language: "", pageNum: self.page, pageSize: 10)
.trackActivity(self.footerLoading)
})
.subscribe(onNext: { (items) in
elements.accept(elements.value + items)
}).disposed(by: rx.disposeBag)
let item = HomeSectionItem.journalDetil(model: HomeJournal.init(vol: "123", title: "321", subTitle: "44444", comment: "234243", commentCount: 98))
var sections: [HomeSectionModel] = [.journal(title: "", items: [item, item])]
items.accept(sections)
input.selection.drive { indexPath in
guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
self.itemSelected.onNext(sectionItem)
// guard let sectionItem = self.items.value.first?.items[indexPath.row] else { return }
// self.itemSelected.onNext(sectionItem)
}.disposed(by: rx.disposeBag)
// self.provider.journalList(style: "all", language: "", pageNum: 0, pageSize: 10)
// .subscribe { [weak self] array in
// guard array.count > 0 else { return }
//
// let items = array.map { homeJournal in
// return HomeSectionItem.journalDetil(model: homeJournal)
// }
//
// self?.items.accept([.journal(title: "", items: items)])
// } onFailure: { error in
//
// }.disposed(by: rx.disposeBag)
return Output.init(items: items,
return Output.init(items: elements,
selection: input.selection,
itemSelected: itemSelected)
}
func request(style: String, language: String, pageNum: Int, pageSize: Int) -> Observable<[HomeSectionModel]> {
return self.provider.journalList(style: "all", language: "", pageNum: 0, pageSize: 10)
.trackActivity(loading)
.trackError(error)
.map({ (array) -> [HomeSectionModel] in
let items = array.map { homeJournal in
return HomeSectionItem.journalDetil(model: homeJournal)
}
let section = [HomeSectionModel.journal(title: "", items: items)]
return section
})
}
}

@ -12,7 +12,49 @@ class MultiUserAvatarView: UIView {
var avatarViewArray = [UIImageView]()
var homeJournal: HomeJournal? {
didSet {
for (index, avatarView) in avatarViewArray.enumerated() {
avatarView.removeFromSuperview()
if index <= avatarViewArray.count - 1 {
avatarViewArray.remove(at: index)
}
}
guard let array = homeJournal?.commentArray else {
return
}
for (index, comment) in array.enumerated() {
let imageView = UIImageView.init()
imageView.layer.cornerRadius = 10
imageView.layer.borderWidth = 2
imageView.backgroundColor = .red
imageView.layer.borderColor = UIColor.white.cgColor
avatarViewArray.append(imageView)
addSubview(imageView)
imageView.snp.makeConstraints { make in
make.left.equalTo(self).offset(index * 10)
make.size.equalTo(CGSize.init(width: 20, height: 20))
make.top.equalTo(self)
make.bottom.equalTo(self)
if index == array.count - 1 {
make.right.equalTo(self)
}
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)

@ -11,7 +11,7 @@ class JournalDetailHeaderView: UIView {
var titleImageView: UIImageView = {
let titleImageView = UIImageView.init()
titleImageView.image = UIImage.init(named: "")
// titleImageView.image = UIImage.init(named: "")
return titleImageView
}()

@ -7,6 +7,8 @@
import UIKit
import SVProgressHUD
import RxSwift
import RxCocoa
class LoginViewController: ViewController {
@ -99,17 +101,16 @@ class LoginViewController: ViewController {
guard let viewModel = viewModel as? LoginViewModel else { return }
let agreementBoxSelected = loginAgreementView.announceCheckBoxView.rx.tap.map{!self.loginAgreementView.announceCheckBoxView.isSelected}.asDriver(onErrorJustReturn: false)
// let agreementBoxSelected = loginAgreementView.announceCheckBoxView.rx.tap.map{!self.loginAgreementView.announceCheckBoxView.isSelected}.asDriver(onErrorJustReturn: false)
let login = PublishRelay<Void>.init()
let codeButtonTrigger = Observable.merge(login.asObservable(), loginButton.rx.tap.asObservable())
let input = LoginViewModel.Input.init(numberButtonTrigger: phoneView.codeButton.rx.tap.asDriver(),
codeButtonTrigger: loginButton.rx.tap.asDriver(),
codeButtonTrigger: codeButtonTrigger.asDriver(onErrorDriveWith: .just(())),
wechatButtonTrigger: otherLoginView.weChatButton.rx.tap.asDriver(),
appleButtonTrigger: otherLoginView.appleButton.rx.tap.asDriver(),
notiWecahtResp: NotificationCenter.default.rx.notification(.notiWecahtResp))
let output = viewModel.transform(input: input)
_ = phoneView.phoneTextField.rx.textInput <-> viewModel.phone
@ -143,6 +144,34 @@ class LoginViewController: ViewController {
viewModel.isAgree.subscribe { isAgree in
print("isAgree : \(isAgree)")
}.disposed(by: rx.disposeBag)
viewModel.toAlearView.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.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])
])
agree.cancelClosures = {[weak self] in
}
agree.confirmClosures = {[weak self] in
self?.dismiss(animated: true)
viewModel.isAgree.accept(true)
login.accept(())
}
self?.present(agree, animated: true)
}.disposed(by: rx.disposeBag)
@ -152,6 +181,7 @@ class LoginViewController: ViewController {
print("isPhoneValid : \(isPhoneValid)")
}.disposed(by: rx.disposeBag)

@ -24,6 +24,7 @@ class LoginViewModel: ViewModel, ViewModelType {
struct Output {
let number: BehaviorRelay<String>
let isPhoneValid: Driver<Bool>
let toAlearView: PublishRelay<Void>
let toLogin: Driver<Bool>
let errorMessage: Driver<String>
@ -32,6 +33,7 @@ class LoginViewModel: ViewModel, ViewModelType {
let number = BehaviorRelay(value: "+86")
let phone = BehaviorRelay(value: "")
let isAgree = BehaviorRelay(value: false)
let toAlearView = PublishRelay<Void>.init()
func transform(input: Input) -> Output {
@ -53,15 +55,7 @@ class LoginViewModel: ViewModel, ViewModelType {
let canLogin = Driver.combineLatest(phoneValid, isAgree.asDriver()) { $0 && $1 }
let loginTrigger = input.codeButtonTrigger.withLatestFrom(canLogin)
let toLogin = loginTrigger.flatMapLatest { [weak self] canLogin -> Driver<Bool> in
guard let self = self, canLogin else { return Driver.just(false) }
return self.requestSMSData().asDriver(onErrorJustReturn: false)
}
let errorMessage = input.codeButtonTrigger
.withLatestFrom(Driver.combineLatest(phoneValid, isAgree.asDriver()))
.flatMapLatest { isPhoneValid, isAgreed -> Driver<String> in
@ -69,13 +63,26 @@ class LoginViewModel: ViewModel, ViewModelType {
return Driver.just("电话号码无效")
}
if !isAgreed {
return Driver.just("请同意用户协议")
self.toAlearView.accept(())
return Driver.just("")
}
return Driver.just("")
}
let loginTrigger = input.codeButtonTrigger.withLatestFrom(canLogin)
let toLogin = loginTrigger.flatMapLatest { [weak self] canLogin -> Driver<Bool> in
guard let self = self, canLogin else { return Driver.just(false) }
return self.requestSMSData().asDriver(onErrorJustReturn: false)
}
input.wechatButtonTrigger.drive { _ in
@ -99,22 +106,13 @@ class LoginViewModel: ViewModel, ViewModelType {
}.disposed(by: rx.disposeBag)
// input.agreementBoxChecked.drive { isSelected in
// print("agreementBoxChecked: \(isSelected)")
//
// }.disposed(by: rx.disposeBag)
// phone.subscribe { phone in
// print("phone: \(phone)")
//
// }.disposed(by: rx.disposeBag)
return Output.init(number: number,
isPhoneValid: phoneValid, toLogin: toLogin, errorMessage: errorMessage)
isPhoneValid: phoneValid,
toAlearView: toAlearView,
toLogin: toLogin,
errorMessage: errorMessage)
}

@ -117,30 +117,9 @@ class PhoneCodeController: ViewController {
output.tipsText.drive(self.tipsLabel.rx.text).disposed(by: rx.disposeBag)
output.toAlertView.subscribe { [weak self] _ in
let agree = AlertViewController.init()
agree.titleLabel.text = "阅读并同意以下协议"
agree.cancelButton.setTitle("取消", for: .normal)
agree.confirmButton.setTitle("已阅读并同意", for: .normal)
output.toPopRoot.subscribe { [weak self] _ in
self?.navigator.pop(sender: self, toRoot: true)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
paragraphStyle.lineSpacing = 5
agree.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])
])
agree.cancelClosures = {[weak self] in
AuthManager.removeToken()
}
agree.confirmClosures = {[weak self] in
self?.dismiss(animated: true)
self?.navigator.pop(sender: self, toRoot: true)
}
self?.present(agree, animated: true)
}.disposed(by: rx.disposeBag)
}

@ -21,7 +21,7 @@ class PhoneCodeViewModel: ViewModel, ViewModelType {
let countdownText: Observable<String>
let isButtonEnabled: Driver<Bool>
let tipsText: Driver<String>
let toAlertView: PublishRelay<Void>
let toPopRoot: PublishRelay<Void>
let errorMessage: Driver<String>
}
@ -29,7 +29,7 @@ class PhoneCodeViewModel: ViewModel, ViewModelType {
private var tipsText = "验证码已发送至 "
let toAlertView = PublishRelay<Void>.init()
let toPopRoot = PublishRelay<Void>.init()
var phone: String
@ -92,12 +92,11 @@ class PhoneCodeViewModel: ViewModel, ViewModelType {
print("text: \(text) isFinish: \(isFinish) ")
self.requestLoginData(mobileCheckCode: text).subscribe(onNext: { [weak self] login in
self.requestLoginData(mobileCheckCode: text).subscribe(onNext: { [weak self] token in
AuthManager.setToken(token: Token.init(isValid: true, basicToken: login.token))
// AuthManager.tokenValidated()
AuthManager.setToken(token: Token.init(isValid: true, basicToken: token))
self?.toAlertView.accept(())
self?.toPopRoot.accept(())
}, onError: { error in
if case HTTPServiceError.errorJudge(let err) = error {
SVProgressHUD.showText(withStatus: err.message)
@ -111,12 +110,12 @@ class PhoneCodeViewModel: ViewModel, ViewModelType {
return Output(countdownText: countdownText,
isButtonEnabled: isButtonEnabled,
tipsText: tipsText,
toAlertView: toAlertView,
toPopRoot: toPopRoot,
errorMessage: errorMessages.asDriver(onErrorJustReturn: ""))
}
func requestLoginData(mobileCheckCode: String) -> Observable<Login> {
func requestLoginData(mobileCheckCode: String) -> Observable<String> {
let mobileCheckCode = mobileCheckCode
let phone = phone
return provider.loginOrRegister(mobile: phone, mobileCheckCode: mobileCheckCode)

@ -13,6 +13,7 @@ class EditInfoHeaderView: UIView {
avatorView.layer.cornerRadius = 45
avatorView.layer.masksToBounds = true
avatorView.backgroundColor = .red
avatorView.isUserInteractionEnabled = true
return avatorView
}()

@ -9,6 +9,9 @@ import UIKit
import RxSwift
import RxCocoa
import RxDataSources
import RxGesture
import PhotosUI
import RxModal
class EditInfoViewController: TableViewController {
let headerView: EditInfoHeaderView = {
@ -28,8 +31,6 @@ class EditInfoViewController: TableViewController {
view.backgroundColor = .white
navigationItem.title = "编辑资料"
tableView.mj_footer = nil
tableView.mj_header = nil
tableView.register(SettingViewCell.self, forCellReuseIdentifier: "SettingViewCell")
tableView.tableHeaderView = headerView
@ -66,6 +67,42 @@ class EditInfoViewController: TableViewController {
}.disposed(by: rx.disposeBag)
headerView.avatorView.rx.tapGesture().when(.recognized)
.subscribe { gestureRecognizer in
// self.navigator.show(segue: .photoConfirm, sender: self, transition: .navigationPresent(type: .photoConfirm))
// let configuration = PHPickerConfiguration()
// configuration.filter = .images //
RxModal.photoPicker(presenter: .viewController(self)) { config in
config.filter = .images
config.selectionLimit = 1
}.subscribe { results in
let itemProviders = results.map(\.itemProvider)
for item in itemProviders {
if item.canLoadObject(ofClass: UIImage.self) {
item.loadObject(ofClass: UIImage.self) { (image, error) in
DispatchQueue.main.async {
if let image = image as? UIImage {
self.headerView.avatorView.image = image
}
}
}
}
}
} onFailure: { error in
}.disposed(by: self.rx.disposeBag)
}.disposed(by: rx.disposeBag)
tableView.mj_footer = nil
tableView.mj_header = nil
}
}

@ -16,6 +16,7 @@ class EditInfoViewModel: ViewModel, ViewModelType {
struct Input {
let viewWillAppear: ControlEvent<Bool>
let selection: Driver<IndexPath>
// let avatorTrigger: ControlEvent<Void>
}

@ -11,17 +11,17 @@ class PhotoConfirmViewController: UIViewController {
let photoControl: PhotoConfirmControl = {
let photoControl = PhotoConfirmControl.init()
photoControl.titleLabel.text = "从手机相册选择"
photoControl.addTarget(PhotoConfirmViewController.self, action: #selector(photoControlClick), for: .touchUpInside)
photoControl.isUserInteractionEnabled = true
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)
let cancelControl: PhotoConfirmControl = {
let cancelControl = PhotoConfirmControl.init()
cancelControl.lineView.isHidden = true
cancelControl.titleLabel.text = "取消"
return confirmControl
return cancelControl
}()
@ -29,12 +29,30 @@ class PhotoConfirmViewController: UIViewController {
super.viewDidLoad()
makeUI()
photoControl.addTarget(self, action: #selector(photoControlClick), for: .touchUpInside)
cancelControl.addTarget(self, action: #selector(confirmControlClick), for: .touchUpInside)
photoControl.rx.controlEvent(.touchUpInside)
.subscribe { _ in
// self.dismiss(animated: true)
}.disposed(by: rx.disposeBag)
cancelControl.rx.controlEvent(.touchUpInside)
.subscribe { _ in
self.dismiss(animated: true)
}.disposed(by: rx.disposeBag)
}
func makeUI() {
view.backgroundColor = .white
view.addSubview(photoControl)
view.addSubview(confirmControl)
view.addSubview(cancelControl)
}
@ -49,7 +67,7 @@ class PhotoConfirmViewController: UIViewController {
make.top.equalTo(12)
}
confirmControl.snp.makeConstraints { make in
cancelControl.snp.makeConstraints { make in
make.left.equalTo(view)
make.right.equalTo(view)
make.top.equalTo(photoControl.snp.bottom)
@ -60,11 +78,9 @@ class PhotoConfirmViewController: UIViewController {
}
@objc func photoControlClick() {
self.dismiss(animated: true)
}
@objc func confirmControlClick() {
self.dismiss(animated: true)
}
}
@ -103,7 +119,7 @@ class PhotoConfirmControl: UIControl {
super.layoutSubviews()
titleLabel.snp.makeConstraints { make in
make.centerX.equalTo(self)
make.center.equalTo(self)
}
lineView.snp.makeConstraints { make in
@ -115,3 +131,16 @@ class PhotoConfirmControl: UIControl {
}
}
extension PhotoConfirmViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let carePresentationVC = CardPresentationController.init(presentedViewController: presented, presenting: presenting)
carePresentationVC.viewType = .photoConfirm
return carePresentationVC
}
}

@ -30,11 +30,8 @@ class SettingViewController: TableViewController {
navigationItem.title = "设置"
tableView.mj_header = nil
tableView.mj_footer = nil
tableView.sectionHeaderHeight = 9
tableView.sectionFooterHeight = 0
tableView.register(SettingViewCell.self, forCellReuseIdentifier: "SettingViewCell")
tableView.tableFooterView = footerView
@ -113,6 +110,8 @@ class SettingViewController: TableViewController {
}.disposed(by: rx.disposeBag)
tableView.mj_header = nil
tableView.mj_footer = nil
}
}

@ -18,10 +18,15 @@ protocol IndieMusicAPI {
///
func sendsms(mobile: String) -> Single<EmptyModel>
///
func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single<Login>
func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single<String>
/// Token
func autoLogin() -> Single<Login>
func autoLogin() -> Single<String>
///
func getUserInfo() -> Single<User>
///
func editUserInfo(nickName: String?, signature: String?, birthDay: String?, sex: String?) -> Single<EmptyModel>
///
func journalList(style: String, language: String, pageNum: Int, pageSize: Int) -> Single<[HomeJournal]>
}

@ -14,13 +14,14 @@ protocol ProductAPIType {
var addXAuth: Bool { get }
}
enum APIConfig {
case wechatAccessToken([String: Any])
case login(String, String, [String: Any])
case sendsms(mobil: String, [String: Any])
case autoLogin([String: Any])
case getUserInfo([String: Any])
case editUserInfo([String: Any])
case journalList([String: Any])
}
extension APIConfig: TargetType {
@ -38,26 +39,30 @@ extension APIConfig: TargetType {
return "user/user/autoLogin"
case .getUserInfo:
return "user/my/getUserInfo"
case .editUserInfo:
return "user/my/updateUserInfo"
case .journalList:
return "luoo-music/journal/list"
}
}
var method: Moya.Method {
switch self {
case .wechatAccessToken:
case .wechatAccessToken, .journalList:
return .get
case .sendsms, .login, .autoLogin, .getUserInfo:
case .sendsms, .login, .autoLogin, .getUserInfo, .editUserInfo:
return .post
}
}
var parameterEncoding: ParameterEncoding {
switch self {
case .wechatAccessToken:
case .wechatAccessToken, .journalList:
return URLEncoding.default
case .login:
return JSONEncoding.default
case .sendsms, .autoLogin, .getUserInfo:
case .sendsms, .autoLogin, .getUserInfo, .editUserInfo:
return JSONEncoding.default
}
}
@ -66,10 +71,10 @@ extension APIConfig: TargetType {
var task: Task {
var parameters: [String: Any] = [:]
switch self {
case .wechatAccessToken:
case .wechatAccessToken, .journalList:
return .requestPlain
case .login(_, _, let dic), .sendsms(_, let dic), .autoLogin(let dic), .getUserInfo(let dic):
case .login(_, _, let dic), .sendsms(_, let dic), .autoLogin(let dic), .getUserInfo(let dic), .editUserInfo(let dic):
parameters = dic
return .requestParameters(parameters: parameters, encoding: parameterEncoding)
@ -83,7 +88,7 @@ extension APIConfig: TargetType {
switch self {
case .wechatAccessToken:
return URL(string: Configs.App.wechatURL)!
case .login, .sendsms, .autoLogin, .getUserInfo:
default:
return URL(string: Configs.App.environmentType.baseUrl)!
}
@ -91,7 +96,7 @@ extension APIConfig: TargetType {
var headers : [String : String]? {
switch self {
case .autoLogin, .getUserInfo:
case .autoLogin, .getUserInfo, .journalList:
return ["token": AuthManager.shared.token?.basicToken ?? ""]
default:
return nil

@ -29,6 +29,7 @@ enum ApiError: Error {
}
class RestApi: IndieMusicAPI {
let indieMusicProvider: IndieMusicNetworking
@ -72,12 +73,12 @@ extension RestApi {
return requestObject(.wechatAccessToken(dic), with: "data", type: EmptyModel.self)
}
func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single<Login> {
return requestObject(.login(mobile, mobileCheckCode, ["": ""]), with: "data", type: Login.self)
func loginOrRegister(mobile: String, mobileCheckCode: String) -> Single<String> {
return requestObject(.login(mobile, mobileCheckCode, ["": ""]), with: "data", type: String.self)
}
func autoLogin() -> Single<Login> {
return requestObject(.autoLogin(["": ""]), with: "data", type: Login.self)
func autoLogin() -> Single<String> {
return requestObject(.autoLogin(["": ""]), with: "data", type: String.self)
}
func sendsms(mobile: String) -> Single<EmptyModel> {
@ -90,5 +91,34 @@ extension RestApi {
return requestObject(.getUserInfo(dic), with: "data", type: User.self)
}
func editUserInfo(nickName: String?, signature: String?, birthDay: String?, sex: String?) -> Single<EmptyModel> {
var dic = ["": ""]
if let nickName = nickName {
dic["nickName"] = nickName
}
if let signature = signature {
dic["signature"] = signature
}
if let birthDay = birthDay {
dic["birthDay"] = birthDay
}
if let sex = sex {
dic["sex"] = sex
}
return requestObject(.editUserInfo(dic), with: "data", type: EmptyModel.self)
}
func journalList(style: String, language: String, pageNum: Int, pageSize: Int) -> Single<[HomeJournal]> {
let dic = ["style": style,
"language": language,
"pageNum": pageNum,
"pageSize": pageSize
] as [String : Any]
return requestObject(.journalList(dic), with: "data.rows", type: [HomeJournal].self)
}
}

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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

@ -38,5 +38,7 @@
<string>weixinULAPI</string>
<string>weixinURLParamsAPI</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
</dict>
</plist>

@ -24,6 +24,7 @@ target 'IndieMusic' do
pod 'AttributedString'
pod 'ESTMusicIndicator'
pod 'IQKeyboardManagerSwift'
pod 'lottie-ios'
pod 'NSObject+Rx'
@ -33,7 +34,10 @@ target 'IndieMusic' do
pod 'Moya/RxSwift', '~> 15.0'
pod 'RxTheme', '~> 6.0'
pod 'RxDataSources'
pod "RxGesture"
pod 'RxOptional'
pod 'RxModal'
target 'IndieMusicTests' do
inherit! :search_paths
# Pods for testing

Loading…
Cancel
Save