Save the cover image

dev
wenlei 1 year ago
parent 417786c210
commit 88cdaf5be3

@ -37,6 +37,8 @@
774A18102B070A6900F56DF1 /* SongViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774A180F2B070A6900F56DF1 /* SongViewCell.swift */; };
774A18122B07327C00F56DF1 /* CommentCountButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774A18112B07327C00F56DF1 /* CommentCountButton.swift */; };
774A18142B07329600F56DF1 /* MultiUserAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774A18132B07329600F56DF1 /* MultiUserAvatarView.swift */; };
7751009A2B62050C00F46109 /* UIImageView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 775100992B62050C00F46109 /* UIImageView+Rx.swift */; };
7751009C2B62065800F46109 /* Date+IndieMusic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7751009B2B62065800F46109 /* Date+IndieMusic.swift */; };
7751D3502B42ABBF00F1F2BD /* SettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7751D34F2B42ABBF00F1F2BD /* SettingViewController.swift */; };
7751D3522B42AC2B00F1F2BD /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7751D3512B42AC2B00F1F2BD /* SettingView.swift */; };
7751D3542B42AE0E00F1F2BD /* SettingViewMdel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7751D3532B42AE0E00F1F2BD /* SettingViewMdel.swift */; };
@ -252,6 +254,8 @@
774A180F2B070A6900F56DF1 /* SongViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongViewCell.swift; sourceTree = "<group>"; };
774A18112B07327C00F56DF1 /* CommentCountButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentCountButton.swift; sourceTree = "<group>"; };
774A18132B07329600F56DF1 /* MultiUserAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiUserAvatarView.swift; sourceTree = "<group>"; };
775100992B62050C00F46109 /* UIImageView+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Rx.swift"; sourceTree = "<group>"; };
7751009B2B62065800F46109 /* Date+IndieMusic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+IndieMusic.swift"; sourceTree = "<group>"; };
7751D34F2B42ABBF00F1F2BD /* SettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingViewController.swift; sourceTree = "<group>"; };
7751D3512B42AC2B00F1F2BD /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; };
7751D3532B42AE0E00F1F2BD /* SettingViewMdel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingViewMdel.swift; sourceTree = "<group>"; };
@ -647,6 +651,7 @@
778B8AA02AF8ED0E0034AFD4 /* UIImage+IndieMusic.swift */,
778B8AA22AF8ED0E0034AFD4 /* UIView+Borders.swift */,
7751D3852B45409000F1F2BD /* NSNotification+IndieMusic.swift */,
7751009B2B62065800F46109 /* Date+IndieMusic.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -867,6 +872,7 @@
children = (
778B8AA42AF8ED0E0034AFD4 /* UIViewController+Rx.swift */,
778B8AA52AF8ED0E0034AFD4 /* RxMJRefresh.swift */,
775100992B62050C00F46109 /* UIImageView+Rx.swift */,
);
path = "RxSwift+Extension";
sourceTree = "<group>";
@ -1224,6 +1230,7 @@
774399A82AFE28BA006F8EEA /* BlurEffectView.swift in Sources */,
778B8A852AF8ECE50034AFD4 /* SearchViewController.swift in Sources */,
770228E92B55169C00E07F7A /* InternationalNumber.swift in Sources */,
7751009C2B62065800F46109 /* Date+IndieMusic.swift in Sources */,
7751D3802B45271600F1F2BD /* BindPhoneViewModel.swift in Sources */,
774A17F52B045F1C00F56DF1 /* Player.swift in Sources */,
7751D3882B45584000F1F2BD /* LaunchADManager.swift in Sources */,
@ -1349,6 +1356,7 @@
7751D3662B42BE7F00F1F2BD /* FeedbackViewController.swift in Sources */,
77A60D822B5B97A100D4E435 /* AssetDataManager.swift in Sources */,
77FB7A7B2B4A4FC900B64030 /* MessageViewController.swift in Sources */,
7751009A2B62050C00F46109 /* UIImageView+Rx.swift in Sources */,
77FB7A722B48E93100B64030 /* SearchResultsViewModel.swift in Sources */,
774A18142B07329600F56DF1 /* MultiUserAvatarView.swift in Sources */,
778B8AAA2AF8ED0E0034AFD4 /* AVPlayer.swift in Sources */,

@ -0,0 +1,181 @@
//
// Date+IndieMusic.swift
// IndieMusic
//
// Created by WenLei on 2024/1/25.
//
import Foundation
import SwiftDate
extension TimeInterval {
///
func timeIntervalChangeToTimeStr(_ isSeconds: Bool = false, _ dateFormat: String? = "yyyy年MM月dd日 HH:mm") -> String {
//1000,
if isSeconds {
}
let date: Date = Date.init(timeIntervalSince1970: isSeconds ? self : self/1000)
let formatter = DateFormatter.init()
formatter.dateFormat = dateFormat
return formatter.string(from: date as Date)
}
///
func timeString() -> String{
//1000,
let date: Date = Date.init(timeIntervalSince1970: self/1000)
// init(timeIntervalSince1970: timeInterval/1000)
let formatter = DateFormatter.init()
if date.isToday() {
//
formatter.dateFormat = "今天 HH:mm"
return formatter.string(from: date as Date)
}else if date.isYesterday(){
//
formatter.dateFormat = "昨天 HH:mm"
return formatter.string(from: date as Date)
}else if date.isSameWeek(){
//
let week = date.weekdayStringFromDate()
formatter.dateFormat = "\(week) HH:mm"
return formatter.string(from: date as Date)
} else {
formatter.dateFormat = "yyyy年MM月dd日 HH:mm"
return formatter.string(from: date as Date)
}
}
}
extension String {
///
func timeStrChangeToTimeInterval(_ dateFormat: String? = "yyyy-MM-dd HH:mm") -> TimeInterval {
let formatter = DateFormatter.init()
formatter.dateFormat = dateFormat
let date = formatter.date(from: self)! as NSDate
return date.timeIntervalSince1970
}
}
extension Date {
///
func dateString(isShowYear: Bool = true, format: String = "yyyy年MM月dd日 HH:mm") -> String {
if isToday {
return toFormat("今天 HH:mm")
} else if isYesterday {
return toFormat("昨天 HH:mm")
} else if isSameWeek() {
return toFormat("EEEE HH:mm")
} else if isSameYear() {
return isShowYear ? toFormat(format) : toFormat("MM月dd日 HH:mm")
}
return toFormat(format)
}
///
func compareTime(startTime:NSString,endTime:NSString) -> ComparisonResult{
return startTime.compare(endTime as String)
}
func isToday() -> Bool {
let calendar = Calendar.current
//
let nowComponents = calendar.dateComponents([.day,.month,.year], from: Date() )
//self
let selfComponents = calendar.dateComponents([.day,.month,.year], from: self as Date)
return (selfComponents.year == nowComponents.year) && (selfComponents.month == nowComponents.month) && (selfComponents.day == nowComponents.day)
}
func isYesterday() -> Bool {
let calendar = Calendar.current
//
let nowComponents = calendar.dateComponents([.day, .month, .year], from: Date() )
//self
let selfComponents = calendar.dateComponents([.day, .month, .year], from: self as Date)
let cmps = calendar.dateComponents([.day], from: selfComponents, to: nowComponents)
return cmps.day == 1
}
func isSameWeek() -> Bool {
let calendar = Calendar.current
//
let nowComponents = calendar.dateComponents([.weekOfMonth,.month,.year], from: Date() )
//self
let selfComponents = calendar.dateComponents([.weekOfMonth,.month,.year], from: self as Date)
return (selfComponents.year == nowComponents.year) && (selfComponents.month == nowComponents.month) && (selfComponents.weekOfMonth == nowComponents.weekOfMonth)
}
func isSameYear() -> Bool {
let calendar = Calendar.current
//
let nowComponents = calendar.dateComponents([.weekOfMonth,.month,.year], from: Date() )
//self
let selfComponents = calendar.dateComponents([.weekOfMonth,.month,.year], from: self as Date)
return (selfComponents.year == nowComponents.year)
}
func weekdayStringFromDate() -> String {
let weekdays:NSArray = ["星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
var calendar = Calendar.init(identifier: .gregorian)
let timeZone = TimeZone.init(identifier: "Asia/Shanghai")
calendar.timeZone = timeZone!
let theComponents = calendar.dateComponents([.weekOfMonth], from: self as Date)
return weekdays.object(at: theComponents.weekOfMonth!) as! String
}
}
extension Int {
}
//extension BMKTime {
// func timeToToSeconds() -> Int {
//
// let daySeconds = datesToSeconds(dates: Int(self.dates))
// let hoursSeconds = hoursToSeconds(hours: Int(self.hours))
// let minutesSeconds = minutesToSeconds(minutes: Int(self.minutes))
//
//
//
// return daySeconds + hoursSeconds + minutesSeconds
// }
//}
///
func datesToSeconds(dates: Int) -> Int {
return dates * 24 * 60 * 60
}
///
func hoursToSeconds(hours: Int) -> Int {
return hours * 60 * 60
}
///
func minutesToSeconds(minutes: Int) -> Int {
return minutes * 60
}

@ -0,0 +1,40 @@
//
// UIImageView+Rx.swift
// IndieMusic
//
// Created by WenLei on 2024/1/25.
//
import Foundation
import RxSwift
import RxCocoa
import Photos
extension UIImage {
func saveImageToPhotoLibrary() -> Observable<Void> {
return Observable.create { observer in
// 访
PHPhotoLibrary.requestAuthorization { status in
guard status == .authorized else {
observer.onError(NSError(domain: "PhotoLibrary", code: -1, userInfo: [NSLocalizedDescriptionKey: "没有相册访问权限"]))
return
}
//
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: self)
}) { success, error in
if success {
observer.onNext(())
observer.onCompleted()
} else if let error = error {
observer.onError(error)
}
}
}
return Disposables.create()
}
}
}

@ -10,6 +10,7 @@ import RxSwift
import RxCocoa
import RxDataSources
import ETNavBarTransparent
import SVProgressHUD
class JournalDetailController: ViewController, UIScrollViewDelegate {
@ -157,6 +158,18 @@ class JournalDetailController: ViewController, UIScrollViewDelegate {
if indexPath.section == 0 {
let resuableView: JournalAudioHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: "UICollectionElementKindSectionHeader", withReuseIdentifier: "JournalAudioHeaderView", for: indexPath) as! JournalAudioHeaderView
let section = dataSource.sectionModels.first.value
resuableView.saveButton.rx.tap.subscribe { _ in
resuableView.titleImageView.image?.saveImageToPhotoLibrary()
.subscribe(onNext: { _ in
SVProgressHUD.showText(withStatus: "保存成功")
}, onError: { error in
}).disposed(by: self.rx.disposeBag)
}.disposed(by: self.rx.disposeBag)
resuableView.journalDetail = section?.header

@ -10,6 +10,7 @@ import ESTMusicIndicator
import RxSwift
import RxCocoa
import DateToolsSwift
import FSPopoverView
class JournalAudioHeaderView: UICollectionReusableView {
var titleImageView: UIImageView = {
@ -127,6 +128,29 @@ class JournalAudioHeaderView: UICollectionReusableView {
return journalAudioSectionView
}()
let saveButton: UIButton = {
let saveButton = UIButton.init()
saveButton.titleLabel?.font = UIFont.systemFont(ofSize: 12)
saveButton.titleLabel?.textAlignment = .center
saveButton.setTitleColor(.white, for: .normal)
saveButton.setTitle("保存图片", for: .normal)
return saveButton
}()
let popoverView: FSPopoverView = {
let popoverView = FSPopoverView.init()
popoverView.showsArrow = false
popoverView.borderWidth = 0
return popoverView
}()
var isExpand = false {
@ -153,6 +177,8 @@ class JournalAudioHeaderView: UICollectionReusableView {
}
var dropButtonTapObservable: Observable<Void> {
return dropButton.rx.tap.asObservable()
}
@ -191,6 +217,23 @@ class JournalAudioHeaderView: UICollectionReusableView {
super.init(frame: frame)
makeUI()
popoverView.dataSource = self
titleImageView.rx.tapGesture().when(.recognized)
.subscribe { tap in
self.popoverView.present(fromPoint: self.titleImageView.center, in: self, displayIn: self)
}.disposed(by: rx.disposeBag)
saveButton.rx.tap.subscribe { _ in
self.popoverView.dismiss()
}.disposed(by: rx.disposeBag)
}
required init?(coder: NSCoder) {
@ -313,6 +356,36 @@ class JournalAudioHeaderView: UICollectionReusableView {
}
extension JournalAudioHeaderView: FSPopoverViewDataSource {
func backgroundView(for popoverView: FSPopoverView) -> UIView? {
let view = UIView()
view.backgroundColor = .primaryText()
return view
}
func contentView(for popoverView: FSPopoverView) -> UIView? {
return saveButton
}
func contentSize(for popoverView: FSPopoverView) -> CGSize {
return CGSize.init(width: 82, height: 37)
}
func containerSafeAreaInsets(for popoverView: FSPopoverView) -> UIEdgeInsets {
var insets = self.safeAreaInsets
// insets.left = 10.0
// insets.right = 10.0
return insets
}
func popoverViewShouldDismissOnTapOutside(_ popoverView: FSPopoverView) -> Bool {
return true
}
}
class JournalDetailHeaderView: UICollectionReusableView {
var titleLabel: UILabel = {

@ -112,7 +112,7 @@ extension APIConfig: TargetType {
var headers : [String : String]? {
switch self {
case .autoLogin, .getUserInfo, .journalList, .journalMusic:
return ["token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNzQ5MzIwOTE4MDI0NDU4MjQwIiwic3ViIjoi6ZuA5LmQLealt-S4iSTmnbDlhYnpkavnn7MiLCJpYXQiOjE3MDYwOTM2OTksInJvbGVzIjoidXNlciIsImV4cCI6MTcwNjY5ODQ5OX0.lPDwWBk0nShq68YfcmuK40R4eAAv2cH_XZICrsxYj4Y"]
return ["token": AuthManager.shared.token?.basicToken ?? ""]
default:
return nil
}

@ -119,28 +119,21 @@ class OnlineProvider<Target> where Target: Moya.TargetType {
.filter(statusCode: 200)
.asObservable()
.filterSuccess()
// .do(onSuccess: { (response) in
// print(response.data)
//
// }, onError: { (error) in
//
// if let error = error as? MoyaError {
// switch error {
// case .statusCode(let response):
// if response.statusCode == 401 {
// // Unauthorized
// if AuthManager.shared.hasValidToken {
// AuthManager.removeToken()
// Application.shared.presentInitialScreen(in: Application.shared.window)
// }
// }
// default: break
// }
// }
// })
}
}
// func requestWithProgress(_ token: Target) -> Observable<(progress: Double, response: Response)> {
// let request = provider.rx.request(token, callbackQueue: DispatchQueue.global(qos: .userInitiated))
// let progress = request.progress()
//
// return Observable.combineLatest(progress, request.asObservable()) { (progress, response) in
// return (progress, response)
// }
// }
//
}
protocol NetworkingType {
@ -167,6 +160,11 @@ struct IndieMusicNetworking: NetworkingType {
let actualRequest = self.provider.request(token)
return actualRequest
}
}

@ -59,6 +59,8 @@ extension RestApi {
.observe(on: MainScheduler.instance)
.asSingle()
}
}

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>我们需要您的同意来保存图片到您的相册</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>我们需要您的同意来保存图片到您的相册</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

@ -29,6 +29,7 @@ target 'IndieMusic' do
pod 'MarqueeLabel'
pod 'PINCache'
pod 'DeviceKit', '~> 5.2'
pod 'FSPopoverView'
pod 'NSObject+Rx'

Loading…
Cancel
Save