数据结构改动

This commit is contained in:
Mr.zhou 2024-05-29 17:31:45 +08:00
parent 57a5482045
commit 4621f95204
73 changed files with 5443 additions and 2936 deletions

View File

@ -56,6 +56,8 @@
CBB9F9DF2BEDDCC5008338DE /* MP_PlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB9F9DE2BEDDCC5008338DE /* MP_PlayerManager.swift */; };
CBBA6A222BFF12030047ADF8 /* MP_AVURLAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBA6A212BFF12030047ADF8 /* MP_AVURLAsset.swift */; };
CBBA6A242BFF160C0047ADF8 /* MP_CacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBA6A232BFF160C0047ADF8 /* MP_CacheManager.swift */; };
CBBFA0A62C0706DE00769999 /* MPPositive_MoreSongOperationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBFA0A52C0706DE00769999 /* MPPositive_MoreSongOperationsViewController.swift */; };
CBBFA0A82C070BD900769999 /* MPPositive_MoreOperationDownLoadTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBFA0A72C070BD900769999 /* MPPositive_MoreOperationDownLoadTableViewCell.swift */; };
CBBFA9182BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBFA9172BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift */; };
CBBFA91A2BBA846600057FD5 /* CoreDataDelegete.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBFA9192BBA846600057FD5 /* CoreDataDelegete.swift */; };
CBBFA91E2BBA9B5C00057FD5 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBBFA91D2BBA9B5C00057FD5 /* Notification.swift */; };
@ -260,6 +262,8 @@
CBB9F9DE2BEDDCC5008338DE /* MP_PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_PlayerManager.swift; sourceTree = "<group>"; };
CBBA6A212BFF12030047ADF8 /* MP_AVURLAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_AVURLAsset.swift; sourceTree = "<group>"; };
CBBA6A232BFF160C0047ADF8 /* MP_CacheManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_CacheManager.swift; sourceTree = "<group>"; };
CBBFA0A52C0706DE00769999 /* MPPositive_MoreSongOperationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_MoreSongOperationsViewController.swift; sourceTree = "<group>"; };
CBBFA0A72C070BD900769999 /* MPPositive_MoreOperationDownLoadTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_MoreOperationDownLoadTableViewCell.swift; sourceTree = "<group>"; };
CBBFA9172BBA83BA00057FD5 /* MP_CoreDataHandlerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_CoreDataHandlerManager.swift; sourceTree = "<group>"; };
CBBFA9192BBA846600057FD5 /* CoreDataDelegete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataDelegete.swift; sourceTree = "<group>"; };
CBBFA91D2BBA9B5C00057FD5 /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
@ -872,6 +876,7 @@
CB0918902BD255EA006D2B39 /* MPPositive_NavigationController.swift */,
CB0918962BD25D8C006D2B39 /* MPPositive_TabBarController.swift */,
CBCC234E2BEE57AC004D7A57 /* MPPositive_PresentationController.swift */,
CBBFA0A52C0706DE00769999 /* MPPositive_MoreSongOperationsViewController.swift */,
);
path = "Base基类导航栏标签栏";
sourceTree = "<group>";
@ -926,6 +931,7 @@
CB09189A2BD25F50006D2B39 /* MPPositive_CustomTabBarView.swift */,
CB09189C2BD25F63006D2B39 /* MPPositive_CustomTabBarItem.swift */,
CB0918A42BD26E16006D2B39 /* MPPositive_BottomShowView.swift */,
CBBFA0A72C070BD900769999 /* MPPositive_MoreOperationDownLoadTableViewCell.swift */,
);
path = Base;
sourceTree = "<group>";
@ -1261,6 +1267,7 @@
CBCB500A2BD11402009760B3 /* MPSideA_CustomTabBar.swift in Sources */,
CB2416912C05D36F007877F7 /* MPPositive_DownloadViewModel.swift in Sources */,
CBCAFB692BB3CAC400BC6520 /* MP_Lunch_ProgressView.swift in Sources */,
CBBFA0A62C0706DE00769999 /* MPPositive_MoreSongOperationsViewController.swift in Sources */,
CBD6F2182BF4A29B00343A4A /* MPPositive_ArtistViewModel.swift in Sources */,
CBCB50102BD11402009760B3 /* MPSideA_SettingTableViewCell.swift in Sources */,
CBCAFB5D2BB3C52100BC6520 /* HexColor.swift in Sources */,
@ -1361,6 +1368,7 @@
CBFECE3F2BF1176B00E07DC4 /* MPPositive_JsonSearchResults.swift in Sources */,
CBC6874B2BC2B0710023ECA6 /* String.swift in Sources */,
CBD3135F2BD642D90015D227 /* MPPositive_HomeListFourthCollectionViewCell.swift in Sources */,
CBBFA0A82C070BD900769999 /* MPPositive_MoreOperationDownLoadTableViewCell.swift in Sources */,
CBBA6A242BFF160C0047ADF8 /* MP_CacheManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "COCO_Line_Arrow_-_Bottom_1@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "COCO_Line_Arrow_-_Bottom_1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "COCO_Line_Arrow_-_Bottom_2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "COCO_Line_Arrow_-_Bottom_2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -310,30 +310,30 @@ class MP_ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes
// MARK: URLSession
///
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard response.mimeType?.contains("video") == true else {
print("\(playItem?.title ?? "")网络地址不可用,响应类型是\(response.mimeType ?? "")")
//
MP_NetWorkManager.shared.requestPlayer(playItem?.videoId ?? "", playlistId: "") {[weak self] resourceUrls, coverUrls in
//
guard let self = self else {return}
print("\(playItem?.title ?? "")重新加载一次")
let url = URL(string: resourceUrls.0.first ?? "")!
playItem?.url = url
if mediaData == nil {
startDataRequest(with: url)
}else {
continuationDataRequest(with: url, count: Int64(mediaData!.count))
}
}
return
}
completionHandler(Foundation.URLSession.ResponseDisposition.allow)
if mediaData == nil {
mediaData = Data()
mediaDataBlocks = []
}
self.response = response
processPendingRequests()
// guard response.mimeType?.contains("video") == true else {
// print("\(playItem?.title ?? "")\(response.mimeType ?? "")")
// //
// MP_NetWorkManager.shared.requestPlayer(playItem?.videoId ?? "", playlistId: "") {[weak self] resourceUrls, coverUrls in
// //
// guard let self = self else {return}
// print("\(playItem?.title ?? "")")
// let url = URL(string: resourceUrls.0.first ?? "")!
// playItem?.url = url
// if mediaData == nil {
// startDataRequest(with: url)
// }else {
// continuationDataRequest(with: url, count: Int64(mediaData!.count))
// }
// }
// return
// }
// completionHandler(Foundation.URLSession.ResponseDisposition.allow)
// if mediaData == nil {
// mediaData = Data()
// mediaDataBlocks = []
// }
// self.response = response
// processPendingRequests()
}
///
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {

View File

@ -81,6 +81,22 @@ class DownloadManager: NSObject {
session.cancel(key)
}
}
//
func deleteFileDocuments(_ videoId:String, completion:@escaping((String) -> Void)) {
let downloadsURL = DocumentsURL.appendingPathComponent("Downloads")
let fileURL = downloadsURL.appendingPathComponent("\(videoId).mp4")
if FileManager.default.fileExists(atPath: fileURL.absoluteString) {
do{
try FileManager.default.removeItem(at: fileURL)
//
completion(videoId)
}catch{
print("删除文件时发生错误:\(error)")
}
}else {
print("文件不存在")
}
}
}

View File

@ -91,7 +91,7 @@ class MP_NetWorkManager: NSObject {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -205,7 +205,7 @@ extension MP_NetWorkManager {
"clientName": "WEB_REMIX",
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -268,7 +268,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -325,7 +325,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -381,7 +381,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -434,7 +434,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -488,7 +488,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -539,7 +539,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -572,7 +572,7 @@ extension MP_NetWorkManager {
//MARK: - player
/// Player(/)
/// - Parameter item:
func requestPlayer(_ videoId: String, playlistId: String, completion:@escaping ((([String],[Float],[String]), [String]?) -> Void)){
func requestAndroidPlayer(_ videoId: String, playlistId: String, completion:@escaping ((([String],[Int],[String]), [String]?) -> Void)){
//player
let path = header+point+player
//url
@ -580,51 +580,34 @@ extension MP_NetWorkManager {
print("Url is Incorrect")
return
}
let day = Date().timeZone()
//videoIdparams
let parameters:[String:Any] = [
"videoId":videoId,
"prettyPrint":"false",
// "playlistId":"OLAK5uy_knZiqQOlTDeQ3jecXrW_VIAZKdMnkLGgw",
"context":[
"client":[
// //访
// "clientName": "WEB_REMIX",
// "clientVersion": "1.\(currTimeDate).01.00"
//web
"clientName": "WEB_REMIX",
"visitorData":visitorData,
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36,gzip(gfe)",
// "remoteHost":"2401:b60:13:4335:bea3:7c0b:5c4e:db84",
"originalUrl":"https://music.youtube.com/watch?v=\(videoId)",
//访
"clientVersion": "1.\(currTimeDate).01.00",
"clientName": "ANDROID_MUSIC",
"clientVersion": "\(day.month).\(day.toString(.custom("dd"))).1",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
"gl":locaton
]
],
"playbackContext": [
"contentPlaybackContext": [
"signatureTimestamp": MP_WebWork.shared.signatureTimestamp ?? 0
"browserVersion":"125.0.0.0",
// "userAgent":
]
]
]
//guard netWorkStatu != .notReachable else {return}
requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
requestAndroidPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
completion(resourceUlrs, coverUrls)
}
}
///
private func requestPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[Float],[String]), [String]?) -> Void)) {
private func requestAndroidPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) {
//post
MPSession.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseDecodable(of: JsonPlayer.self) { [weak self] (response) in
MPSession.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseDecodable(of: JsonAndroidPlayer.self) { [weak self] (response) in
guard let self = self else {return}
switch response.result {
case .success(let value):
parsingPlayer(value) { resourceUlrs, coverUrls in
parsingAndroidPlayer(value) { resourceUlrs, coverUrls in
completion(resourceUlrs, coverUrls)
}
case .failure(let error):
@ -633,6 +616,60 @@ extension MP_NetWorkManager {
}
}
}
// func requestPlayer(_ videoId: String, playlistId: String, completion:@escaping ((([String],[Float],[String]), [String]?) -> Void)){
// //player
// let path = header+point+player
// //url
// guard let url = URL(string: path) else {
// print("Url is Incorrect")
// return
// }
// //videoIdparams
// let parameters:[String:Any] = [
// "videoId":videoId,
// "prettyPrint":"false",
// "context":[
// "client":[
// "clientName": "WEB_REMIX",
//// "visitorData":visitorData,
//// "originalUrl":"https://music.youtube.com/watch?v=\(videoId)",
// //访
// "clientVersion": "1.\(currTimeDate).01.00",
// "platform":"MOBILE",
// //
// "hl":Language_first_local,
// //
// "gl":locaton
// ]
// ],
// "playbackContext": [
// "contentPlaybackContext": [
// "signatureTimestamp": MP_WebWork.shared.signatureTimestamp ?? 0
// ]
// ]
// ]
// //guard netWorkStatu != .notReachable else {return}
// requestPostPlayer(url, parameters: parameters){ resourceUlrs, coverUrls in
// completion(resourceUlrs, coverUrls)
// }
// }
///
// private func requestPostPlayer(_ url:URL, parameters:Parameters, completion:@escaping((([String],[Float],[String]), [String]?) -> Void)) {
// //post
// MPSession.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseDecodable(of: JsonPlayer.self) { [weak self] (response) in
// guard let self = self else {return}
//
// switch response.result {
// case .success(let value):
// parsingPlayer(value) { resourceUlrs, coverUrls in
// completion(resourceUlrs, coverUrls)
// }
// case .failure(let error):
// //
// print("Request failed: \(error)")
// }
// }
// }
//MARK: -
///
/// - Parameter lyricId: id
@ -655,7 +692,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -702,7 +739,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -755,7 +792,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -806,7 +843,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -863,7 +900,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -920,7 +957,7 @@ extension MP_NetWorkManager {
"visitorData":visitorData,
//访
"clientVersion": "1.\(currTimeDate).01.00",
"platform":"DESKTOP",
"platform":"MOBILE",
//
"hl":Language_first_local,
//
@ -1221,50 +1258,73 @@ extension MP_NetWorkManager {
/// - Parameters:
/// - player: player
/// - completion:
private func parsingPlayer(_ player:JsonPlayer, completion:@escaping((([String],[Float],[String]), [String]?) -> Void)){
// private func parsingPlayer(_ player:JsonPlayer, completion:@escaping((([String],[Float],[String]), [String]?) -> Void)){
// var infos:[String]?
// //player
// if let videoDetails = player.videoDetails {
// infos = parsingPlayerVideoDetails(videoDetails)
// }
// if let streamingData = player.streamingData {
// parsingPlayerStreamingData(streamingData){ videos,floats,approxDurationMs in
// completion((videos,floats,approxDurationMs),infos)
// }
// }
// }
private func parsingAndroidPlayer(_ player:JsonAndroidPlayer,completion:@escaping((([String],[Int],[String]), [String]?) -> Void)){
var infos:[String]?
//player
if let videoDetails = player.videoDetails {
infos = parsingPlayerVideoDetails(videoDetails)
infos = parsingAndroidPlayerVideoDetails(videoDetails)
}
if let streamingData = player.streamingData {
parsingPlayerStreamingData(streamingData){ videos,floats,approxDurationMs in
completion((videos,floats,approxDurationMs),infos)
parsingAndroidPlayerStreamingData(streamingData){ videos,itags,mimeType in
completion((videos,itags,mimeType),infos)
}
}
}
/// _StreamingData
/// - Parameters:
/// - streamingData:
/// - completion:
private func parsingPlayerStreamingData(_ streamingData:JsonPlayer.StreamingData, completion:@escaping(([String],[Float],[String]) -> Void)) {
var group:DispatchGroup? = DispatchGroup()
private func parsingAndroidPlayerStreamingData(_ streamingData:JsonAndroidPlayer.StreamingData, completion:@escaping(([String],[Int],[String]) -> Void)) {
var videos:[String] = []
var floats:[Float] = []
var approxDurationMs:[String] = []
var itags:[Int] = []
var mimeType:[String] = []
let allFormats = (streamingData.formats ?? []) + (streamingData.adaptiveFormats ?? [])
for format in allFormats {
if let signatureCipher = format.signatureCipher {
// DispatchGroup
group?.enter()
//
parsingPlayerSignatureCipher(signatureCipher) { result in
//
videos.append(result)
floats.append(format.bitrate ?? 0)
approxDurationMs.append(format.approxDurationMs ?? "")
// DispatchGroup
group?.leave()
}
}
}
group?.notify(queue: .main) {
completion(videos, floats, approxDurationMs)
group = nil
videos.append(format.url ?? "")
itags.append(format.itag ?? 0)
mimeType.append(format.mimeType ?? "")
}
completion(videos,itags,mimeType)
}
// private func parsingPlayerStreamingData(_ streamingData:JsonPlayer.StreamingData, completion:@escaping(([String],[Float],[String]) -> Void)) {
// var group:DispatchGroup? = DispatchGroup()
// var videos:[String] = []
// var floats:[Float] = []
// var approxDurationMs:[String] = []
// let allFormats = (streamingData.formats ?? []) + (streamingData.adaptiveFormats ?? [])
// for format in allFormats {
// if let signatureCipher = format.signatureCipher {
// // DispatchGroup
// group?.enter()
// //
// parsingPlayerSignatureCipher(signatureCipher) { result in
// //
// videos.append(result)
// floats.append(format.bitrate ?? 0)
// approxDurationMs.append(format.approxDurationMs ?? "")
// // DispatchGroup
// group?.leave()
// }
// }
// }
// group?.notify(queue: .main) {
// completion(videos, floats, approxDurationMs)
// group = nil
// }
// }
///_SignatureCipher
private func parsingPlayerSignatureCipher(_ signatureCipher:String, completion:@escaping((String) -> Void)) {
// print("Resources-SignatureCipher:\(signatureCipher)")
@ -1296,7 +1356,7 @@ extension MP_NetWorkManager {
/// _VideoDetails
/// - Parameter videoDetails:
/// - Returns: videoIdtitleauthorurls
private func parsingPlayerVideoDetails(_ videoDetails:JsonPlayer.VideoDetails) -> [String]? {
private func parsingAndroidPlayerVideoDetails(_ videoDetails:JsonAndroidPlayer.VideoDetails) -> [String]? {
var urls:[String]?
videoDetails.thumbnail?.thumbnails?.forEach({ item in
if item.url != nil {
@ -1310,6 +1370,20 @@ extension MP_NetWorkManager {
})
return urls
}
// private func parsingPlayerVideoDetails(_ videoDetails:JsonPlayer.VideoDetails) -> [String]? {
// var urls:[String]?
// videoDetails.thumbnail?.thumbnails?.forEach({ item in
// if item.url != nil {
// if urls != nil {
// urls!.append(item.url!)
// }else {
// urls = []
// urls!.append(item.url!)
// }
// }
// })
// return urls
// }
///_Lyrics
private func parsingLyrics(_ lyrics:JsonLyrics) -> String? {
if let first = lyrics.contents?.sectionListRenderer?.contents?.first {

View File

@ -162,7 +162,7 @@ class MP_PlayerManager:NSObject{
}
}else {
//
let byte = currentTime + 100
let byte = currentTime + 30
let rate = Float(byte)/duration
if self.cacheValueBlock != nil {
self.cacheValueBlock!(rate)
@ -193,7 +193,7 @@ class MP_PlayerManager:NSObject{
}else {
//
player = .init(url: loadPlayer.currentVideo.resourcePlayerURL!)
player.maxRetryCount = 3
player.maxRetryCount = 2
}
//
let index = loadPlayer.listViewVideos.firstIndex(of: loadPlayer.currentVideo) ?? 0
@ -212,6 +212,7 @@ class MP_PlayerManager:NSObject{
switch status {
case .fsAudioStreamFailed://
print("\(loadPlayer.currentVideo?.title ?? "")加载失败")
playState = .Null
case .fsAudioStreamRetryingFailed://
print("\(loadPlayer.currentVideo?.title ?? "")重试失败")
print("失败URL:\(String(describing: loadPlayer.currentVideo?.resourcePlayerURL))")

View File

@ -125,3 +125,122 @@ struct JsonPlayer: Codable {
}
}
}
///
struct JsonAndroidPlayer: Codable {
///
let streamingData:StreamingData?
////
let videoDetails:VideoDetails?
enum CodingKeys: String, CodingKey {
case streamingData = "streamingData"
case videoDetails = "videoDetails"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
streamingData = try values.decodeIfPresent(StreamingData.self, forKey: .streamingData)
videoDetails = try values.decodeIfPresent(VideoDetails.self, forKey: .videoDetails)
}
//MARK: -
struct StreamingData: Codable {
///访
let expiresInSeconds:String?
///
let formats:[Format]?
///
let adaptiveFormats:[Format]?
enum CodingKeys: String, CodingKey {
case expiresInSeconds = "expiresInSeconds"
case formats = "formats"
case adaptiveFormats = "adaptiveFormats"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
expiresInSeconds = try values.decodeIfPresent(String.self, forKey: .expiresInSeconds)
formats = try values.decodeIfPresent([Format].self, forKey: .formats)
adaptiveFormats = try values.decodeIfPresent([Format].self, forKey: .adaptiveFormats)
}
///
struct Format: Codable {
///
let itag: Int?
///
let bitrate:Float?
///
let mimeType:String?
///
let qualityLabel:String?
///
let url:String?
///
let approxDurationMs:String?
enum CodingKeys: String, CodingKey {
case itag = "itag"
case bitrate = "bitrate"
case mimeType = "mimeType"
case qualityLabel = "qualityLabel"
case url = "url"
case approxDurationMs = "approxDurationMs"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
itag = try values.decodeIfPresent(Int.self, forKey: .itag)
bitrate = try values.decodeIfPresent(Float.self, forKey: .bitrate)
mimeType = try values.decodeIfPresent(String.self, forKey: .mimeType)
qualityLabel = try values.decodeIfPresent(String.self, forKey: .qualityLabel)
approxDurationMs = try values.decodeIfPresent(String.self, forKey: .approxDurationMs)
url = try values.decodeIfPresent(String.self, forKey: .url)
}
}
}
//MARK: - /
struct VideoDetails: Codable {
////videoId
let videoId:String?
///
let title:String?
///
let thumbnail:Thumbnail?
///
let author:String?
enum CodingKeys: String, CodingKey {
case videoId = "videoId"
case title = "title"
case thumbnail = "thumbnail"
case author = "author"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
videoId = try values.decodeIfPresent(String.self, forKey: .videoId)
title = try values.decodeIfPresent(String.self, forKey: .title)
thumbnail = try values.decodeIfPresent(Thumbnail.self, forKey: .thumbnail)
author = try values.decodeIfPresent(String.self, forKey: .author)
}
///
struct Thumbnail: Codable {
///
let thumbnails:[Thumbnails]?
enum CodingKeys: String, CodingKey {
case thumbnails = "thumbnails"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
thumbnails = try values.decodeIfPresent([Thumbnails].self, forKey: .thumbnails)
}
struct Thumbnails: Codable {
///
let url:String?
enum CodingKeys: String, CodingKey {
case url = "url"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
url = try values.decodeIfPresent(String.self, forKey: .url)
}
}
}
}
}

View File

@ -32,10 +32,8 @@ class MPPositive_SongItemModel: NSObject {
var videoId:String!
///ID
var relatedID:String!
///
var bitrates:[Float]?
///
var approxDurationMs:[String]?
///ID
var playlistId:String?
///
var itags:[Int]?
///
var mimeTypes:[String]?
}

View File

@ -110,8 +110,8 @@ class MPPositive_PlayerLoadViewModel: NSObject {
//
improveDataforResouceAndCover(item) {[weak self] resourceUrls, coverUrls in
item.resourceUrls = resourceUrls.0
item.bitrates = resourceUrls.1
item.approxDurationMs = resourceUrls.2
item.itags = resourceUrls.1
item.mimeTypes = resourceUrls.2
item.coverUrls = coverUrls
//,ViewModellistViewVideos
self?.listViewVideos.append(.init(item))
@ -134,14 +134,14 @@ class MPPositive_PlayerLoadViewModel: NSObject {
improveDataforResouceAndCover(currentVideo.song) {[weak self] resourceUrls, coverUrls in
guard let self = self else {return}
currentVideo.song.resourceUrls = resourceUrls.0
currentVideo.song.bitrates = resourceUrls.1
currentVideo.song.approxDurationMs = resourceUrls.2
currentVideo.song.itags = resourceUrls.1
currentVideo.song.mimeTypes = resourceUrls.2
//listViewVideos
listViewVideos.forEach({ item in
if item.song.videoId == self.currentVideo.song.videoId {
item.song.resourceUrls = self.currentVideo.song.resourceUrls
item.song.bitrates = self.currentVideo.song.bitrates
item.song.approxDurationMs = self.currentVideo.song.approxDurationMs
item.song.itags = self.currentVideo.song.itags
item.song.mimeTypes = self.currentVideo.song.mimeTypes
}
})
// currentVideo.resourceAsset = .init(url: .init(string: currentVideo.song.resourceUrls!.first!)!)
@ -189,9 +189,9 @@ class MPPositive_PlayerLoadViewModel: NSObject {
}
}
///player
private func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Float],[String]), [String]?) -> Void)) {
private func improveDataforResouceAndCover(_ song:MPPositive_SongItemModel, completion:@escaping((([String],[Int],[String]), [String]?) -> Void)) {
//player
MP_NetWorkManager.shared.requestPlayer(song.videoId, playlistId: song.playlistId ?? "") { resourceUrls, coverUrls in
MP_NetWorkManager.shared.requestAndroidPlayer(song.videoId, playlistId: "") { resourceUrls, coverUrls in
completion(resourceUrls,coverUrls)
}
}

View File

@ -0,0 +1,196 @@
//
// MPPositive_MoreOperationsViewController.swift
// MusicPlayer
//
// Created by Mr.Zhou on 2024/5/29.
//
import UIKit
import Kingfisher
///
class MPPositive_MoreSongOperationsViewController: UIViewController {
//View
private var loadView = CircularProgressView()
//
private lazy var indictorImageView:UIImageView = .init(image: .init(named: "Player_Indictor'logo"))
//ICON
private lazy var iconImageView:UIImageView = {
let imageView:UIImageView = .init()
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 10*width
return imageView
}()
//Label
private lazy var titleLabel:UILabel = createLabel("Title", font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left)
//
private lazy var subtitleLabel:UILabel = createLabel("Title", font: .systemFont(ofSize: 12*width, weight: .regular), textColor: .init(hex: "#666666"), textAlignment: .left)
//
private lazy var collectionBtn:UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "Artist_Collection'logo"), for: .normal)
btn.setImage(UIImage(named: "Artist_Collectioned'logo"), for: .selected)
btn.setBackgroundImage(UIImage(named: "Artist_Collection'bg"), for: .normal)
btn.setBackgroundImage(UIImage(named: "Artist_Collectioned'bg"), for: .selected)
btn.addTarget(self, action: #selector(collectionClick(_ :)), for: .touchUpInside)
return btn
}()
//线
private lazy var lineView:UIView = {
let lineView:UIView = UIView()
lineView.backgroundColor = .init(hex: "#444444")
return lineView
}()
//tableView
private lazy var tableView:UITableView = {
let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.contentInset = .init(top: 0, left: 0, bottom: bottomPadding, right: 0)
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
tableView.dataSource = self
tableView.delegate = self
tableView.register(MPPositive_MoreOperationDownLoadTableViewCell.self, forCellReuseIdentifier: MPPositive_MoreOperationTableViewCellID)
return tableView
}()
private let MPPositive_MoreOperationTableViewCellID = "MPPositive_MoreOperationTableViewCell"
private var song:MPPositive_SongItemModel!{
didSet{
iconImageView.kf.setImage(with: URL(string: song.reviewUrls?.last ?? ""), placeholder: placeholderImage)
titleLabel.text = song.title
subtitleLabel.text = song.shortBylineText
//
collectionBtn.isSelected = MPPositive_CollectionSongModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0
//
isLoaded = MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).count != 0
}
}
private var isLoaded:Bool = false{
didSet{
DispatchQueue.main.async {
[weak self] in
self?.tableView.reloadData()
}
}
}
init(_ song:MPPositive_SongItemModel) {
super.init(nibName: nil, bundle: nil)
self.song = song
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .init(hex: "#282A2C")
view.layer.masksToBounds = true
view.layer.maskedCorners = [.layerMinXMinYCorner,.layerMinXMaxYCorner]
view.layer.cornerRadius = 18*width
configure()
}
private func configure() {
view.addSubview(indictorImageView)
indictorImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.equalTo(29*width)
make.height.equalTo(4*width)
make.top.equalToSuperview().offset(8*width)
}
view.addSubview(iconImageView)
iconImageView.snp.makeConstraints { make in
make.width.height.equalTo(50*width)
make.left.equalToSuperview().offset(18*width)
make.top.equalToSuperview().offset(32*width)
}
view.addSubview(collectionBtn)
collectionBtn.snp.makeConstraints { make in
make.width.height.equalTo(32*width)
make.centerY.equalTo(iconImageView)
make.right.equalToSuperview().offset(-18*width)
}
view.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.top.equalTo(iconImageView).offset(8*width)
make.left.equalTo(iconImageView.snp.right).offset(12*width)
make.right.equalTo(collectionBtn.snp.left).offset(-12*width)
}
view.addSubview(subtitleLabel)
subtitleLabel.snp.makeConstraints { make in
make.left.right.equalTo(titleLabel)
make.bottom.equalTo(iconImageView).offset(-8*width)
}
view.addSubview(lineView)
lineView.snp.makeConstraints { make in
make.width.equalTo(339*width)
make.height.equalTo(1*width)
make.centerX.equalToSuperview()
make.top.equalTo(iconImageView.snp.bottom).offset(15*width)
}
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.top.equalTo(lineView.snp.bottom).offset(8*width)
make.left.right.bottom.equalToSuperview()
}
}
//
@objc private func collectionClick(_ sender:UIButton) {
if self.collectionBtn.isSelected == true{
self.collectionBtn.isSelected = false
MPPositive_CollectionSongModel.fetch(.init(format: "videoId == %@", song.videoId)).forEach { item in
if item.videoId == song.videoId {
MPPositive_CollectionSongModel.delete(item)
}
}
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
}else{
self.collectionBtn.isSelected = true
let item = MPPositive_CollectionSongModel.create()
item.coverImage = URL(string: song.reviewUrls?.last ?? "")
item.title = song.title
item.subtitle = song.shortBylineText
item.videoId = song.videoId
item.lyricsID = song.lyricsID
item.relatedID = song.relatedID
MPPositive_CollectionSongModel.save()
MPPositive_LoadCoreModel.shared.reloadCollectionSongViewModel(nil)
}
}
}
//MARK: - tableView
extension MPPositive_MoreSongOperationsViewController:UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_MoreOperationTableViewCellID, for: indexPath) as! MPPositive_MoreOperationDownLoadTableViewCell
cell.title = isLoaded ? "Remove Download":"Add Download"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
if isLoaded {
//
MPPositive_DownloadItemModel.fetch(.init(format: "videoId == %@", song.videoId)).forEach { item in
if item.videoId == song.videoId {
MPPositive_DownloadItemModel.delete(item)
}
}
MPPositive_LoadCoreModel.shared.reloadLoadSongViewModel(nil)
DownloadManager.shared.deleteFileDocuments(song.videoId) { [weak self] videoId in
guard let self = self else {return}
MP_HUD.progress("Loading...", delay: 0.5) {
self.isLoaded = false
MP_HUD.text("Removed", delay: 1.0, completion: nil)
}
}
}else {
//
}
}
}
}

View File

@ -10,6 +10,8 @@ import UIKit
enum MPPositive_PresentModal{
///
case PlayerList
///
case MoreOperations
}
class MPPositive_PresentationController: UIPresentationController {
//
@ -27,6 +29,11 @@ class MPPositive_PresentationController: UIPresentationController {
make.left.right.bottom.equalToSuperview()
make.height.equalTo(380*width)
})
case .MoreOperations:
presentedView?.snp.makeConstraints({ (make) in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(160*width+bottomPadding)
})
}
//
setMask()

View File

@ -70,6 +70,9 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
//
var array:[MPPositive_SongItemModel] = []
for (index,song) in MPPositive_LoadCoreModel.shared.songViewModels.enumerated() {
@ -89,4 +92,5 @@ extension MPPositive_LoveSongsViewController: UITableViewDataSource, UITableView
MP_PlayerManager.shared.loadPlayer = lodaViewModel
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
}

View File

@ -70,6 +70,9 @@ extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableV
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
//
var array:[MPPositive_SongItemModel] = []
for (index, song) in MPPositive_LoadCoreModel.shared.loadViewModels.enumerated() {
@ -92,4 +95,5 @@ extension MPPositive_OfflineSongsViewController: UITableViewDataSource, UITableV
MP_PlayerManager.shared.loadPlayer = lodaViewModel
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
}

View File

@ -181,6 +181,9 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
let listVC = MPPositive_ListShowViewController(item.browseItem.browseId ?? "", params: "", title: item.title ?? "", subtitle: item.subtitle ?? "")
navigationController?.pushViewController(listVC, animated: true)
case .single:
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
///
//next
MP_NetWorkManager.shared.requestNextList(item.browseItem.playListId ?? "", videoId: item.browseItem.videoId ?? ""){ [weak self] listSongs in
@ -191,6 +194,7 @@ extension MPPositive_ArtistShowViewController: JXPagingViewDelegate{
MP_PlayerManager.shared.loadPlayer = lodaViewModel
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
case .none:
break
}

View File

@ -108,6 +108,9 @@ extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDeleg
switch item.browseItem.itemType {
case .single:
///
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
//next
MP_NetWorkManager.shared.requestNextList(item.browseItem.playListId ?? "", videoId: item.browseItem.videoId ?? ""){ [weak self] listSongs in
guard let self = self else {return}
@ -117,6 +120,7 @@ extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDeleg
MP_PlayerManager.shared.loadPlayer = lodaViewModel
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
case .list:
//
let listVC = MPPositive_ListShowViewController(item.browseItem.browseId ?? "", params: item.browseItem.params ?? "", title: item.title ?? "", subtitle: item.subtitle ?? "")

View File

@ -278,6 +278,9 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
//next
MP_NetWorkManager.shared.requestNextList(listOrAlbum.items[indexPath.row].browseItem.playListId ?? "", videoId: listOrAlbum.items[indexPath.row].browseItem.videoId ?? ""){ [weak self] listSongs in
guard let self = self else {return}
@ -289,4 +292,5 @@ extension MPPositive_ListShowViewController: UITableViewDataSource, UITableViewD
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
}
}

View File

@ -15,7 +15,7 @@ class MPPositive_PlayerListShowViewController: UIViewController {
let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.contentInset = .init(top: 0, left: 0, bottom: 30*width, right: 0)
tableView.contentInset = .init(top: 0, left: 0, bottom: bottomPadding, right: 0)
tableView.estimatedRowHeight = 200
tableView.rowHeight = UITableView.automaticDimension
tableView.dataSource = self

View File

@ -106,6 +106,9 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
let listVC = MPPositive_ListShowViewController(item.item.browseId ?? "", params: "", title: item.title ?? "", subtitle: item.subtitle ?? "")
navigationController?.pushViewController(listVC, animated: true)
case .single:
MPPositive_Debouncer.shared.call {
[weak self] in
guard let self = self else {return}
///
//next
MP_NetWorkManager.shared.requestNextList(item.item.playListId ?? "", videoId: item.item.videoId ?? ""){ [weak self] listSongs in
@ -116,6 +119,7 @@ class MPPositive_SearchResultShowViewController: MPPositive_BaseViewController {
MP_PlayerManager.shared.loadPlayer = lodaViewModel
NotificationCenter.notificationKey.post(notificationName: .pup_player_vc)
}
}
case .none:
break
}

View File

@ -0,0 +1,69 @@
//
// MPPositive_MoreOperationTableViewCell.swift
// MusicPlayer
//
// Created by Mr.Zhou on 2024/5/29.
//
import UIKit
import DownloadButton
class MPPositive_MoreOperationDownLoadTableViewCell: UITableViewCell {
//
private lazy var iconImageView:UIImageView = {
let imageView:UIImageView = .init()
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true
return imageView
}()
//
private lazy var LoadBtn:PKDownloadButton = {
let btn:PKDownloadButton = .init()
//
btn.isUserInteractionEnabled = false
btn.downloadedButton.cleanDefaultAppearance()
// btn.downloadedButton.setBackgroundImage(UIImage(named: ""), for: <#T##UIControl.State#>)
return btn
}()
private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .white, textAlignment: .left)
var title:String!{
didSet{
iconImageView.image = UIImage(named: title)
titleLabel.text = title
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = .clear
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
private func configure() {
contentView.addSubview(iconImageView)
iconImageView.snp.makeConstraints { make in
make.width.height.equalTo(24*width)
make.top.equalToSuperview().offset(12*width).priority(999)
make.bottom.equalToSuperview().offset(-12*width)
make.left.equalToSuperview().offset(18*width)
}
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalTo(iconImageView.snp.right).offset(12*width)
}
}
}

View File

@ -17,13 +17,6 @@ class MPPositive_LibraryTableViewCell: UITableViewCell {
}()
private lazy var titleLabel:UILabel = createLabel(font: .systemFont(ofSize: 14*width, weight: .medium), textColor: .white, textAlignment: .left)
private lazy var subtitleLabel:UILabel = createLabel(font: .systemFont(ofSize: 12*width, weight: .regular), textColor: .init(hex: "#FFFFFF", alpha: 0.5), textAlignment: .left)
///
private lazy var moreBtn:UIButton = {
let btn:UIButton = .init()
btn.setBackgroundImage(UIImage(named: "Song_More'logo"), for: .normal)
btn.addTarget(self, action: #selector(moreActionClick(_ :)), for: .touchUpInside)
return btn
}()
var listViewModel:MPPositive_CollectionListViewModel!{
didSet{
listViewModel.setImage(iconImageView)
@ -58,17 +51,11 @@ class MPPositive_LibraryTableViewCell: UITableViewCell {
make.bottom.equalToSuperview().offset(-6*width)
make.left.equalToSuperview().offset(18*width)
}
contentView.addSubview(moreBtn)
moreBtn.snp.makeConstraints { make in
make.width.height.equalTo(24*width)
make.centerY.equalTo(iconImageView.snp.centerY)
make.right.equalToSuperview().offset(-18*width)
}
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.top.equalTo(iconImageView.snp.top).offset(10*width)
make.left.equalTo(iconImageView.snp.right).offset(12*width)
make.right.equalTo(moreBtn.snp.left).offset(-10*width)
make.right.equalToSuperview().offset(-10*width)
}
contentView.addSubview(subtitleLabel)
subtitleLabel.snp.makeConstraints { make in
@ -77,8 +64,8 @@ class MPPositive_LibraryTableViewCell: UITableViewCell {
make.right.equalTo(titleLabel.snp.right)
}
}
//
@objc private func moreActionClick(_ sender:UIButton) {
}
// //
// @objc private func moreActionClick(_ sender:UIButton) {
//
// }
}

View File

@ -45,9 +45,7 @@ class MPPositive_ArtistShowHeaderView: UIView {
let items = MPPositive_CollectionArtistModel.fetch(.init(format: "artistId == %@", self.artistid))
for item in items {
if item.artistId == self.artistid {
self.collectionBtn.isSelected = true
}
}
}

View File

@ -3,7 +3,7 @@
// Created by Mr.Zhou on 2024/5/8.
import UIKit
import DownloadButton
//BView(View)
class MPPositive_PlayerCoverView: UIView {
//View
@ -37,6 +37,26 @@ class MPPositive_PlayerCoverView: UIView {
btn.addTarget(self, action: #selector(loadActionClick(_ :)), for: .touchUpInside)
return btn
}()
///
lazy var downloadButton:PKDownloadButton = {
let btn:PKDownloadButton = .init()
//
btn.startDownloadButton.cleanDefaultAppearance()
btn.startDownloadButton.setBackgroundImage(UIImage(named: "Song_Unload'logo"), for: .normal)
//
btn.downloadedButton.setBackgroundImage(UIImage(named: "Song_Loaded'logo"), for: .normal)
btn.downloadedButton.isUserInteractionEnabled = false
//
btn.stopDownloadButton.stopButton.setImage(UIImage(named: "download"), for: .normal)
btn.stopDownloadButton.isUserInteractionEnabled = false
btn.stopDownloadButton.tintColor = UIColor(hex: "#80F988")
btn.stopDownloadButton.filledLineWidth = 1.5
btn.stopDownloadButton.filledLineStyleOuter = true
//
// btn.pendingView.tintColor = .
return btn
}()
///View
lazy var sliderView:MPPositive_PlayerSilder = {
let sliderView:MPPositive_PlayerSilder = .init(frame: .init(x: 0, y: 0, width: 335*width, height: 6*width))

View File

@ -93,9 +93,13 @@ class MPPositive_PlayerListShowTableViewCell: UITableViewCell {
}
//
@objc private func removeClick(_ sender:UIButton) {
MPPositive_Debouncer.shared.call{
[weak self] in
guard let self = self else {return}
guard removeBlock != nil else {
return
}
removeBlock!()
}
}
}

View File

@ -25,6 +25,8 @@ pod 'JXPagingView/Paging'
pod 'MJRefresh'
#流音频播放
pod 'FreeStreamer'
#下载按钮
pod "DownloadButton"
#下载框架
pod 'Tiercel'
end

View File

@ -1,5 +1,6 @@
PODS:
- Alamofire (5.9.1)
- DownloadButton (0.1.0)
- FreeStreamer (4.0.0):
- Reachability (~> 3.0)
- IQKeyboardManagerSwift (6.5.16)
@ -17,6 +18,7 @@ PODS:
DEPENDENCIES:
- Alamofire
- DownloadButton
- FreeStreamer
- IQKeyboardManagerSwift
- JXPagingView/Paging
@ -31,6 +33,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- Alamofire
- DownloadButton
- FreeStreamer
- IQKeyboardManagerSwift
- JXPagingView
@ -45,6 +48,7 @@ SPEC REPOS:
SPEC CHECKSUMS:
Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c
DownloadButton: 49a21a89e0d7d1b42d9134f79aaa40e727cd57c3
FreeStreamer: 7e9c976045701ac2f7e9c14c17245203c37bf2ea
IQKeyboardManagerSwift: 12d89768845bb77b55cc092ecc2b1f9370f06b76
JXPagingView: afdd2e9af09c90160dd232b970d603cc6e7ddd0e
@ -57,6 +61,6 @@ SPEC CHECKSUMS:
SwiftDate: 72d28954e8e1c6c1c0f917ccc8005e4f83c7d4b2
Tiercel: c0a73f876a72800333b15f4e7e48791f4ad21e90
PODFILE CHECKSUM: 3804949e23587f6d341ef21aa5e0b1c55a818968
PODFILE CHECKSUM: 0b090feb210ab3fcc05329c1820f94cdb8cf93f6
COCOAPODS: 1.15.2

20
Pods/DownloadButton/LICENSE generated Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2015 Pavel Katunin <wk.katunin@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---------------------------------------------------------------------
Contact details
Email: wk.katunin@gmail.com
Site: https://ru.linkedin.com/in/pavelkatunin

View File

@ -0,0 +1,19 @@
//
// CALayer+PKDownloadButtonAnimations.h
// Download
//
// Created by Pavel on 31/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
@interface CALayer (PKDownloadButtonAnimations)
- (void)addRotationAnimationWithKey:(NSString *)animationKey
fullRotationDuration:(NSTimeInterval)fullRotationDuration;
- (void)removeRotationAnimationWithKey:(NSString *)animationKey;
- (void)removeRotationAnimationWithKey:(NSString *)animationKey
fullRotationDuration:(NSTimeInterval)fullRotationDuration;
@end

View File

@ -0,0 +1,60 @@
//
// CALayer+PKDownloadButtonAnimations.m
// Download
//
// Created by Pavel on 31/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "CALayer+PKDownloadButtonAnimations.h"
static NSString *kRorationEndKey = @"PKLayerRorationEndKey";
@implementation CALayer (PKDownloadButtonAnimations)
- (void)addRotationAnimationWithKey:(NSString *)animationKey fullRotationDuration:(NSTimeInterval)fullRotationDuration {
NSNumber *fromValue = [self.presentationLayer valueForKeyPath:@"transform.rotation"];
[self removeAnimationForKey:kRorationEndKey];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotationAnimation.fromValue = fromValue;
rotationAnimation.toValue = @([rotationAnimation.fromValue floatValue] + (2. * M_PI));
rotationAnimation.duration = fullRotationDuration;
rotationAnimation.repeatCount = HUGE_VALF;
rotationAnimation.removedOnCompletion = NO;
[self addAnimation:rotationAnimation forKey:animationKey];
}
- (void)removeRotationAnimationWithKey:(NSString *)animationKey {
[self removeRotationAnimationWithKey:animationKey fullRotationDuration:0.0];
}
- (void)removeRotationAnimationWithKey:(NSString *)animationKey fullRotationDuration:(NSTimeInterval)fullRotationDuration {
NSNumber *fromValue = [self.presentationLayer valueForKeyPath:@"transform.rotation"];
NSNumber *toValue = @((fromValue.doubleValue < 0.0) ? 0.0 : 2.0 * M_PI);
[self removeAnimationForKey:animationKey];
const NSTimeInterval animationDuration = ABS(toValue.doubleValue - fromValue.doubleValue) * (fullRotationDuration / (2.0 * M_PI));
if (fromValue.doubleValue != 0.0 && ![fromValue isEqualToNumber:toValue] && animationDuration > 0.0) {
[CATransaction begin];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotationAnimation.fromValue = fromValue;
rotationAnimation.toValue = toValue;
rotationAnimation.duration = animationDuration;
[CATransaction setCompletionBlock:^{
[self removeAnimationForKey:kRorationEndKey];
}];
[self addAnimation:rotationAnimation forKey:kRorationEndKey];
[CATransaction commit];
}
else {
[self removeAnimationForKey:kRorationEndKey];
}
}
@end

View File

@ -0,0 +1,18 @@
#import <UIKit/UIKit.h>
@interface NSLayoutConstraint (PKDownloadButton)
+ (NSArray *)constraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets;
+ (NSArray *)horizontalConstraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets;
+ (NSArray *)verticalConstraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets;
+ (NSLayoutConstraint *)constraintForView:(UIView *)view withWidth:(CGFloat)width;
+ (NSLayoutConstraint *)constraintForView:(UIView *)view withHeight:(CGFloat)height;
+ (NSArray *)constraintsForView:(UIView *)view withSize:(CGSize)size;
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format views:(NSDictionary *)views;
+ (NSLayoutConstraint *)constraintForCenterByYView:(UIView *)overlay withView:(UIView *)view;
+ (NSLayoutConstraint *)constraintForCenterByXView:(UIView *)overlay withView:(UIView *)view;
// Constraints for center view above it's superview
+ (NSArray *)constraintsForCenterView:(UIView *)overlay;
+ (NSArray *)constraintsForCenterView:(UIView *)overlay withView:(UIView *)view;
@end

View File

@ -0,0 +1,99 @@
#import "NSLayoutConstraint+PKDownloadButton.h"
@implementation NSLayoutConstraint (PKDownloadButton)
+ (NSArray *)constraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets {
NSArray *horizontalConstraints = [self horizontalConstraintsForWrappedSubview:view withInsets:insets];
NSArray *verticalConstraints = [self verticalConstraintsForWrappedSubview:view withInsets:insets];
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[horizontalConstraints count] + [verticalConstraints count]];
[resultArray addObjectsFromArray:horizontalConstraints];
[resultArray addObjectsFromArray:verticalConstraints];
return resultArray;
}
+ (NSArray *)horizontalConstraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets {
NSString *horizontalConstraintsFormat = [NSString stringWithFormat:@"H:|-(%d)-[view]-(%d)-|",
(int)insets.left,
(int)roundf(insets.right)];
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:horizontalConstraintsFormat
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(view)];
return horizontalConstraints;
}
+ (NSArray *)verticalConstraintsForWrappedSubview:(UIView *)view withInsets:(UIEdgeInsets)insets {
NSString *verticalConstraintsFormat = [NSString stringWithFormat:@"V:|-(%d)-[view]-(%d)-|", (int)insets.top, (int)insets.bottom];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:verticalConstraintsFormat
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(view)];
return verticalConstraints;
}
+ (NSLayoutConstraint *)constraintForView:(UIView *)view withWidth:(CGFloat)width {
return [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:width];
}
+ (NSLayoutConstraint *)constraintForView:(UIView *)view withHeight:(CGFloat)height {
return [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:height];
}
+ (NSArray *)constraintsForView:(UIView *)view withSize:(CGSize)size {
NSLayoutConstraint *width = [NSLayoutConstraint constraintForView:view
withWidth:size.width];
NSLayoutConstraint *height = [NSLayoutConstraint constraintForView:view
withHeight:size.height];
return @[width, height];
}
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format views:(NSDictionary *)views {
return [self constraintsWithVisualFormat:format options:0 metrics:nil views:views];
}
+ (NSLayoutConstraint *)constraintForCenterByXView:(UIView *)overlay withView:(UIView *)view {
return [NSLayoutConstraint constraintWithItem:overlay
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
}
+ (NSLayoutConstraint *)constraintForCenterByYView:(UIView *)overlay withView:(UIView *)view {
return [NSLayoutConstraint constraintWithItem:overlay
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
}
+ (NSArray *)constraintsForCenterView:(UIView *)overlay {
return [self constraintsForCenterView:overlay withView:overlay.superview];
}
+ (NSArray *)constraintsForCenterView:(UIView *)overlay withView:(UIView *)view {
NSMutableArray *constraints = [NSMutableArray array];
[constraints addObject:[self constraintForCenterByXView:overlay withView:view]];
[constraints addObject:[self constraintForCenterByYView:overlay withView:view]];
return constraints;
}
@end

View File

@ -0,0 +1,20 @@
//
// PKCircleProgressView.h
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface PKCircleProgressView : UIView
@property (nonatomic, assign) IBInspectable CGFloat progress; /// 0.f - 1.0f
@property (nonatomic, assign) IBInspectable CGFloat filledLineWidth; /// 0.f +
@property (nonatomic, assign) IBInspectable CGFloat emptyLineWidth; /// 0.f +
@property (nonatomic, assign) IBInspectable CGFloat radius; /// 0.f +
@property (nonatomic, assign) IBInspectable BOOL filledLineStyleOuter;
@end

View File

@ -0,0 +1,181 @@
//
// PKCircleProgressView.m
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "PKCircleProgressView.h"
#import "UIColor+PKDownloadButton.h"
#import "PKCircleView.h"
#import "NSLayoutConstraint+PKDownloadButton.h"
static const CGFloat kDefaultRaduis = 13.f;
static const CGFloat kDefaultFilledLineWidth = 3.f;
static const CGFloat kDefaultEmptyLineWidth = 1.f;
static const CGFloat kStartAngle = M_PI * 1.5;
@interface PKCircleProgressView ()
@property (nonatomic, assign) CGFloat startAngle;
@property (nonatomic, assign) CGFloat endAngle;
@property (nonatomic, weak) PKCircleView *emptyLineCircleView;
@property (nonatomic, weak) PKCircleView *filledLineCircleView;
@property (nonatomic, weak) NSLayoutConstraint *emptyLineCircleWidth;
@property (nonatomic, weak) NSLayoutConstraint *emptyLineCircleHeight;
@property (nonatomic, weak) NSLayoutConstraint *filledLineCircleWidth;
@property (nonatomic, weak) NSLayoutConstraint *filledLineCircleHeight;
@property (nonatomic, assign) CGFloat emptyLineCircleSize;
@property (nonatomic, assign) CGFloat filledLineCircleSize;
- (PKCircleView *)createEmptyLineCircleView;
- (PKCircleView *)createFilledLineCircleView;
- (NSArray *)createCircleConstraints;
@end
static PKCircleProgressView *CommonInit(PKCircleProgressView *self) {
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.startAngle = kStartAngle;
self.endAngle = self.startAngle + (M_PI * 2);
self.clipsToBounds = NO;
PKCircleView *emptyLineCircleView = [self createEmptyLineCircleView];
self.emptyLineCircleView = emptyLineCircleView;
emptyLineCircleView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:emptyLineCircleView];
PKCircleView *filledLineCircleView = [self createFilledLineCircleView];
self.filledLineCircleView = filledLineCircleView;
filledLineCircleView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:filledLineCircleView];
[self addConstraints:[self createCircleConstraints]];
self.emptyLineWidth = kDefaultEmptyLineWidth;
self.filledLineWidth = kDefaultFilledLineWidth;
self.radius = kDefaultRaduis;
}
return self;
}
@implementation PKCircleProgressView
#pragma mark - initilaization / deallocation
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
#pragma mark - properties
- (void)setEmptyLineCircleSize:(CGFloat)emptyLineCircleSize {
self.emptyLineCircleWidth.constant = emptyLineCircleSize;
self.emptyLineCircleHeight.constant = emptyLineCircleSize;
}
- (void)setFilledLineCircleSize:(CGFloat)filledLineCircleSize {
self.filledLineCircleWidth.constant = filledLineCircleSize;
self.filledLineCircleHeight.constant = filledLineCircleSize;
}
- (void)setProgress:(CGFloat)progress {
_progress = progress;
self.filledLineCircleView.startAngleRadians = self.startAngle;
self.filledLineCircleView.endAngleRadians = (self.endAngle - self.startAngle) * progress + self.startAngle;
[self setNeedsDisplay];
}
- (void)setFilledLineWidth:(CGFloat)filledLineWidth {
_filledLineWidth = filledLineWidth;
self.filledLineCircleView.lineWidth = filledLineWidth;
[self setNeedsUpdateConstraints];
}
- (void)setEmptyLineWidth:(CGFloat)emptyLineWidth {
_emptyLineWidth = emptyLineWidth;
self.emptyLineCircleView.lineWidth = emptyLineWidth;
[self setNeedsUpdateConstraints];
}
- (void)setRadius:(CGFloat)radius {
_radius = radius;
[self setNeedsUpdateConstraints];
}
- (void)setFilledLineStyleOuter:(BOOL)filledLineStyleOuter {
_filledLineStyleOuter = filledLineStyleOuter;
[self setNeedsUpdateConstraints];
}
#pragma mark - UIView
- (void)updateConstraints {
[super updateConstraints];
self.emptyLineCircleSize = self.radius * 2.f;
CGFloat deltaRaduis = 0.f;
if (self.filledLineStyleOuter) {
deltaRaduis = - self.emptyLineCircleView.lineWidth / 2.f + self.filledLineCircleView.lineWidth;
}
else {
deltaRaduis = - self.emptyLineCircleView.lineWidth / 2.f;
}
self.filledLineCircleSize = self.radius * 2.f + deltaRaduis * 2.f;
}
#pragma mark - private methods
- (PKCircleView *)createEmptyLineCircleView {
PKCircleView *emptyCircelView = [[PKCircleView alloc] init];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintForView:emptyCircelView
withWidth:0.f];
self.emptyLineCircleWidth = widthConstraint;
[emptyCircelView addConstraint:widthConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintForView:emptyCircelView
withHeight:0.f];
self.emptyLineCircleHeight = heightConstraint;
[emptyCircelView addConstraint:heightConstraint];
return emptyCircelView;
}
- (PKCircleView *)createFilledLineCircleView {
PKCircleView *filledCircelView = [[PKCircleView alloc] init];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintForView:filledCircelView
withWidth:0.f];
self.filledLineCircleWidth = widthConstraint;
[filledCircelView addConstraint:widthConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintForView:filledCircelView
withHeight:0.f];
self.filledLineCircleHeight = heightConstraint;
[filledCircelView addConstraint:heightConstraint];
return filledCircelView;
}
- (NSArray *)createCircleConstraints {
NSMutableArray *constraints = [NSMutableArray array];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsForCenterView:self.emptyLineCircleView
withView:self]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsForCenterView:self.filledLineCircleView
withView:self]];
return constraints;
}
@end

View File

@ -0,0 +1,18 @@
//
// PKCircleView.h
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface PKCircleView : UIView
@property (nonatomic, assign) IBInspectable CGFloat startAngleRadians;
@property (nonatomic, assign) IBInspectable CGFloat endAngleRadians;
@property (nonatomic, assign) IBInspectable CGFloat lineWidth;
@end

View File

@ -0,0 +1,92 @@
//
// PKCircleView.m
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "PKCircleView.h"
#import "UIColor+PKDownloadButton.h"
static const CGFloat kDefaultLineWidth = 1.f;
@interface PKCircleView ()
- (void)drawCircleRadius:(CGFloat)radius
rect:(CGRect)rect
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngel
lineWidth:(CGFloat)lineWidth;
@end
static PKCircleView *CommonInit(PKCircleView *self) {
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.startAngleRadians = M_PI * 1.5;
self.endAngleRadians = self.startAngleRadians + (M_PI * 2);
self.lineWidth = kDefaultLineWidth;
}
return self;
}
@implementation PKCircleView
#pragma mark - initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
#pragma mark - properties
- (void)setLineWidth:(CGFloat)lineWidth {
_lineWidth = lineWidth;
[self setNeedsDisplay];
}
- (void)setStartAngleRadians:(CGFloat)startAngleRadians {
_startAngleRadians = startAngleRadians;
[self setNeedsDisplay];
}
- (void)setEndAngleRadians:(CGFloat)endAngleRadians {
_endAngleRadians = endAngleRadians;
[self setNeedsDisplay];
}
#pragma mark - UIView
- (void)drawRect:(CGRect)rect {
[self drawCircleRadius:MIN(rect.size.width / 2, rect.size.height / 2) - self.lineWidth / 2.f
rect:rect
startAngle:self.startAngleRadians
endAngle:self.endAngleRadians
lineWidth:self.lineWidth];
}
#pragma mark - private methods
- (void)drawCircleRadius:(CGFloat)radius
rect:(CGRect)rect
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngel
lineWidth:(CGFloat)lineWidth {
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[self.tintColor setStroke];
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:radius
startAngle:startAngle
endAngle:endAngel
clockwise:YES];
bezierPath.lineWidth = lineWidth;
[bezierPath stroke];
}
@end

View File

@ -0,0 +1,46 @@
//
// PKDownloadButton.h
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PKStopDownloadButton.h"
#import "PKCircleProgressView.h"
#import "PKPendingView.h"
typedef NS_ENUM(NSUInteger, PKDownloadButtonState) {
kPKDownloadButtonState_StartDownload,
kPKDownloadButtonState_Pending,
kPKDownloadButtonState_Downloading,
kPKDownloadButtonState_Downloaded
};
@class PKDownloadButton;
typedef void(^DownloadButtonTappedCallback)(PKDownloadButton *downloadButton, PKDownloadButtonState state);
@protocol PKDownloadButtonDelegate <NSObject>
- (void)downloadButtonTapped:(PKDownloadButton *)downloadButton
currentState:(PKDownloadButtonState)state;
@end
IB_DESIGNABLE
@interface PKDownloadButton : UIView
@property (nonatomic, weak) IBOutlet id <PKDownloadButtonDelegate> delegate;
@property (nonatomic, copy) DownloadButtonTappedCallback callback;
@property (nonatomic, weak, readonly) UIButton *startDownloadButton;
@property (nonatomic, weak, readonly) PKStopDownloadButton *stopDownloadButton;
@property (nonatomic, weak, readonly) UIButton *downloadedButton;
@property (nonatomic, weak, readonly) PKPendingView *pendingView;
@property (nonatomic, assign) PKDownloadButtonState state;
@end

View File

@ -0,0 +1,190 @@
//
// PKDownloadButton.m
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "PKDownloadButton.h"
#import "PKMacros.h"
#import "NSLayoutConstraint+PKDownloadButton.h"
#import "UIImage+PKDownloadButton.h"
#import "UIColor+PKDownloadButton.h"
#import "PKPendingView.h"
#import "UIButton+PKDownloadButton.h"
static NSDictionary *DefaultTitleAttributes() {
return @{ NSForegroundColorAttributeName : [UIColor defaultDwonloadButtonBlueColor],
NSFontAttributeName : [UIFont systemFontOfSize:14.f]};
}
static NSDictionary *HighlitedTitleAttributes() {
return @{ NSForegroundColorAttributeName : [UIColor whiteColor],
NSFontAttributeName : [UIFont systemFontOfSize:14.f]};
}
@interface PKDownloadButton ()
@property (nonatomic, weak) UIButton *startDownloadButton;
@property (nonatomic, weak) PKStopDownloadButton *stopDownloadButton;
@property (nonatomic, weak) UIButton *downloadedButton;
@property (nonatomic, weak) PKPendingView *pendingView;
@property (nonatomic, strong) NSMutableArray *stateViews;
- (UIButton *)createStartDownloadButton;
- (PKStopDownloadButton *)createStopDownloadButton;
- (UIButton *)createDownloadedButton;
- (PKPendingView *)createPendingView;
- (void)currentButtonTapped:(id)sender;
- (void)createSubviews;
- (NSArray *)createConstraints;
@end
static PKDownloadButton *CommonInit(PKDownloadButton *self) {
if (self != nil) {
[self createSubviews];
[self addConstraints:[self createConstraints]];
self.state = kPKDownloadButtonState_StartDownload;
}
return self;
}
@implementation PKDownloadButton
#pragma mark - Properties
- (void)setState:(PKDownloadButtonState)state {
_state = state;
[self.stateViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SafeObjClassCast(UIView, view, obj);
view.hidden = YES;
}];
switch (state) {
case kPKDownloadButtonState_StartDownload:
self.startDownloadButton.hidden = NO;
break;
case kPKDownloadButtonState_Pending:
self.pendingView.hidden = NO;
break;
case kPKDownloadButtonState_Downloading:
self.stopDownloadButton.hidden = NO;
self.stopDownloadButton.progress = 0.f;
break;
case kPKDownloadButtonState_Downloaded:
self.downloadedButton.hidden = NO;
break;
default:
NSAssert(NO, @"unsupported state");
break;
}
}
#pragma mark - Initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
#pragma mark - private methods
- (UIButton *)createStartDownloadButton {
UIButton *startDownloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
[startDownloadButton configureDefaultAppearance];
NSAttributedString *title = [[NSAttributedString alloc] initWithString:@"DOWNLOAD" attributes:DefaultTitleAttributes()];
[startDownloadButton setAttributedTitle:title forState:UIControlStateNormal];
NSAttributedString *highlitedTitle = [[NSAttributedString alloc] initWithString:@"DOWNLOAD" attributes:HighlitedTitleAttributes()];
[startDownloadButton setAttributedTitle:highlitedTitle forState:UIControlStateHighlighted];
[startDownloadButton addTarget:self
action:@selector(currentButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
return startDownloadButton;
}
- (PKStopDownloadButton *)createStopDownloadButton {
PKStopDownloadButton *stopDownloadButton = [[PKStopDownloadButton alloc] init];
[stopDownloadButton.stopButton addTarget:self action:@selector(currentButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
return stopDownloadButton;
}
- (UIButton *)createDownloadedButton {
UIButton *downloadedButton = [UIButton buttonWithType:UIButtonTypeCustom];
NSAttributedString *title = [[NSAttributedString alloc] initWithString:@"REMOVE" attributes:DefaultTitleAttributes()];
[downloadedButton setAttributedTitle:title forState:UIControlStateNormal];
NSAttributedString *highlitedTitle = [[NSAttributedString alloc] initWithString:@"REMOVE" attributes:HighlitedTitleAttributes()];
[downloadedButton setAttributedTitle:highlitedTitle forState:UIControlStateHighlighted];
[downloadedButton configureDefaultAppearance];
[downloadedButton addTarget:self
action:@selector(currentButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
return downloadedButton;
}
- (PKPendingView *)createPendingView {
PKPendingView *pendingView = [[PKPendingView alloc] init];
[pendingView addTarget:self action:@selector(currentButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
return pendingView;
}
- (void)currentButtonTapped:(id)sender {
[self.delegate downloadButtonTapped:self currentState:self.state];
BlockSafeRun(self.callback, self, self.state);
}
- (void)createSubviews {
self.stateViews = (__bridge_transfer NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
UIButton *startDownloadButton = [self createStartDownloadButton];
startDownloadButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:startDownloadButton];
self.startDownloadButton = startDownloadButton;
[self.stateViews addObject:startDownloadButton];
PKStopDownloadButton *stopDownloadButton = [self createStopDownloadButton];
stopDownloadButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:stopDownloadButton];
self.stopDownloadButton = stopDownloadButton;
[self.stateViews addObject:stopDownloadButton];
UIButton *downloadedButton = [self createDownloadedButton];
downloadedButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:downloadedButton];
self.downloadedButton = downloadedButton;
[self.stateViews addObject:downloadedButton];
PKPendingView *pendingView = [self createPendingView];
pendingView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:pendingView];
self.pendingView = pendingView;
[self.stateViews addObject:pendingView];
}
- (NSArray *)createConstraints {
NSMutableArray *constraints = [NSMutableArray array];
[self.stateViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SafeObjClassCast(UIView, view, obj);
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsForWrappedSubview:view
withInsets:UIEdgeInsetsZero]];
}];
return constraints;
}
@end

View File

@ -0,0 +1,42 @@
//
// PKMacros.h
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#ifndef Download_PKMacros_h
#define Download_PKMacros_h
#pragma mark - Block helpers
#define BlockSafeRun(block_, ...) do { if ((block_) != NULL) (block_)(__VA_ARGS__); } while (NO)
#define BlockSafeRunEx(defaultValue_, block_, ...) (((block_) != NULL) ? (block_)(__VA_ARGS__) : (defaultValue_))
#define BlockSafeRunOnTargetQueue(queue, block, ...) do { if ((block) != NULL) dispatch_async(queue, ^{ (block)(__VA_ARGS__); }); } while (0)
#define BlockSafeRunOnMainQueue(block, ...) BlockSafeRunOnTargetQueue(dispatch_get_main_queue(), (block), __VA_ARGS__)
#if __has_feature(objc_arc)
#define BlockWeakObject(o) __typeof__(o) __weak
#define BlockWeakSelf BlockWeakObject(self)
#define BlockStrongObject(o) __typeof__(o) __strong
#define BlockStrongSelf BlockStrongObject(self)
#define WeakifySelf BlockWeakSelf ___weakSelf___ = self; do {} while (0)
#define StrongifySelf BlockStrongSelf self = ___weakSelf___; do {} while (0)
#endif // __has_feature(objc_arc)
#define SafeObjClassCast(destClass_, resultObj_, originalObj_) \
destClass_ *resultObj_ = (destClass_ *)originalObj_;\
NSAssert2((resultObj_) == nil || [(resultObj_) isKindOfClass:[destClass_ class]],\
@"Incorrect cast: original object (%@) could not be casted to the destination class (%@)", \
(originalObj_), NSStringFromClass([destClass_ class]))
#define SafeObjProtocolCast(destProtocol_, resultObj_, originalObj_) \
id <destProtocol_> resultObj_ = (id <destProtocol_>)originalObj_;\
NSAssert2((resultObj_) == nil || [(resultObj_) conformsToProtocol:@protocol(destProtocol_)],\
@"Incorrect cast: original object (%@) could not be casted to the destination protocol (%@)", \
(originalObj_), NSStringFromProtocol(@protocol(destProtocol_)))
#endif // Download_PKMacros_h

View File

@ -0,0 +1,25 @@
//
// PKPendingView.h
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PKCircleView.h"
IB_DESIGNABLE
@interface PKPendingView : UIControl
@property (nonatomic, weak, readonly) PKCircleView *circleView;
@property (nonatomic, assign) IBInspectable CGFloat radius;
@property (nonatomic, assign) IBInspectable CGFloat lineWidth;
@property (nonatomic, assign) IBInspectable CGFloat emptyLineRadians;
@property (nonatomic, assign) IBInspectable CGFloat spinTime;
- (void)startSpin;
- (void)stopSpin;
@end

View File

@ -0,0 +1,134 @@
//
// PKPendingView.m
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "PKPendingView.h"
#import "NSLayoutConstraint+PKDownloadButton.h"
#import "CALayer+PKDownloadButtonAnimations.h"
static NSString *const kSpinAnimationKey = @"PKSpin";
static const CGFloat kDefaultRaduis = 13.f;
static const CGFloat kDefaultEmptyLineRadians = 0.4f;
static const CGFloat kDefaultSpinTime = 1.f;
@interface PKPendingView ()
@property (nonatomic, weak) PKCircleView *circleView;
@property (nonatomic, weak) NSLayoutConstraint *width;
@property (nonatomic, weak) NSLayoutConstraint *height;
@property (nonatomic, assign) BOOL isSpinning;
- (PKCircleView *)createCircleView;
- (NSArray *)createConstraints;
@end
static PKPendingView *CommonInit(PKPendingView *self) {
if (self != nil) {
PKCircleView *circleView = [self createCircleView];
circleView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:circleView];
self.circleView = circleView;
self.circleView.userInteractionEnabled = NO;
[self addConstraints:[self createConstraints]];
self.emptyLineRadians = kDefaultEmptyLineRadians;
self.radius = kDefaultRaduis;
self.clipsToBounds = NO;
self.spinTime = kDefaultSpinTime;
[self startSpin];
}
return self;
}
@implementation PKPendingView
#pragma mark - initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
#pragma mark - properties
- (void)setSpinTime:(CGFloat)spinTime {
_spinTime = spinTime;
[self.circleView.layer removeRotationAnimationWithKey:kSpinAnimationKey];
if (self.isSpinning) {
[self startSpin];
}
}
- (void)setRadius:(CGFloat)radius {
self.width.constant = radius * 2;
self.height.constant = radius * 2;
[self setNeedsLayout];
}
- (void)setLineWidth:(CGFloat)lineWidth {
self.circleView.lineWidth = lineWidth;
[self setNeedsDisplay];
}
- (CGFloat)lineWidth {
return self.circleView.lineWidth;
}
- (void)setEmptyLineRadians:(CGFloat)emptyLineRadians {
_emptyLineRadians = emptyLineRadians;
self.circleView.startAngleRadians = 1.5f * M_PI + emptyLineRadians / 2.f;
self.circleView.endAngleRadians = self.circleView.startAngleRadians + 2 * M_PI - emptyLineRadians;
[self setNeedsDisplay];
}
- (void)setTintColor:(UIColor *)tintColor {
self.circleView.tintColor = tintColor;
[self setNeedsDisplay];
}
#pragma mark - private methods
- (PKCircleView *)createCircleView {
PKCircleView *circleView = [[PKCircleView alloc] init];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintForView:circleView
withHeight:0.f];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintForView:circleView
withWidth:0.f];
[circleView addConstraints:@[heightConstraint, widthConstraint]];
self.width = widthConstraint;
self.height = heightConstraint;
return circleView;
}
- (NSArray *)createConstraints {
NSMutableArray *constraints = [NSMutableArray array];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsForCenterView:self.circleView
withView:self]];
return constraints;
}
- (void)startSpin {
self.isSpinning = YES;
[self.circleView.layer addRotationAnimationWithKey:kSpinAnimationKey
fullRotationDuration:self.spinTime];
}
- (void)stopSpin {
[self.circleView.layer removeRotationAnimationWithKey:kSpinAnimationKey];
self.isSpinning = NO;
}
@end

View File

@ -0,0 +1,18 @@
//
// PKStopDownloadButton.h
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PKCircleProgressView.h"
IB_DESIGNABLE
@interface PKStopDownloadButton : PKCircleProgressView
@property (nonatomic, assign) IBInspectable CGFloat stopButtonWidth;
@property (nonatomic, weak, readonly) UIButton *stopButton;
@end

View File

@ -0,0 +1,96 @@
//
// PKStopDownloadButton.m
// PKDownloadButton
//
// Created by Pavel on 28/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "PKStopDownloadButton.h"
#import "UIColor+PKDownloadButton.h"
#import "NSLayoutConstraint+PKDownloadButton.h"
#import "UIImage+PKDownloadButton.h"
static const CGFloat kDefaultStopButtonWidth = 8.f;
@interface PKStopDownloadButton ()
@property (nonatomic, weak) UIButton *stopButton;
- (UIButton *)createStopButton;
- (NSArray *)createStopButtonConstraints;
- (PKCircleProgressView *)createCircleProgressView;
@end
static PKStopDownloadButton *CommonInit(PKStopDownloadButton *self) {
if (self != nil) {
UIButton *stopButton = [self createStopButton];
stopButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:stopButton];
self.stopButton = stopButton;
[self addConstraints:[self createStopButtonConstraints]];
[self setNeedsDisplay];
}
return self;
}
@implementation PKStopDownloadButton
#pragma mark - properties
- (void)setStopButtonWidth:(CGFloat)stopButtonWidth {
_stopButtonWidth = stopButtonWidth;
[self.stopButton setImage:[UIImage stopImageOfSize:stopButtonWidth
color:self.tintColor]
forState:UIControlStateNormal];
[self setNeedsDisplay];
}
- (void)setTintColor:(UIColor *)tintColor {
[super setTintColor:tintColor];
[self.stopButton setImage:[UIImage stopImageOfSize:self.stopButtonWidth
color:tintColor]
forState:UIControlStateNormal];
[self setNeedsDisplay];
}
#pragma mark - initialization
- (instancetype)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
#pragma mark - private methods
- (UIButton *)createStopButton {
UIButton *stopButton = [UIButton buttonWithType:UIButtonTypeCustom];
_stopButtonWidth = kDefaultStopButtonWidth;
[stopButton setImage:[UIImage stopImageOfSize:_stopButtonWidth
color:[UIColor defaultDwonloadButtonBlueColor]]
forState:UIControlStateNormal];
return stopButton;
}
- (NSArray *)createStopButtonConstraints {
NSMutableArray *constraints = [NSMutableArray array];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsForWrappedSubview:self.stopButton
withInsets:UIEdgeInsetsZero]];
return constraints;
}
- (PKCircleProgressView *)createCircleProgressView {
PKCircleProgressView *circleProgressView = [[PKCircleProgressView alloc] init];
return circleProgressView;
}
@end

View File

@ -0,0 +1,16 @@
//
// UIButton+PKDownloadButton.h
// Download
//
// Created by Pavel on 01/06/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIButton (PKDownloadButton)
- (void)configureDefaultAppearance;
- (void)cleanDefaultAppearance;
@end

View File

@ -0,0 +1,31 @@
//
// UIButton+PKDownloadButton.m
// Download
//
// Created by Pavel on 01/06/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "UIButton+PKDownloadButton.h"
#import "UIImage+PKDownloadButton.h"
#import "UIColor+PKDownloadButton.h"
@implementation UIButton (PKDownloadButton)
- (void)configureDefaultAppearance {
UIImage *backImage = [UIImage buttonBackgroundWithColor:[UIColor defaultDwonloadButtonBlueColor]];
[self setBackgroundImage:[backImage resizableImageWithCapInsets:UIEdgeInsetsMake(15.f, 15.f, 15.f, 15.f)]
forState:UIControlStateNormal];
[self setBackgroundImage:[UIImage highlitedButtonBackgroundWithColor:[UIColor defaultDwonloadButtonBlueColor]]
forState:UIControlStateHighlighted];
}
- (void)cleanDefaultAppearance {
[self setBackgroundImage:nil forState:UIControlStateNormal];
[self setBackgroundImage:nil forState:UIControlStateHighlighted];
[self setAttributedTitle:nil forState:UIControlStateNormal];
[self setAttributedTitle:nil forState:UIControlStateHighlighted];
}
@end

View File

@ -0,0 +1,16 @@
//
// UIColor+PKDownloadButton.h
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIColor (PKDownloadButton)
+ (UIColor *)defaultDwonloadButtonBlueColor;
+ (UIColor *)defaultDwonloadButtonRedColor;
@end

View File

@ -0,0 +1,21 @@
//
// UIColor+PKDownloadButton.m
// Download
//
// Created by Pavel on 30/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "UIColor+PKDownloadButton.h"
@implementation UIColor (PKDownloadButton)
+ (UIColor *)defaultDwonloadButtonBlueColor {
return [UIColor colorWithRed:0.f green:113.f / 255.f blue:1.f alpha:1.f];
}
+ (UIColor *)defaultDwonloadButtonRedColor {
return [UIColor redColor];
}
@end

View File

@ -0,0 +1,17 @@
//
// UIImage+PKDownloadButton.h
// Download
//
// Created by Pavel on 31/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIImage (PKDownloadButton)
+ (UIImage *)stopImageOfSize:(CGFloat)size color:(UIColor *)color;
+ (UIImage *)buttonBackgroundWithColor:(UIColor *)color;
+ (UIImage *)highlitedButtonBackgroundWithColor:(UIColor *)color;
@end

View File

@ -0,0 +1,67 @@
//
// UIImage+PKDownloadButton.m
// Download
//
// Created by Pavel on 31/05/15.
// Copyright (c) 2015 Katunin. All rights reserved.
//
#import "UIImage+PKDownloadButton.h"
@implementation UIImage (PKDownloadButton)
+ (UIImage *)stopImageOfSize:(CGFloat)size color:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, 1.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[color setStroke];
CGRect stopImageRect = CGRectMake(0.f, 0.f, size, size);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextAddRect(context, stopImageRect);
CGContextFillRect(context, stopImageRect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (UIImage *)buttonBackgroundWithColor:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30.f, 30.f), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[color setStroke];
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(2.f, 2.f, 26.f, 26.f)
cornerRadius:4.f];
CGContextSetStrokeColorWithColor(context, color.CGColor);
bezierPath.lineWidth = 1.f;
[bezierPath stroke];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(10.f, 10.f, 10.f, 10.f)];
}
+ (UIImage *)highlitedButtonBackgroundWithColor:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(30.f, 30.f), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[color setStroke];
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(2.f, 2.f, 26.f, 26.f)
cornerRadius:4.f];
CGContextSetStrokeColorWithColor(context, color.CGColor);
bezierPath.lineWidth = 1.f;
[bezierPath stroke];
[color setFill];
[bezierPath fill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(10.f, 10.f, 10.f, 10.f)];
}
@end

129
Pods/DownloadButton/README.md generated Normal file
View File

@ -0,0 +1,129 @@
# DownloadButton
[![CI Status](http://img.shields.io/travis/Pavel Katunin/DownloadButton.svg?style=flat)](https://travis-ci.org/Pavel Katunin/DownloadButton)
[![Version](https://img.shields.io/cocoapods/v/DownloadButton.svg?style=flat)](http://cocoapods.org/pods/DownloadButton)
[![License](https://img.shields.io/cocoapods/l/DownloadButton.svg?style=flat)](http://cocoapods.org/pods/DownloadButton)
[![Platform](https://img.shields.io/cocoapods/p/DownloadButton.svg?style=flat)](http://cocoapods.org/pods/DownloadButton)
Customizable appstore style download button.
![Default button animation](https://cloud.githubusercontent.com/assets/1636737/7921348/7fadc250-08ad-11e5-9f01-9f7e1f173a97.gif)
![Default button](https://cloud.githubusercontent.com/assets/1636737/7920830/2c4470da-08aa-11e5-99be-e7e9a04479f8.png)
Customizable designable components. Editing with IB.
![Custom button animation](https://cloud.githubusercontent.com/assets/1636737/7921396/ec8b21c4-08ad-11e5-99a4-c73c971c0d42.gif)
![Custom button](https://cloud.githubusercontent.com/assets/1636737/7920909/7403d906-08aa-11e5-92d5-f92a09bc2bdc.png)
![Designable button](https://cloud.githubusercontent.com/assets/1636737/7921499/92a772ba-08ae-11e5-9222-e5bd229de6b2.gif)
## Usage
To run the example project, clone the repo, and run `pod install` from the Example directory first.
Check example project to see usage.
```objective-c
#import <DownloadButton/PKDownloadButton.h>
```
Usage for representing network operations:
```objective-c
#pragma mark - PKDownloadButtonDelegate
- (void)downloadButtonTapped:(PKDownloadButton *)downloadButton
currentState:(PKDownloadButtonState)state {
switch (state) {
case kPKDownloadButtonState_StartDownload:
self.downloadButton.state = kPKDownloadButtonState_Pending;
[self.pendingSimulator startDownload];
break;
case kPKDownloadButtonState_Pending:
[self.pendingSimulator cancelDownload];
self.downloadButton.state = kPKDownloadButtonState_StartDownload;
break;
case kPKDownloadButtonState_Downloading:
[self.downloaderSimulator cancelDownload];
self.downloadButton.state = kPKDownloadButtonState_StartDownload;
break;
case kPKDownloadButtonState_Downloaded:
self.downloadButton.state = kPKDownloadButtonState_StartDownload;
self.imageView.hidden = YES;
break;
default:
NSAssert(NO, @"unsupported state");
break;
}
}
#pragma mark - DownloaderSimulatorDelegate
- (void)simulator:(PKDownloaderSimulator *)simulator didUpdateProgress:(double)progress {
if (simulator == self.pendingSimulator) {
if (progress == 1.) {
self.downloadButton.state = kPKDownloadButtonState_Downloading;
[self.downloaderSimulator startDownload];
}
}
else if (simulator == self.downloaderSimulator) {
self.downloadButton.stopDownloadButton.progress = progress;
if (progress == 1) {
self.downloadButton.state = kPKDownloadButtonState_Downloaded;
self.imageView.hidden = NO;
}
}
}
```
Appearance customization:
```objective-c
#import "UIImage+PKDownloadButton.h"
#import "UIButton+PKDownloadButton.h"
[self.downloadButton.downloadedButton cleanDefaultAppearance];
[self.downloadButton.downloadedButton setBackgroundImage:[UIImage buttonBackgroundWithColor:[UIColor redColor]]
forState:UIControlStateNormal];
[self.downloadButton.downloadedButton setBackgroundImage:[UIImage highlitedButtonBackgroundWithColor:[UIColor redColor]]
forState:UIControlStateHighlighted];
[self.downloadButton.downloadedButton setTitle:@"delete" forState:UIControlStateNormal];
[self.downloadButton.downloadedButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[self.downloadButton.downloadedButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
self.downloadButton.stopDownloadButton.tintColor = [UIColor blackColor];
self.downloadButton.stopDownloadButton.filledLineStyleOuter = YES;
self.downloadButton.pendingView.tintColor = [UIColor orangeColor];
self.downloadButton.pendingView.radius = 24.f;
self.downloadButton.pendingView.emptyLineRadians = 2.f;
self.downloadButton.pendingView.spinTime = 3.f;
[self.downloadButton.startDownloadButton cleanDefaultAppearance];
[self.downloadButton.startDownloadButton setImage:[UIImage imageNamed:@"download_default"] forState:UIControlStateNormal];
```
## Requirements
Requires iOS SDK version > 8.0
## Installation
DownloadButton is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod "DownloadButton"
```
## Author
Pavel Katunin, wk.katunin@gmail.com
## License
DownloadButton is available under the Apache License, Version 2.0. See the LICENSE file for more info.

6
Pods/Manifest.lock generated
View File

@ -1,5 +1,6 @@
PODS:
- Alamofire (5.9.1)
- DownloadButton (0.1.0)
- FreeStreamer (4.0.0):
- Reachability (~> 3.0)
- IQKeyboardManagerSwift (6.5.16)
@ -17,6 +18,7 @@ PODS:
DEPENDENCIES:
- Alamofire
- DownloadButton
- FreeStreamer
- IQKeyboardManagerSwift
- JXPagingView/Paging
@ -31,6 +33,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- Alamofire
- DownloadButton
- FreeStreamer
- IQKeyboardManagerSwift
- JXPagingView
@ -45,6 +48,7 @@ SPEC REPOS:
SPEC CHECKSUMS:
Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c
DownloadButton: 49a21a89e0d7d1b42d9134f79aaa40e727cd57c3
FreeStreamer: 7e9c976045701ac2f7e9c14c17245203c37bf2ea
IQKeyboardManagerSwift: 12d89768845bb77b55cc092ecc2b1f9370f06b76
JXPagingView: afdd2e9af09c90160dd232b970d603cc6e7ddd0e
@ -57,6 +61,6 @@ SPEC CHECKSUMS:
SwiftDate: 72d28954e8e1c6c1c0f917ccc8005e4f83c7d4b2
Tiercel: c0a73f876a72800333b15f4e7e48791f4ad21e90
PODFILE CHECKSUM: 3804949e23587f6d341ef21aa5e0b1c55a818968
PODFILE CHECKSUM: 0b090feb210ab3fcc05329c1820f94cdb8cf93f6
COCOAPODS: 1.15.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_DownloadButton : NSObject
@end
@implementation PodsDummy_DownloadButton
@end

View File

@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -0,0 +1,27 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "CALayer+PKDownloadButtonAnimations.h"
#import "NSLayoutConstraint+PKDownloadButton.h"
#import "PKCircleProgressView.h"
#import "PKCircleView.h"
#import "PKDownloadButton.h"
#import "PKMacros.h"
#import "PKPendingView.h"
#import "PKStopDownloadButton.h"
#import "UIButton+PKDownloadButton.h"
#import "UIColor+PKDownloadButton.h"
#import "UIImage+PKDownloadButton.h"
FOUNDATION_EXPORT double DownloadButtonVersionNumber;
FOUNDATION_EXPORT const unsigned char DownloadButtonVersionString[];

View File

@ -0,0 +1,13 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/DownloadButton
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,6 @@
framework module DownloadButton {
umbrella header "DownloadButton-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,13 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/DownloadButton
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -24,6 +24,29 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## DownloadButton
Copyright (c) 2015 Pavel Katunin <wk.katunin@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---------------------------------------------------------------------
Contact details
Email: wk.katunin@gmail.com
Site: https://ru.linkedin.com/in/pavelkatunin
## FreeStreamer
Copyright (c) 2011-2018 Matias Muhonen <mmu@iki.fi> 穆马帝

View File

@ -41,6 +41,35 @@ THE SOFTWARE.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2015 Pavel Katunin &lt;wk.katunin@gmail.com&gt;
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---------------------------------------------------------------------
Contact details
Email: wk.katunin@gmail.com
Site: https://ru.linkedin.com/in/pavelkatunin</string>
<key>License</key>
<string>Apache License, Version 2.0</string>
<key>Title</key>
<string>DownloadButton</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2011-2018 Matias Muhonen &lt;mmu@iki.fi&gt; 穆马帝

View File

@ -1,5 +1,6 @@
${PODS_ROOT}/Target Support Files/Pods-MusicPlayer/Pods-MusicPlayer-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/DownloadButton/DownloadButton.framework
${BUILT_PRODUCTS_DIR}/FreeStreamer/FreeStreamer.framework
${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework
${BUILT_PRODUCTS_DIR}/JXPagingView/JXPagingView.framework

View File

@ -1,4 +1,5 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DownloadButton.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FreeStreamer.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardManagerSwift.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JXPagingView.framework

View File

@ -1,5 +1,6 @@
${PODS_ROOT}/Target Support Files/Pods-MusicPlayer/Pods-MusicPlayer-frameworks.sh
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
${BUILT_PRODUCTS_DIR}/DownloadButton/DownloadButton.framework
${BUILT_PRODUCTS_DIR}/FreeStreamer/FreeStreamer.framework
${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework
${BUILT_PRODUCTS_DIR}/JXPagingView/JXPagingView.framework

View File

@ -1,4 +1,5 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DownloadButton.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FreeStreamer.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardManagerSwift.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JXPagingView.framework

View File

@ -177,6 +177,7 @@ code_sign_if_enabled() {
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DownloadButton/DownloadButton.framework"
install_framework "${BUILT_PRODUCTS_DIR}/FreeStreamer/FreeStreamer.framework"
install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework"
install_framework "${BUILT_PRODUCTS_DIR}/JXPagingView/JXPagingView.framework"
@ -191,6 +192,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
install_framework "${BUILT_PRODUCTS_DIR}/DownloadButton/DownloadButton.framework"
install_framework "${BUILT_PRODUCTS_DIR}/FreeStreamer/FreeStreamer.framework"
install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework"
install_framework "${BUILT_PRODUCTS_DIR}/JXPagingView/JXPagingView.framework"

View File

@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer/FreeStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView/JXPagingView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView/JXSegmentedView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel/Tiercel.framework/Headers" $(SDKROOT)/usr/include/libxml2
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton/DownloadButton.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer/FreeStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView/JXPagingView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView/JXSegmentedView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel/Tiercel.framework/Headers" $(SDKROOT)/usr/include/libxml2
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -l"c++" -l"swiftCoreGraphics" -l"xml2" -framework "AVFoundation" -framework "Accelerate" -framework "Alamofire" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "Foundation" -framework "FreeStreamer" -framework "IQKeyboardManagerSwift" -framework "JXPagingView" -framework "JXSegmentedView" -framework "Kingfisher" -framework "MJRefresh" -framework "MediaPlayer" -framework "QuartzCore" -framework "Reachability" -framework "SVProgressHUD" -framework "SnapKit" -framework "SwiftDate" -framework "SystemConfiguration" -framework "Tiercel" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_LDFLAGS = $(inherited) -l"c++" -l"swiftCoreGraphics" -l"xml2" -framework "AVFoundation" -framework "Accelerate" -framework "Alamofire" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "DownloadButton" -framework "Foundation" -framework "FreeStreamer" -framework "IQKeyboardManagerSwift" -framework "JXPagingView" -framework "JXSegmentedView" -framework "Kingfisher" -framework "MJRefresh" -framework "MediaPlayer" -framework "QuartzCore" -framework "Reachability" -framework "SVProgressHUD" -framework "SnapKit" -framework "SwiftDate" -framework "SystemConfiguration" -framework "Tiercel" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)

View File

@ -1,11 +1,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer/FreeStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView/JXPagingView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView/JXSegmentedView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel/Tiercel.framework/Headers" $(SDKROOT)/usr/include/libxml2
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DownloadButton/DownloadButton.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FreeStreamer/FreeStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXPagingView/JXPagingView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/JXSegmentedView/JXSegmentedView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Tiercel/Tiercel.framework/Headers" $(SDKROOT)/usr/include/libxml2
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
OTHER_LDFLAGS = $(inherited) -l"c++" -l"swiftCoreGraphics" -l"xml2" -framework "AVFoundation" -framework "Accelerate" -framework "Alamofire" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "Foundation" -framework "FreeStreamer" -framework "IQKeyboardManagerSwift" -framework "JXPagingView" -framework "JXSegmentedView" -framework "Kingfisher" -framework "MJRefresh" -framework "MediaPlayer" -framework "QuartzCore" -framework "Reachability" -framework "SVProgressHUD" -framework "SnapKit" -framework "SwiftDate" -framework "SystemConfiguration" -framework "Tiercel" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_LDFLAGS = $(inherited) -l"c++" -l"swiftCoreGraphics" -l"xml2" -framework "AVFoundation" -framework "Accelerate" -framework "Alamofire" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "DownloadButton" -framework "Foundation" -framework "FreeStreamer" -framework "IQKeyboardManagerSwift" -framework "JXPagingView" -framework "JXSegmentedView" -framework "Kingfisher" -framework "MJRefresh" -framework "MediaPlayer" -framework "QuartzCore" -framework "Reachability" -framework "SVProgressHUD" -framework "SnapKit" -framework "SwiftDate" -framework "SystemConfiguration" -framework "Tiercel" -framework "UIKit" -weak_framework "Combine" -weak_framework "SwiftUI"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)