我的页面开发,网络状态监控
2
Podfile
@ -31,6 +31,8 @@ target 'ShortPlay' do
|
|||||||
pod 'KTVHTTPCache' #视频缓存
|
pod 'KTVHTTPCache' #视频缓存
|
||||||
pod 'HWPanModal' #底部弹出控制器
|
pod 'HWPanModal' #底部弹出控制器
|
||||||
pod 'Kingfisher' #图片加载
|
pod 'Kingfisher' #图片加载
|
||||||
|
pod 'EmptyStateKit' #空数据页面
|
||||||
|
pod 'ReachabilitySwift' #网络状态监控
|
||||||
|
|
||||||
|
|
||||||
target 'ShortPlayTests' do
|
target 'ShortPlayTests' do
|
||||||
|
10
Podfile.lock
@ -1,6 +1,7 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- Alamofire (5.10.2)
|
- Alamofire (5.10.2)
|
||||||
- CocoaAsyncSocket (7.6.5)
|
- CocoaAsyncSocket (7.6.5)
|
||||||
|
- EmptyStateKit (1.1.0)
|
||||||
- HWPanModal (0.9.9)
|
- HWPanModal (0.9.9)
|
||||||
- Kingfisher (8.3.1)
|
- Kingfisher (8.3.1)
|
||||||
- KTVHTTPCache (3.0.2):
|
- KTVHTTPCache (3.0.2):
|
||||||
@ -10,6 +11,7 @@ PODS:
|
|||||||
- Moya/Core (= 15.0.0)
|
- Moya/Core (= 15.0.0)
|
||||||
- Moya/Core (15.0.0):
|
- Moya/Core (15.0.0):
|
||||||
- Alamofire (~> 5.0)
|
- Alamofire (~> 5.0)
|
||||||
|
- ReachabilitySwift (5.2.4)
|
||||||
- SmartCodable (4.3.2)
|
- SmartCodable (4.3.2)
|
||||||
- SnapKit (5.7.1)
|
- SnapKit (5.7.1)
|
||||||
- Toast (4.1.1)
|
- Toast (4.1.1)
|
||||||
@ -21,11 +23,13 @@ PODS:
|
|||||||
- ZFPlayer/Core (4.1.4)
|
- ZFPlayer/Core (4.1.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- EmptyStateKit
|
||||||
- HWPanModal
|
- HWPanModal
|
||||||
- Kingfisher
|
- Kingfisher
|
||||||
- KTVHTTPCache
|
- KTVHTTPCache
|
||||||
- MJRefresh
|
- MJRefresh
|
||||||
- Moya
|
- Moya
|
||||||
|
- ReachabilitySwift
|
||||||
- SmartCodable
|
- SmartCodable
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- Toast
|
- Toast
|
||||||
@ -36,11 +40,13 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- Alamofire
|
- Alamofire
|
||||||
- CocoaAsyncSocket
|
- CocoaAsyncSocket
|
||||||
|
- EmptyStateKit
|
||||||
- HWPanModal
|
- HWPanModal
|
||||||
- Kingfisher
|
- Kingfisher
|
||||||
- KTVHTTPCache
|
- KTVHTTPCache
|
||||||
- MJRefresh
|
- MJRefresh
|
||||||
- Moya
|
- Moya
|
||||||
|
- ReachabilitySwift
|
||||||
- SmartCodable
|
- SmartCodable
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- Toast
|
- Toast
|
||||||
@ -50,17 +56,19 @@ SPEC REPOS:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
|
EmptyStateKit: dc41e9ce5c6089f67a49d063bce73ade9f2ba73f
|
||||||
HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2
|
HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2
|
||||||
Kingfisher: 3204d23de16b5ea53541c44ca5a8efb55741dec3
|
Kingfisher: 3204d23de16b5ea53541c44ca5a8efb55741dec3
|
||||||
KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86
|
KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86
|
||||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||||
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
||||||
|
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
|
||||||
SmartCodable: 88fbf3d65207c2376fdbce4b080a3d578cb51be8
|
SmartCodable: 88fbf3d65207c2376fdbce4b080a3d578cb51be8
|
||||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||||
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
||||||
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
||||||
|
|
||||||
PODFILE CHECKSUM: 13500f038833f93358c53b1941ce4a7f311776dd
|
PODFILE CHECKSUM: 3842e01f3a774298d51e08a9caf0e72ea42cd7bc
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
@ -448,7 +448,7 @@
|
|||||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@ -485,7 +485,7 @@
|
|||||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
102
ShortPlay.xcodeproj/xcshareddata/xcschemes/ShortPlay.xcscheme
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1620"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
buildArchitectures = "Automatic">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1DBC40582DA4EDFC0093FCB0"
|
||||||
|
BuildableName = "ShortPlay.app"
|
||||||
|
BlueprintName = "ShortPlay"
|
||||||
|
ReferencedContainer = "container:ShortPlay.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1DBC406E2DA4EE010093FCB0"
|
||||||
|
BuildableName = "ShortPlayTests.xctest"
|
||||||
|
BlueprintName = "ShortPlayTests"
|
||||||
|
ReferencedContainer = "container:ShortPlay.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1DBC40782DA4EE010093FCB0"
|
||||||
|
BuildableName = "ShortPlayUITests.xctest"
|
||||||
|
BlueprintName = "ShortPlayUITests"
|
||||||
|
ReferencedContainer = "container:ShortPlay.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1DBC40582DA4EDFC0093FCB0"
|
||||||
|
BuildableName = "ShortPlay.app"
|
||||||
|
BlueprintName = "ShortPlay"
|
||||||
|
ReferencedContainer = "container:ShortPlay.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "1DBC40582DA4EDFC0093FCB0"
|
||||||
|
BuildableName = "ShortPlay.app"
|
||||||
|
BlueprintName = "ShortPlay"
|
||||||
|
ReferencedContainer = "container:ShortPlay.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -33,7 +33,7 @@ extension AppDelegate {
|
|||||||
|
|
||||||
|
|
||||||
let backgroundImage = UIImage(color: .clear)
|
let backgroundImage = UIImage(color: .clear)
|
||||||
let backgroundColor = UIColor.clear
|
let backgroundColor = UIColor.black
|
||||||
let shadowImage = UIImage()
|
let shadowImage = UIImage()
|
||||||
let shadowColor = UIColor.clear
|
let shadowColor = UIColor.clear
|
||||||
|
|
||||||
|
@ -17,8 +17,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
self.appConfig()
|
self.appConfig()
|
||||||
|
|
||||||
SPLoginManager.manager.requestVisitorLogin(completer: nil)
|
SPLoginManager.manager.requestVisitorLogin(completer: nil)
|
||||||
|
///开启网络监控
|
||||||
|
// SPNetworkReachabilityManager.manager.startMonitoring()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
||||||
|
|
||||||
|
|
||||||
|
NetworkObserver.share.startMonitoring()
|
||||||
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -38,6 +43,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
|
// if SPNetworkReachabilityManager.manager.isReachable {
|
||||||
|
// SPLoginManager.manager.requestVisitorLogin(completer: nil)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class SPVideoAPI: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///收藏短剧
|
///收藏短剧
|
||||||
static func requestCollectShort(isCollect: Bool, shortPlayId: String, videoId: String, success: (() -> Void)?) {
|
static func requestCollectShort(isCollect: Bool, shortPlayId: String, videoId: String?, success: (() -> Void)?, failure: (() -> Void)? = nil) {
|
||||||
let path: String
|
let path: String
|
||||||
if isCollect {
|
if isCollect {
|
||||||
path = "/collect"
|
path = "/collect"
|
||||||
@ -51,12 +51,17 @@ class SPVideoAPI: NSObject {
|
|||||||
path = "/cancelCollect"
|
path = "/cancelCollect"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parameters: [String : Any] = [
|
||||||
|
"short_play_id" : shortPlayId,
|
||||||
|
]
|
||||||
|
|
||||||
|
if let videoId = videoId {
|
||||||
|
parameters["video_id"] = videoId
|
||||||
|
}
|
||||||
|
|
||||||
var param = SPNetworkParameters(path: path)
|
var param = SPNetworkParameters(path: path)
|
||||||
param.isLoding = true
|
param.isLoding = true
|
||||||
param.parameters = [
|
param.parameters = parameters
|
||||||
"short_play_id" : shortPlayId,
|
|
||||||
"video_id" : videoId
|
|
||||||
]
|
|
||||||
|
|
||||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<String>) in
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<String>) in
|
||||||
if response.code == SPNetworkCodeSucceed {
|
if response.code == SPNetworkCodeSucceed {
|
||||||
@ -65,6 +70,8 @@ class SPVideoAPI: NSObject {
|
|||||||
"state" : isCollect,
|
"state" : isCollect,
|
||||||
"id" : shortPlayId,
|
"id" : shortPlayId,
|
||||||
])
|
])
|
||||||
|
} else {
|
||||||
|
failure?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +90,20 @@ class SPVideoAPI: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///历史记录列表
|
||||||
|
static func requestPlayHistoryList(page: Int, completer: ((_ listModel: SPListModel<SPShortModel>?) -> Void)?) {
|
||||||
|
var param = SPNetworkParameters(path: "/myHistorys")
|
||||||
|
param.method = .get
|
||||||
|
param.parameters = [
|
||||||
|
"current_page" : page,
|
||||||
|
"page_size" : 20
|
||||||
|
]
|
||||||
|
|
||||||
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPListModel<SPShortModel>>) in
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPVideoAPI {
|
extension SPVideoAPI {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// SPNetworkReachabilityManager.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Network
|
||||||
|
import Combine
|
||||||
|
import Alamofire
|
||||||
|
|
||||||
|
class SPNetworkReachabilityManager {
|
||||||
|
// enum Status {
|
||||||
|
// case notReachable
|
||||||
|
// case reachableViaWiFi
|
||||||
|
// case reachableViaWWAN
|
||||||
|
// case ethernet
|
||||||
|
// }
|
||||||
|
|
||||||
|
static let manager: SPNetworkReachabilityManager = SPNetworkReachabilityManager()
|
||||||
|
|
||||||
|
private let reachabilityManager = NetworkReachabilityManager()
|
||||||
|
|
||||||
|
///是否有网
|
||||||
|
// @objc var isReachable: Bool {
|
||||||
|
// switch currentReachabilityStatus {
|
||||||
|
// case .notReachable:
|
||||||
|
// return false
|
||||||
|
// case .reachableViaWiFi, .reachableViaWWAN:
|
||||||
|
// return true
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func startMonitoring() {
|
||||||
|
|
||||||
|
reachabilityManager?.startListening(onUpdatePerforming: { status in
|
||||||
|
switch status {
|
||||||
|
case .notReachable:
|
||||||
|
print("网络不可用")
|
||||||
|
case .unknown:
|
||||||
|
print("网络状态未知")
|
||||||
|
case .reachable(.cellular):
|
||||||
|
print("蜂窝网络连接")
|
||||||
|
case .reachable(.ethernetOrWiFi):
|
||||||
|
print("WiFi 或有线网络连接")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPNetworkReachabilityManager {
|
||||||
|
///网络发生变化
|
||||||
|
@objc static let reachabilityDidChangeNotification = NSNotification.Name(rawValue: "reachabilityDidChangeNotification")
|
||||||
|
}
|
@ -11,6 +11,9 @@ class SPHomePageController: SPViewController {
|
|||||||
|
|
||||||
private var topModel: SPHomeTopModel?
|
private var topModel: SPHomeTopModel?
|
||||||
|
|
||||||
|
///是否在请求中
|
||||||
|
private var isRequesting = false
|
||||||
|
|
||||||
private lazy var categoryArr: [SPHomeCategoryModel] = {
|
private lazy var categoryArr: [SPHomeCategoryModel] = {
|
||||||
let arr = [
|
let arr = [
|
||||||
SPHomeCategoryModel(category_name: "Hot Picks".localized, category_id: nil, viewController: SPHomeViewController()),
|
SPHomeCategoryModel(category_name: "Hot Picks".localized, category_id: nil, viewController: SPHomeViewController()),
|
||||||
@ -55,7 +58,8 @@ class SPHomePageController: SPViewController {
|
|||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
||||||
|
|
||||||
sp_setupUI()
|
sp_setupUI()
|
||||||
|
|
||||||
requestData()
|
requestData()
|
||||||
@ -97,6 +101,9 @@ extension SPHomePageController {
|
|||||||
self.navigationController?.pushViewController(vc, animated: true)
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
|
requestData()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,9 +137,9 @@ extension SPHomePageController: JYPageControllerDelegate, JYPageControllerDataSo
|
|||||||
extension SPHomePageController {
|
extension SPHomePageController {
|
||||||
|
|
||||||
private func requestData() {
|
private func requestData() {
|
||||||
if self.topModel != nil { return }
|
if self.topModel != nil || isRequesting { return }
|
||||||
|
|
||||||
|
|
||||||
|
isRequesting = true
|
||||||
SPHomeAPI.requestHomeTopData { [weak self] model in
|
SPHomeAPI.requestHomeTopData { [weak self] model in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if let model = model {
|
if let model = model {
|
||||||
@ -142,6 +149,7 @@ extension SPHomePageController {
|
|||||||
}
|
}
|
||||||
self.pageView.reload()
|
self.pageView.reload()
|
||||||
}
|
}
|
||||||
|
self.isRequesting = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ class SPHomeViewController: SPHomeChildController {
|
|||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
||||||
|
|
||||||
// view.backgroundColor = .clear
|
// view.backgroundColor = .clear
|
||||||
requestModuleData()
|
requestModuleData()
|
||||||
_setupUI()
|
_setupUI()
|
||||||
@ -53,6 +55,12 @@ extension SPHomeViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SPHomeViewController {
|
||||||
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
extension SPHomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
extension SPHomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// SPAboutUsViewController.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPAboutUsViewController: SPViewController {
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -21,6 +21,11 @@ class SPMineViewController: SPViewController {
|
|||||||
return arr
|
return arr
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var headerView: SPMineHeaderView = {
|
||||||
|
let view = SPMineHeaderView(frame: CGRect(x: 0, y: 0, width: kSPScreenWidth, height: 200))
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var tableView: SPTableView = {
|
private lazy var tableView: SPTableView = {
|
||||||
let tableView = SPTableView(frame: .zero, style: .insetGrouped)
|
let tableView = SPTableView(frame: .zero, style: .insetGrouped)
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
@ -49,6 +54,8 @@ class SPMineViewController: SPViewController {
|
|||||||
extension SPMineViewController {
|
extension SPMineViewController {
|
||||||
|
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
|
tableView.tableHeaderView = self.headerView
|
||||||
|
|
||||||
view.addSubview(tableView)
|
view.addSubview(tableView)
|
||||||
|
|
||||||
tableView.snp.makeConstraints { make in
|
tableView.snp.makeConstraints { make in
|
||||||
@ -84,6 +91,10 @@ extension SPMineViewController: UITableViewDelegate, UITableViewDataSource {
|
|||||||
vc.urlStr = SPUserAgreementWebUrl
|
vc.urlStr = SPUserAgreementWebUrl
|
||||||
self.navigationController?.pushViewController(vc, animated: true)
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
|
case .aboutUs:
|
||||||
|
let vc = SPAboutUsViewController()
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
14
ShortPlay/Class/Mine/View/SPMineHeaderView.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// SPMineHeaderView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPMineHeaderView: UIView {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -7,12 +7,14 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class SPCollectListViewController: SPViewController {
|
class SPCollectListViewController: SPMyListChildViewController {
|
||||||
|
|
||||||
|
|
||||||
private lazy var dataArr: [SPShortModel] = []
|
private lazy var dataArr: [SPShortModel] = []
|
||||||
private var page: Int?
|
private var page: Int?
|
||||||
|
|
||||||
|
private lazy var deleteGorup = DispatchGroup()
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3)
|
let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3)
|
||||||
@ -67,6 +69,47 @@ class SPCollectListViewController: SPViewController {
|
|||||||
self?.collectionView.sp_endFooterRefreshing()
|
self?.collectionView.sp_endFooterRefreshing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var sp_isEditing: Bool {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isAllSelected: Bool {
|
||||||
|
var result = true
|
||||||
|
for model in dataArr {
|
||||||
|
if model.sp_isSelected != true {
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override var selectedCount: Int {
|
||||||
|
var result = 0
|
||||||
|
dataArr.forEach {
|
||||||
|
if $0.sp_isSelected ?? false {
|
||||||
|
result += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func setAllSelectedState(isSelected: Bool) {
|
||||||
|
dataArr.forEach {
|
||||||
|
$0.sp_isSelected = isSelected
|
||||||
|
}
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
allSelectedStateDidChange?(isSelected)
|
||||||
|
self.updateDeleteButtonState()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handelDeleteButton() {
|
||||||
|
requestDelete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPCollectListViewController {
|
extension SPCollectListViewController {
|
||||||
@ -87,16 +130,38 @@ extension SPCollectListViewController: UICollectionViewDelegate, UICollectionVie
|
|||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.sp_isEditing = self.sp_isEditing
|
||||||
cell.model = dataArr[indexPath.row]
|
cell.model = dataArr[indexPath.row]
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
return self.dataArr.count
|
let count = self.dataArr.count
|
||||||
|
if count == 0 {
|
||||||
|
let parameters = SPEmptyParameters(image: UIImage(named: "empty_image_01"))
|
||||||
|
let emptyState = SPEmptyState.normail(parameters: parameters)
|
||||||
|
self.collectionView.emptyState.format = emptyState.format
|
||||||
|
self.collectionView.emptyState.show(emptyState)
|
||||||
|
} else {
|
||||||
|
self.collectionView.emptyState.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = dataArr[indexPath.row]
|
||||||
|
if sp_isEditing {
|
||||||
|
model.sp_isSelected = !(model.sp_isSelected ?? false)
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
|
||||||
|
allSelectedStateDidChange?(isAllSelected)
|
||||||
|
self.updateDeleteButtonState()
|
||||||
|
} else {
|
||||||
|
let vc = SPPlayerDetailViewController()
|
||||||
|
vc.shortPlayId = model.short_play_id
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -117,13 +182,33 @@ extension SPCollectListViewController {
|
|||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
self.collectionView.reloadData()
|
self.collectionView.reloadData()
|
||||||
|
if self.sp_isEditing {
|
||||||
|
self.allSelectedStateDidChange?(self.isAllSelected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
completer?()
|
completer?()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func requestDelete() {
|
||||||
|
|
||||||
|
dataArr.forEach {
|
||||||
|
if $0.sp_isSelected == true, let shortPlayId = $0.short_play_id {
|
||||||
|
self.deleteGorup.enter()
|
||||||
|
SPVideoAPI.requestCollectShort(isCollect: false, shortPlayId: shortPlayId, videoId: nil) { [weak self] in
|
||||||
|
self?.deleteGorup.leave()
|
||||||
|
} failure: { [weak self] in
|
||||||
|
self?.deleteGorup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.deleteGorup.notify(queue: DispatchQueue.main) { [weak self] in
|
||||||
|
self?.requestDataList(page: 1, completer: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// SPMyListChildViewController.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPMyListChildViewController: SPViewController {
|
||||||
|
|
||||||
|
var sp_isEditing = false {
|
||||||
|
didSet {
|
||||||
|
deleteButton.isHidden = !sp_isEditing
|
||||||
|
if sp_isEditing {
|
||||||
|
self.view.bringSubviewToFront(deleteButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///是否被全选
|
||||||
|
var isAllSelected: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
///当前被选中的数量
|
||||||
|
var selectedCount: Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
///全选状态发生变化
|
||||||
|
var allSelectedStateDidChange: ((_ isAllSelected: Bool) -> Void)?
|
||||||
|
|
||||||
|
|
||||||
|
private(set) lazy var deleteButton: UIButton = {
|
||||||
|
let button = JXButton(type: .custom)
|
||||||
|
button.setTitle("0", for: .normal)
|
||||||
|
button.setTitleColor(.color9D9D9D(), for: .disabled)
|
||||||
|
button.setTitleColor(.colorF564B6(), for: .normal)
|
||||||
|
button.setImage(UIImage(named: "delete_icon_01"), for: .disabled)
|
||||||
|
button.setImage(UIImage(named: "delete_icon_02"), for: .normal)
|
||||||
|
button.jx_font = .fontRegular(ofSize: 14)
|
||||||
|
button.jx_setBorderColor(.color9D9D9D(), for: .disabled)
|
||||||
|
button.jx_setBorderColor(.colorF564B6(), for: .normal)
|
||||||
|
button.space = 7
|
||||||
|
button.layer.cornerRadius = 24
|
||||||
|
button.layer.masksToBounds = true
|
||||||
|
button.layer.borderWidth = 0.7
|
||||||
|
button.isHidden = true
|
||||||
|
button.addTarget(self, action: #selector(handelDeleteButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
updateDeleteButtonState()
|
||||||
|
|
||||||
|
view.addSubview(deleteButton)
|
||||||
|
|
||||||
|
deleteButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(20)
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview().offset(-20)
|
||||||
|
make.height.equalTo(48)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///设置全选状态
|
||||||
|
func setAllSelectedState(isSelected: Bool) { }
|
||||||
|
|
||||||
|
|
||||||
|
///更新删除按钮状态
|
||||||
|
func updateDeleteButtonState() {
|
||||||
|
let count = self.selectedCount
|
||||||
|
|
||||||
|
deleteButton.isEnabled = count > 0
|
||||||
|
let text = String(format: "Delet (%@)".localized, "\(selectedCount)")
|
||||||
|
deleteButton.setTitle(text, for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handelDeleteButton() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -12,12 +12,40 @@ class SPMyListViewController: SPViewController {
|
|||||||
|
|
||||||
private lazy var titles = ["Follow List".localized, "Play List".localized]
|
private lazy var titles = ["Follow List".localized, "Play List".localized]
|
||||||
|
|
||||||
private lazy var viewControllers: [SPViewController] = {
|
private lazy var viewControllers: [SPMyListChildViewController] = {
|
||||||
let vc1 = SPCollectListViewController()
|
let vc1 = SPCollectListViewController()
|
||||||
|
vc1.allSelectedStateDidChange = { [weak self] isAllSelected in
|
||||||
|
self?.allSelectedButton.isSelected = isAllSelected
|
||||||
|
}
|
||||||
let vc2 = SPPlayHistoryViewController()
|
let vc2 = SPPlayHistoryViewController()
|
||||||
|
vc2.allSelectedStateDidChange = { [weak self] isAllSelected in
|
||||||
|
self?.allSelectedButton.isSelected = isAllSelected
|
||||||
|
}
|
||||||
|
|
||||||
return [vc1, vc2]
|
return [vc1, vc2]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var sp_isEditing = false {
|
||||||
|
didSet {
|
||||||
|
editButton.isHidden = sp_isEditing
|
||||||
|
pageView.segmentedView.isHidden = sp_isEditing
|
||||||
|
|
||||||
|
cancelButton.isHidden = !sp_isEditing
|
||||||
|
allSelectedButton.isHidden = !sp_isEditing
|
||||||
|
pageView.pageContentScrollView.isScrollEnabled = !sp_isEditing
|
||||||
|
|
||||||
|
let vc = viewControllers[pageView.selectedIndex]
|
||||||
|
vc.sp_isEditing = sp_isEditing
|
||||||
|
|
||||||
|
if !sp_isEditing {
|
||||||
|
vc.setAllSelectedState(isSelected: false)
|
||||||
|
allSelectedButton.isSelected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: UI属性
|
||||||
private lazy var pageView: JYPageController = {
|
private lazy var pageView: JYPageController = {
|
||||||
let pageView = JYPageController()
|
let pageView = JYPageController()
|
||||||
pageView.delegate = self
|
pageView.delegate = self
|
||||||
@ -37,6 +65,39 @@ class SPMyListViewController: SPViewController {
|
|||||||
return pageView
|
return pageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var editButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setImage(UIImage(named: "delete_icon_01"), for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(handleEditButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var cancelButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setTitle("Cancel".localized, for: .normal)
|
||||||
|
button.setTitleColor(.colorFFFFFF(alpha: 0.9), for: .normal)
|
||||||
|
button.titleLabel?.font = .fontRegular(ofSize: 15)
|
||||||
|
button.addTarget(self, action: #selector(handleCancelButton), for: .touchUpInside)
|
||||||
|
button.isHidden = true
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
///全选按钮
|
||||||
|
private lazy var allSelectedButton: UIButton = {
|
||||||
|
let button = JXButton(type: .custom)
|
||||||
|
button.setTitle("Select All".localized, for: .normal)
|
||||||
|
button.setTitleColor(.colorFFFFFF(alpha: 0.9), for: .normal)
|
||||||
|
button.setImage(UIImage(named: "check_icon_01"), for: .normal)
|
||||||
|
button.setImage(UIImage(named: "check_icon_01_selected"), for: .selected)
|
||||||
|
button.setImage(UIImage(named: "check_icon_01_selected"), for: [.selected, .highlighted])
|
||||||
|
button.jx_font = .fontRegular(ofSize: 15)
|
||||||
|
button.titleDirection = .right
|
||||||
|
button.space = 6
|
||||||
|
button.isHidden = true
|
||||||
|
button.addTarget(self, action: #selector(handleAllSelectedButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
@ -48,19 +109,58 @@ class SPMyListViewController: SPViewController {
|
|||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
self.sp_isEditing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleEditButton() {
|
||||||
|
self.sp_isEditing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleCancelButton() {
|
||||||
|
self.sp_isEditing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handleAllSelectedButton() {
|
||||||
|
let vc = viewControllers[pageView.selectedIndex]
|
||||||
|
if self.allSelectedButton.isSelected {
|
||||||
|
vc.setAllSelectedState(isSelected: false)
|
||||||
|
} else {
|
||||||
|
vc.setAllSelectedState(isSelected: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPMyListViewController {
|
extension SPMyListViewController {
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
addChild(pageView)
|
addChild(pageView)
|
||||||
view.addSubview(pageView.view)
|
view.addSubview(pageView.view)
|
||||||
|
view.addSubview(editButton)
|
||||||
|
view.addSubview(cancelButton)
|
||||||
|
view.addSubview(allSelectedButton)
|
||||||
|
|
||||||
pageView.view.snp.makeConstraints { make in
|
pageView.view.snp.makeConstraints { make in
|
||||||
make.edges.equalToSuperview()
|
make.edges.equalToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editButton.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-5)
|
||||||
|
make.top.equalToSuperview().offset(kSPStatusbarHeight + 10)
|
||||||
|
make.height.equalTo(35)
|
||||||
|
make.width.equalTo(40)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelButton.snp.makeConstraints { make in
|
||||||
|
make.top.bottom.equalTo(editButton)
|
||||||
|
make.right.equalTo(editButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
allSelectedButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.bottom.equalTo(editButton)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,4 +188,12 @@ extension SPMyListViewController: JYPageControllerDelegate, JYPageControllerData
|
|||||||
return self.viewControllers[index]
|
return self.viewControllers[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pageController(_ pageController: JYPageController, didEnterControllerAt index: Int) {
|
||||||
|
if index == 0 {
|
||||||
|
self.editButton.isHidden = false
|
||||||
|
} else {
|
||||||
|
self.editButton.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,13 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class SPPlayHistoryViewController: SPViewController {
|
class SPPlayHistoryViewController: SPMyListChildViewController {
|
||||||
|
|
||||||
private lazy var dataArr: [SPShortModel] = []
|
private lazy var dataArr: [SPShortModel] = []
|
||||||
private var page: Int?
|
private var page: Int?
|
||||||
|
|
||||||
|
private lazy var deleteGorup = DispatchGroup()
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3)
|
let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3)
|
||||||
@ -21,7 +23,7 @@ class SPPlayHistoryViewController: SPViewController {
|
|||||||
layout.itemSize = .init(width: itemWidth, height: itemHeight)
|
layout.itemSize = .init(width: itemWidth, height: itemHeight)
|
||||||
layout.minimumLineSpacing = 10
|
layout.minimumLineSpacing = 10
|
||||||
layout.minimumInteritemSpacing = 9
|
layout.minimumInteritemSpacing = 9
|
||||||
layout.sectionInset = .init(top: 10, left: 15, bottom: 0, right: 15)
|
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
|
|
||||||
return layout
|
return layout
|
||||||
}()
|
}()
|
||||||
@ -30,6 +32,13 @@ class SPPlayHistoryViewController: SPViewController {
|
|||||||
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
collectionView.dataSource = self
|
collectionView.dataSource = self
|
||||||
|
collectionView.contentInset = .init(top: 10, left: 0, bottom: 0, right: 0)
|
||||||
|
collectionView.sp_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in
|
||||||
|
self?.handleHeaderRefresh(nil)
|
||||||
|
}
|
||||||
|
collectionView.sp_addRefreshBackFooter { [weak self] in
|
||||||
|
self?.handleFooterRefresh(nil)
|
||||||
|
}
|
||||||
SPCollectListCell.registerCell(collectionView: collectionView)
|
SPCollectListCell.registerCell(collectionView: collectionView)
|
||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
@ -45,7 +54,61 @@ class SPPlayHistoryViewController: SPViewController {
|
|||||||
|
|
||||||
override func setBgImageView() { }
|
override func setBgImageView() { }
|
||||||
|
|
||||||
|
|
||||||
|
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||||
|
requestDataList(page: 1) { [weak self] in
|
||||||
|
self?.collectionView.sp_endHeaderRefreshing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||||
|
guard let page = self.page else { return }
|
||||||
|
|
||||||
|
requestDataList(page: page + 1) { [weak self] in
|
||||||
|
self?.collectionView.sp_endFooterRefreshing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var sp_isEditing: Bool {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isAllSelected: Bool {
|
||||||
|
var result = true
|
||||||
|
for model in dataArr {
|
||||||
|
if model.sp_isSelected != true {
|
||||||
|
result = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override var selectedCount: Int {
|
||||||
|
var result = 0
|
||||||
|
dataArr.forEach {
|
||||||
|
if $0.sp_isSelected ?? false {
|
||||||
|
result += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func setAllSelectedState(isSelected: Bool) {
|
||||||
|
dataArr.forEach {
|
||||||
|
$0.sp_isSelected = isSelected
|
||||||
|
}
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
allSelectedStateDidChange?(isSelected)
|
||||||
|
self.updateDeleteButtonState()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handelDeleteButton() {
|
||||||
|
requestDelete()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -68,12 +131,38 @@ extension SPPlayHistoryViewController: UICollectionViewDelegate, UICollectionVie
|
|||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.sp_isEditing = self.sp_isEditing
|
||||||
cell.model = dataArr[indexPath.row]
|
cell.model = dataArr[indexPath.row]
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
return self.dataArr.count
|
let count = self.dataArr.count
|
||||||
|
if count == 0 {
|
||||||
|
let parameters = SPEmptyParameters(image: UIImage(named: "empty_image_01"))
|
||||||
|
let emptyState = SPEmptyState.normail(parameters: parameters)
|
||||||
|
self.collectionView.emptyState.format = emptyState.format
|
||||||
|
self.collectionView.emptyState.show(emptyState)
|
||||||
|
} else {
|
||||||
|
self.collectionView.emptyState.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = dataArr[indexPath.row]
|
||||||
|
if sp_isEditing {
|
||||||
|
model.sp_isSelected = !(model.sp_isSelected ?? false)
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
|
||||||
|
allSelectedStateDidChange?(isAllSelected)
|
||||||
|
self.updateDeleteButtonState()
|
||||||
|
} else {
|
||||||
|
let vc = SPPlayerDetailViewController()
|
||||||
|
vc.shortPlayId = model.short_play_id
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -82,7 +171,7 @@ extension SPPlayHistoryViewController {
|
|||||||
|
|
||||||
private func requestDataList(page: Int, completer: (() -> Void)?) {
|
private func requestDataList(page: Int, completer: (() -> Void)?) {
|
||||||
|
|
||||||
SPVideoAPI.requestCollectList(page: page) { [weak self] listModel in
|
SPVideoAPI.requestPlayHistoryList(page: page) { [weak self] listModel in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let listModel = listModel, let list = listModel.list {
|
if let listModel = listModel, let list = listModel.list {
|
||||||
@ -94,12 +183,31 @@ extension SPPlayHistoryViewController {
|
|||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
self.collectionView.reloadData()
|
self.collectionView.reloadData()
|
||||||
|
if self.sp_isEditing {
|
||||||
|
self.allSelectedStateDidChange?(self.isAllSelected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
completer?()
|
completer?()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func requestDelete() {
|
||||||
|
|
||||||
|
dataArr.forEach {
|
||||||
|
if $0.sp_isSelected == true, let shortPlayId = $0.short_play_id {
|
||||||
|
self.deleteGorup.enter()
|
||||||
|
SPVideoAPI.requestCollectShort(isCollect: false, shortPlayId: shortPlayId, videoId: nil) { [weak self] in
|
||||||
|
self?.deleteGorup.leave()
|
||||||
|
} failure: { [weak self] in
|
||||||
|
self?.deleteGorup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.deleteGorup.notify(queue: DispatchQueue.main) { [weak self] in
|
||||||
|
self?.requestDataList(page: 1, completer: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,14 @@ class SPCollectListCell: SPCollectionViewCell {
|
|||||||
didSet {
|
didSet {
|
||||||
coverImageView.sp_setImage(url: model?.image_url)
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
titleLabel.text = model?.name
|
titleLabel.text = model?.name
|
||||||
|
|
||||||
|
selectedButton.isSelected = model?.sp_isSelected ?? false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sp_isEditing: Bool = false {
|
||||||
|
didSet {
|
||||||
|
selectedButton.isHidden = !sp_isEditing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +39,15 @@ class SPCollectListCell: SPCollectionViewCell {
|
|||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var selectedButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.isHidden = true
|
||||||
|
button.isUserInteractionEnabled = false
|
||||||
|
button.setImage(UIImage(named: "check_icon_01"), for: .normal)
|
||||||
|
button.setImage(UIImage(named: "check_icon_01_selected"), for: .selected)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
@ -49,6 +66,7 @@ extension SPCollectListCell {
|
|||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
contentView.addSubview(coverImageView)
|
contentView.addSubview(coverImageView)
|
||||||
contentView.addSubview(titleLabel)
|
contentView.addSubview(titleLabel)
|
||||||
|
coverImageView.addSubview(selectedButton)
|
||||||
|
|
||||||
coverImageView.snp.makeConstraints { make in
|
coverImageView.snp.makeConstraints { make in
|
||||||
make.left.right.top.equalToSuperview()
|
make.left.right.top.equalToSuperview()
|
||||||
@ -61,6 +79,11 @@ extension SPCollectListCell {
|
|||||||
make.right.lessThanOrEqualToSuperview()
|
make.right.lessThanOrEqualToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedButton.snp.makeConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-5)
|
||||||
|
make.top.equalToSuperview().offset(5)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
|
|
||||||
override func play() {
|
override func play() {
|
||||||
super.play()
|
super.play()
|
||||||
if let _ = self.viewModel.currentPlayer?.model as? SPVideoDetailModel,
|
if let _ = self.detailModel,
|
||||||
let videoInfo = self.viewModel.currentPlayer?.videoInfo
|
let videoInfo = self.viewModel.currentPlayer?.videoInfo
|
||||||
{
|
{
|
||||||
SPVideoAPI.requestRequestVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "")
|
SPVideoAPI.requestRequestVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "")
|
||||||
|
@ -30,6 +30,8 @@ class SPShortModel: SPModel, SmartCodable {
|
|||||||
|
|
||||||
@IgnoredKey
|
@IgnoredKey
|
||||||
var titleAttributedString: NSAttributedString?
|
var titleAttributedString: NSAttributedString?
|
||||||
|
@IgnoredKey
|
||||||
|
var sp_isSelected: Bool?
|
||||||
|
|
||||||
|
|
||||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
|
91
ShortPlay/Libs/Empty/SPEmptyState.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// SPEmptyState.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import EmptyStateKit
|
||||||
|
|
||||||
|
struct SPEmptyParameters {
|
||||||
|
// var title: String = "暂无内容"
|
||||||
|
// var titleFont: UIFont = UIFont.text_md
|
||||||
|
// var titleColor: UIColor = UIColor.system_text_secondary_300
|
||||||
|
var image: UIImage? = UIImage(named: "empty_image_01")
|
||||||
|
// var buttonTitle: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SPEmptyState {
|
||||||
|
case normail(parameters: SPEmptyParameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPEmptyState: CustomState {
|
||||||
|
|
||||||
|
var image: UIImage? {
|
||||||
|
switch self {
|
||||||
|
case .normail(let parameters):
|
||||||
|
return parameters.image
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var title: String? {
|
||||||
|
switch self {
|
||||||
|
case .normail(let parameters):
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// var titleButton: String? {
|
||||||
|
// switch self {
|
||||||
|
// case .normail(let parameters):
|
||||||
|
// return parameters.buttonTitle
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPEmptyState {
|
||||||
|
var format: EmptyStateFormat {
|
||||||
|
|
||||||
|
var format = EmptyStateFormat()
|
||||||
|
format.backgroundColor = .clear
|
||||||
|
format.imageSize = self.image?.size ?? .zero
|
||||||
|
// format.verticalMargin = -10
|
||||||
|
//
|
||||||
|
// format.buttonWidth = 107
|
||||||
|
// format.buttonTopMargin = 10
|
||||||
|
// format.buttonColor = .system_fill_primary_100
|
||||||
|
// format.buttonAttributes = [
|
||||||
|
// .font: UIFont.text_md,
|
||||||
|
// .foregroundColor: UIColor.system_text_secondary_500
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
// switch self {
|
||||||
|
// case .normail(let p):
|
||||||
|
// format.titleAttributes = [
|
||||||
|
// .font: p.titleFont,
|
||||||
|
// .foregroundColor: p.titleColor
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// case .login(let p):
|
||||||
|
// format.titleAttributes = [
|
||||||
|
// .font: p.titleFont,
|
||||||
|
// .foregroundColor: p.titleColor
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
72
ShortPlay/Libs/Reachability/NetworkObserver.swift
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// NetworkObserver.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Network
|
||||||
|
import Reachability
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkObserver {
|
||||||
|
static let share = NetworkObserver()
|
||||||
|
|
||||||
|
|
||||||
|
private let monitor = NWPathMonitor()
|
||||||
|
private let queue = DispatchQueue(label: "NetworkMonitorQueue")
|
||||||
|
|
||||||
|
func startMonitoring() {
|
||||||
|
monitor.pathUpdateHandler = { path in
|
||||||
|
if path.status == .satisfied {
|
||||||
|
print("++++++Network is available")
|
||||||
|
} else {
|
||||||
|
print("++++++No network connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.usesInterfaceType(.wifi) {
|
||||||
|
print("++++++Using Wi-Fi")
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.usesInterfaceType(.cellular) {
|
||||||
|
print("++++++Using Cellular")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.start(queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopMonitoring() {
|
||||||
|
monitor.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private let reachability = try! Reachability()
|
||||||
|
|
||||||
|
func startMonitoring() {
|
||||||
|
reachability.whenReachable = { reachability in
|
||||||
|
if reachability.connection == .wifi {
|
||||||
|
print("++++++Network reachable via Wi-Fi")
|
||||||
|
} else if reachability.connection == .cellular {
|
||||||
|
print("++++++Network reachable via Cellular")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reachability.whenUnreachable = { _ in
|
||||||
|
print("++++++Network not reachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try reachability.startNotifier()
|
||||||
|
} catch {
|
||||||
|
print("++++++Unable to start notifier")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopMonitoring() {
|
||||||
|
reachability.stopNotifier()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
22
ShortPlay/Source/Assets.xcassets/icon/check_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组件 67 – 2@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组件 67 – 2@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/check_icon_01.imageset/组件 67 – 2@2x.png
vendored
Normal file
After Width: | Height: | Size: 764 B |
BIN
ShortPlay/Source/Assets.xcassets/icon/check_icon_01.imageset/组件 67 – 2@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
22
ShortPlay/Source/Assets.xcassets/icon/check_icon_01_selected.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组件 67 – 9@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "组件 67 – 9@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/check_icon_01_selected.imageset/组件 67 – 9@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ShortPlay/Source/Assets.xcassets/icon/check_icon_01_selected.imageset/组件 67 – 9@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.9 KiB |
22
ShortPlay/Source/Assets.xcassets/icon/delete_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "delet-选中@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "delet-选中@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/delete_icon_01.imageset/delet-选中@2x.png
vendored
Normal file
After Width: | Height: | Size: 871 B |
BIN
ShortPlay/Source/Assets.xcassets/icon/delete_icon_01.imageset/delet-选中@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
22
ShortPlay/Source/Assets.xcassets/icon/delete_icon_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "delete@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "delete@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/delete_icon_02.imageset/delete@2x.png
vendored
Normal file
After Width: | Height: | Size: 929 B |
BIN
ShortPlay/Source/Assets.xcassets/icon/delete_icon_02.imageset/delete@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
22
ShortPlay/Source/Assets.xcassets/image/empty_image_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "空空如也@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "空空如也@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/image/empty_image_01.imageset/空空如也@2x.png
vendored
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
ShortPlay/Source/Assets.xcassets/image/empty_image_01.imageset/空空如也@3x.png
vendored
Normal file
After Width: | Height: | Size: 31 KiB |
@ -30,6 +30,9 @@
|
|||||||
"My list" = "My list";
|
"My list" = "My list";
|
||||||
"Follow List" = "Follow List";
|
"Follow List" = "Follow List";
|
||||||
"Play List" = "Play List";
|
"Play List" = "Play List";
|
||||||
|
"Cancel" = "Cancel";
|
||||||
|
"Select All" = "Select All";
|
||||||
|
"Delet (%@)" = "Delet (%@)";
|
||||||
|
|
||||||
///视频详情标题
|
///视频详情标题
|
||||||
"kPlayerDetailTitleString" = "Episode %@ / %@";
|
"kPlayerDetailTitleString" = "Episode %@ / %@";
|
||||||
|
6
ShortPlay/Thirdparty/JXButton/JXButton.swift
vendored
@ -211,6 +211,12 @@ class JXButton: UIButton {
|
|||||||
updateStatus()
|
updateStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var isEnabled: Bool {
|
||||||
|
didSet {
|
||||||
|
updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ open class JYPageController: UIViewController {
|
|||||||
return segment
|
return segment
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var pageContentScrollView : UIScrollView = {
|
private(set) lazy var pageContentScrollView : UIScrollView = {
|
||||||
let scrollView = UIScrollView()
|
let scrollView = UIScrollView()
|
||||||
// scrollView.backgroundColor = .white
|
// scrollView.backgroundColor = .white
|
||||||
scrollView.showsHorizontalScrollIndicator = false
|
scrollView.showsHorizontalScrollIndicator = false
|
||||||
|