diff --git a/Veloria.xcodeproj/project.pbxproj b/Veloria.xcodeproj/project.pbxproj index 403eb7b..47a1303 100644 --- a/Veloria.xcodeproj/project.pbxproj +++ b/Veloria.xcodeproj/project.pbxproj @@ -63,6 +63,20 @@ BF0FA7222DDC859D00C9E5F2 /* NSNumber+VPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */; }; BF0FA7242DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */; }; BF0FA7262DDC8F7600C9E5F2 /* VPCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */; }; + BF0FA7282DDC91F800C9E5F2 /* VPCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7272DDC91F800C9E5F2 /* VPCollectionViewCell.swift */; }; + BF0FA72A2DDC922F00C9E5F2 /* VPHomeRecommandCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7292DDC922F00C9E5F2 /* VPHomeRecommandCell.swift */; }; + BF0FA72C2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA72B2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift */; }; + BF0FA72E2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA72D2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift */; }; + BF0FA7302DDEBB1600C9E5F2 /* UIButton+VPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA72F2DDEBB1600C9E5F2 /* UIButton+VPAdd.swift */; }; + BF0FA7322DDEBD6400C9E5F2 /* AppDelegate+Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7312DDEBD6400C9E5F2 /* AppDelegate+Config.swift */; }; + BF0FA7342DDEC74500C9E5F2 /* VPCategoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7332DDEC74500C9E5F2 /* VPCategoryModel.swift */; }; + BF0FA7372DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BF0FA7362DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf */; }; + BF0FA7392DDECF8900C9E5F2 /* VPHomeListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7382DDECF8900C9E5F2 /* VPHomeListViewController.swift */; }; + BF0FA73B2DDED1C700C9E5F2 /* VPHomeListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA73A2DDED1C700C9E5F2 /* VPHomeListCell.swift */; }; + BF0FA73D2DDED2D000C9E5F2 /* VPVideoAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */; }; + BF0FA73F2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA73E2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift */; }; + BF0FA7412DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7402DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift */; }; + BF0FA7452DDF027900C9E5F2 /* VPPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7442DDF027900C9E5F2 /* VPPlayer.swift */; }; F939C04AD4003BA127F15C28 /* Pods_Veloria.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */; }; /* End PBXBuildFile section */ @@ -132,6 +146,20 @@ BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+VPAdd.swift"; sourceTree = ""; }; BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRecommandContentCell.swift; sourceTree = ""; }; BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionView.swift; sourceTree = ""; }; + BF0FA7272DDC91F800C9E5F2 /* VPCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionViewCell.swift; sourceTree = ""; }; + BF0FA7292DDC922F00C9E5F2 /* VPHomeRecommandCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRecommandCell.swift; sourceTree = ""; }; + BF0FA72B2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRankingContentCell.swift; sourceTree = ""; }; + BF0FA72D2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRankingCell.swift; sourceTree = ""; }; + BF0FA72F2DDEBB1600C9E5F2 /* UIButton+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+VPAdd.swift"; sourceTree = ""; }; + BF0FA7312DDEBD6400C9E5F2 /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = ""; }; + BF0FA7332DDEC74500C9E5F2 /* VPCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCategoryModel.swift; sourceTree = ""; }; + BF0FA7362DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "PlayfairDisplay-VariableFont_wght.ttf"; sourceTree = ""; }; + BF0FA7382DDECF8900C9E5F2 /* VPHomeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeListViewController.swift; sourceTree = ""; }; + BF0FA73A2DDED1C700C9E5F2 /* VPHomeListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeListCell.swift; sourceTree = ""; }; + BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVideoAPI.swift; sourceTree = ""; }; + BF0FA73E2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeSearchButton.swift; sourceTree = ""; }; + BF0FA7402DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+VPRefresh.swift"; sourceTree = ""; }; + BF0FA7442DDF027900C9E5F2 /* VPPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPlayer.swift; sourceTree = ""; }; E0BDA3570E00C90877E45AA0 /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -183,6 +211,7 @@ children = ( 1B056E212DDAC0FD007EE38D /* AppDelegate.swift */, 1B056E282DDAC0FD007EE38D /* SceneDelegate.swift */, + BF0FA7312DDEBD6400C9E5F2 /* AppDelegate+Config.swift */, ); path = AppDelegate; sourceTree = ""; @@ -190,6 +219,7 @@ 1B056E332DDAC1AB007EE38D /* Source */ = { isa = PBXGroup; children = ( + BF0FA7352DDECC4600C9E5F2 /* Font */, 1B056E222DDAC0FD007EE38D /* Assets.xcassets */, 1B056E232DDAC0FD007EE38D /* Info.plist */, 1B056E252DDAC0FD007EE38D /* LaunchScreen.storyboard */, @@ -202,6 +232,7 @@ 1B056E352DDAC1DE007EE38D /* Libs */ = { isa = PBXGroup; children = ( + BF0FA7432DDF025A00C9E5F2 /* Player */, BF0FA6F22DDC603200C9E5F2 /* HUD */, BF0FA6D82DDC5C8200C9E5F2 /* Login */, 1B056E652DDAD098007EE38D /* LocalizedManager */, @@ -214,6 +245,7 @@ isa = PBXGroup; children = ( 1B056E522DDACBF4007EE38D /* Home */, + BF0FA7422DDF024400C9E5F2 /* Explore */, BF0FA6FD2DDC65F300C9E5F2 /* Player */, ); path = Class; @@ -250,6 +282,7 @@ BF0FA7092DDC69C800C9E5F2 /* VPTableViewCell.swift */, BF0FA71A2DDC7FF200C9E5F2 /* VPImageView.swift */, BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */, + BF0FA7272DDC91F800C9E5F2 /* VPCollectionViewCell.swift */, ); path = View; sourceTree = ""; @@ -302,6 +335,8 @@ BF0FA6DE2DDC5E4D00C9E5F2 /* String+VPAdd.swift */, BF0FA71C2DDC807200C9E5F2 /* UIImageView+VPAdd.swift */, BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */, + BF0FA72F2DDEBB1600C9E5F2 /* UIButton+VPAdd.swift */, + BF0FA7402DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift */, ); path = Extension; sourceTree = ""; @@ -329,6 +364,7 @@ isa = PBXGroup; children = ( 1B056E562DDACC6B007EE38D /* VPHomePageViewController.swift */, + BF0FA7382DDECF8900C9E5F2 /* VPHomeListViewController.swift */, ); path = Controller; sourceTree = ""; @@ -416,6 +452,7 @@ isa = PBXGroup; children = ( BF0FA6F82DDC64E700C9E5F2 /* VPHomeAPI.swift */, + BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */, ); path = API; sourceTree = ""; @@ -426,6 +463,7 @@ BF0FA6FB2DDC657500C9E5F2 /* VPHomeDataModel.swift */, BF0FA7062DDC687C00C9E5F2 /* VPHomeDataItem.swift */, BF0FA7112DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift */, + BF0FA7332DDEC74500C9E5F2 /* VPCategoryModel.swift */, ); path = Model; sourceTree = ""; @@ -433,6 +471,8 @@ BF0FA6FD2DDC65F300C9E5F2 /* Player */ = { isa = PBXGroup; children = ( + BF0FA7472DDF03B600C9E5F2 /* Controller */, + BF0FA7462DDF03AD00C9E5F2 /* View */, BF0FA6FE2DDC660300C9E5F2 /* Model */, ); path = Player; @@ -461,7 +501,12 @@ BF0FA70D2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift */, BF0FA70B2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift */, BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */, + BF0FA72B2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift */, BF0FA7182DDC7F4900C9E5F2 /* VPHomeBannerCell.swift */, + BF0FA7292DDC922F00C9E5F2 /* VPHomeRecommandCell.swift */, + BF0FA72D2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift */, + BF0FA73A2DDED1C700C9E5F2 /* VPHomeListCell.swift */, + BF0FA73E2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift */, ); path = View; sourceTree = ""; @@ -483,6 +528,43 @@ path = JXButton; sourceTree = ""; }; + BF0FA7352DDECC4600C9E5F2 /* Font */ = { + isa = PBXGroup; + children = ( + BF0FA7362DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf */, + ); + path = Font; + sourceTree = ""; + }; + BF0FA7422DDF024400C9E5F2 /* Explore */ = { + isa = PBXGroup; + children = ( + ); + path = Explore; + sourceTree = ""; + }; + BF0FA7432DDF025A00C9E5F2 /* Player */ = { + isa = PBXGroup; + children = ( + BF0FA7442DDF027900C9E5F2 /* VPPlayer.swift */, + ); + path = Player; + sourceTree = ""; + }; + BF0FA7462DDF03AD00C9E5F2 /* View */ = { + isa = PBXGroup; + children = ( + ); + path = View; + sourceTree = ""; + }; + BF0FA7472DDF03B600C9E5F2 /* Controller */ = { + isa = PBXGroup; + children = ( + ); + path = Controller; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -548,6 +630,7 @@ files = ( 1B056E2E2DDAC0FD007EE38D /* Assets.xcassets in Resources */, 1B056E642DDAD035007EE38D /* Localizable.strings in Resources */, + BF0FA7372DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf in Resources */, 1B056E302DDAC0FD007EE38D /* LaunchScreen.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -604,6 +687,8 @@ 1B056E742DDB2DD7007EE38D /* UIView+VPAdd.swift in Sources */, BF0FA6F12DDC600200C9E5F2 /* VPNetwork.swift in Sources */, 1B056E572DDACC6B007EE38D /* VPHomePageViewController.swift in Sources */, + BF0FA72A2DDC922F00C9E5F2 /* VPHomeRecommandCell.swift in Sources */, + BF0FA7322DDEBD6400C9E5F2 /* AppDelegate+Config.swift in Sources */, BF0FA6DF2DDC5E4D00C9E5F2 /* String+VPAdd.swift in Sources */, BF0FA7002DDC665300C9E5F2 /* VPShortModel.swift in Sources */, 1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */, @@ -616,22 +701,30 @@ 1B056E592DDACD44007EE38D /* VPTabBarItemContentView.swift in Sources */, 1B056E2B2DDAC0FD007EE38D /* AppDelegate.swift in Sources */, 1B056E6C2DDADAA1007EE38D /* VPTabBar.swift in Sources */, + BF0FA72E2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift in Sources */, + BF0FA7302DDEBB1600C9E5F2 /* UIButton+VPAdd.swift in Sources */, 1B056E5D2DDACD8E007EE38D /* UIFont+VPAdd.swift in Sources */, + BF0FA7282DDC91F800C9E5F2 /* VPCollectionViewCell.swift in Sources */, BF0FA6F92DDC64E700C9E5F2 /* VPHomeAPI.swift in Sources */, BF0FA6F42DDC604500C9E5F2 /* VPHUD.swift in Sources */, BF0FA7222DDC859D00C9E5F2 /* NSNumber+VPAdd.swift in Sources */, + BF0FA73B2DDED1C700C9E5F2 /* VPHomeListCell.swift in Sources */, BF0FA71B2DDC7FF200C9E5F2 /* VPImageView.swift in Sources */, 1B056E2C2DDAC0FD007EE38D /* SceneDelegate.swift in Sources */, 1B056E462DDAC370007EE38D /* UIScreen+VPAdd.swift in Sources */, + BF0FA73D2DDED2D000C9E5F2 /* VPVideoAPI.swift in Sources */, BF0FA6EE2DDC5F8700C9E5F2 /* JXUUID.m in Sources */, BF0FA7052DDC67AC00C9E5F2 /* VPHomeViewModel.swift in Sources */, BF0FA6EF2DDC5F8700C9E5F2 /* PDKeyChain.m in Sources */, 1B056E4D2DDAC7C1007EE38D /* VPTabBarController.swift in Sources */, BF0FA6DA2DDC5CB600C9E5F2 /* VPLoginManager.swift in Sources */, + BF0FA72C2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift in Sources */, + BF0FA7342DDEC74500C9E5F2 /* VPCategoryModel.swift in Sources */, 1B056E4F2DDAC7FC007EE38D /* VPNavigationController.swift in Sources */, 1B056E3F2DDAC2DB007EE38D /* VPCryptorService.swift in Sources */, BF0FA7202DDC83AE00C9E5F2 /* JXButton.swift in Sources */, BF0FA7072DDC687C00C9E5F2 /* VPHomeDataItem.swift in Sources */, + BF0FA7452DDF027900C9E5F2 /* VPPlayer.swift in Sources */, BF0FA70E2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift in Sources */, BF0FA6D72DDC5BE100C9E5F2 /* VPURLPath.swift in Sources */, 1B056E5B2DDACD80007EE38D /* UIColor+VPAdd.swift in Sources */, @@ -644,14 +737,17 @@ 1B056E412DDAC30A007EE38D /* VPModel.swift in Sources */, BF0FA70A2DDC69C800C9E5F2 /* VPTableViewCell.swift in Sources */, 1B056E442DDAC355007EE38D /* UIDevice+VPAdd.swift in Sources */, + BF0FA73F2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift in Sources */, 1B056E512DDACBE5007EE38D /* VPViewController.swift in Sources */, BF0FA7122DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift in Sources */, BF0FA7162DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift in Sources */, BF0FA7172DDC78FF00C9E5F2 /* ZKCycleScrollView.swift in Sources */, BF0FA6D52DDC5B5D00C9E5F2 /* VPApi.swift in Sources */, BF0FA6FC2DDC657500C9E5F2 /* VPHomeDataModel.swift in Sources */, + BF0FA7392DDECF8900C9E5F2 /* VPHomeListViewController.swift in Sources */, BF0FA6F62DDC616300C9E5F2 /* VPToast.swift in Sources */, 1B056E492DDAC3DF007EE38D /* VPAppTool.swift in Sources */, + BF0FA7412DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift in Sources */, BF0FA70C2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift in Sources */, BF0FA7102DDC6CA200C9E5F2 /* VPListModel.swift in Sources */, ); diff --git a/Veloria/AppDelegate/AppDelegate+Config.swift b/Veloria/AppDelegate/AppDelegate+Config.swift new file mode 100644 index 0000000..99a2ee6 --- /dev/null +++ b/Veloria/AppDelegate/AppDelegate+Config.swift @@ -0,0 +1,16 @@ +// +// AppDelegate+Config.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +extension AppDelegate { + + func appConfig() { + UIButton.vp_Awake() + } + +} diff --git a/Veloria/AppDelegate/AppDelegate.swift b/Veloria/AppDelegate/AppDelegate.swift index 2b7ecae..045c7af 100644 --- a/Veloria/AppDelegate/AppDelegate.swift +++ b/Veloria/AppDelegate/AppDelegate.swift @@ -10,10 +10,12 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { -// https://api-qjwl168.qjwl168.com/velo + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. + + appConfig() + return true } diff --git a/Veloria/Base/Controller/VPTabBarController.swift b/Veloria/Base/Controller/VPTabBarController.swift index 0e901e7..61c1655 100644 --- a/Veloria/Base/Controller/VPTabBarController.swift +++ b/Veloria/Base/Controller/VPTabBarController.swift @@ -87,7 +87,7 @@ extension VPTabBarController { let nav1 = createNavigationController(viewController: VPHomePageViewController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected")) - let nav2 = createNavigationController(viewController: VPHomePageViewController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) + let nav2 = createNavigationController(viewController: VPHomePageViewController(), title: "Explore".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) let nav3 = createNavigationController(viewController: VPHomePageViewController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected")) let nav4 = createNavigationController(viewController: VPHomePageViewController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) diff --git a/Veloria/Base/Controller/VPViewController.swift b/Veloria/Base/Controller/VPViewController.swift index a764272..e91333e 100644 --- a/Veloria/Base/Controller/VPViewController.swift +++ b/Veloria/Base/Controller/VPViewController.swift @@ -27,7 +27,9 @@ class VPViewController: UIViewController { } } - + func handleHeaderRefresh(_ completer: (() -> Void)?) {} + + func handleFooterRefresh(_ completer: (() -> Void)?) {} } diff --git a/Veloria/Base/Define/VPDefine.swift b/Veloria/Base/Define/VPDefine.swift index d9d23b6..8f41c58 100644 --- a/Veloria/Base/Define/VPDefine.swift +++ b/Veloria/Base/Define/VPDefine.swift @@ -29,3 +29,20 @@ public func vpLog(message:Any? , file: String = #file, function: String = #funct #else public func vpLog(message:Any?) { } #endif + +public func vp_swizzled_instanceMethod(_ prefix: String, oldClass: Swift.AnyClass!, oldSelector: String, newClass: Swift.AnyClass) { + let newSelector = prefix + "_" + oldSelector; + let originalSelector = NSSelectorFromString(oldSelector) + let swizzledSelector = NSSelectorFromString(newSelector) + + let originalMethod = class_getInstanceMethod(oldClass, originalSelector) + let swizzledMethod = class_getInstanceMethod(newClass, swizzledSelector) + + let isAdd = class_addMethod(oldClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) + + if isAdd { + class_replaceMethod(newClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!)) + }else { + method_exchangeImplementations(originalMethod!, swizzledMethod!) + } +} diff --git a/Veloria/Base/Extension/UIButton+VPAdd.swift b/Veloria/Base/Extension/UIButton+VPAdd.swift new file mode 100644 index 0000000..52f7b91 --- /dev/null +++ b/Veloria/Base/Extension/UIButton+VPAdd.swift @@ -0,0 +1,118 @@ +// +// UIButton+VPAdd.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +extension UIButton { + fileprivate struct AssociatedKeys { + static var vp_gradientLayer: Int? + static var vp_gradientBorder: Int? + static var vp_gradientBorderShapeLayer: Int? + } + + + @objc public static func vp_Awake() { + vp_swizzled_instanceMethod("vp", oldClass: self, oldSelector: "layoutSubviews", newClass: self) + } + + @objc func vp_layoutSubviews() { + vp_layoutSubviews() + vp_gradientLayer?.frame = self.bounds + + vp_gradientBorder?.frame = self.bounds + let path = UIBezierPath(roundedRect: bounds.insetBy(dx: 2, dy: 2), cornerRadius: layer.cornerRadius) + vp_gradientBorderShapeLayer?.path = path.cgPath + } + + + +} + +extension UIButton { + private var vp_gradientLayer: CAGradientLayer? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.vp_gradientLayer) as? CAGradientLayer + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.vp_gradientLayer, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + private var vp_gradientBorder: CAGradientLayer? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.vp_gradientBorder) as? CAGradientLayer + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.vp_gradientBorder, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + private var vp_gradientBorderShapeLayer: CAShapeLayer? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.vp_gradientBorderShapeLayer) as? CAShapeLayer + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.vp_gradientBorderShapeLayer, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + ///设置渐变色 + func vp_setGradient() { + if vp_gradientLayer == nil { + let gLayer = CAGradientLayer() + gLayer.colors = [UIColor.color05CEA0().cgColor, UIColor.color7C174F().cgColor] + gLayer.locations = [0, 0.8] + gLayer.startPoint = .init(x: 0, y: 0.3) + gLayer.endPoint = .init(x: 1, y: 0.8) + vp_gradientLayer = gLayer + } + + if let layer = vp_gradientLayer { + self.layer.addSublayer(layer) + self.titleLabel?.layer.zPosition = 1 + } + } + + ///删除渐变层 + func vp_removeGradient() { + vp_gradientLayer?.removeFromSuperlayer() + } + + + + + ///设置渐变边框 + func vp_setGradientBorder() { + if vp_gradientBorder == nil { + let gLayer = CAGradientLayer() + gLayer.colors = [UIColor.color05CEA0().cgColor, UIColor.color7C174F().cgColor] + gLayer.locations = [0, 0.8] + gLayer.startPoint = .init(x: 0, y: 0.3) + gLayer.endPoint = .init(x: 1, y: 0.8) + + let shapeLayer = CAShapeLayer() + shapeLayer.lineWidth = 1 + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.strokeColor = UIColor.black.cgColor + gLayer.mask = shapeLayer + + vp_gradientBorderShapeLayer = shapeLayer + vp_gradientBorder = gLayer + } + if let layer = vp_gradientBorder { + self.layer.addSublayer(layer) + self.titleLabel?.layer.zPosition = 1 + } + } + + ///删除渐变边框 + func vp_removeGradientBorder() { + vp_gradientBorder?.removeFromSuperlayer() + } + + +} diff --git a/Veloria/Base/Extension/UIColor+VPAdd.swift b/Veloria/Base/Extension/UIColor+VPAdd.swift index 58eb98f..c1bf02f 100644 --- a/Veloria/Base/Extension/UIColor+VPAdd.swift +++ b/Veloria/Base/Extension/UIColor+VPAdd.swift @@ -41,4 +41,12 @@ extension UIColor { static func colorAFAFAF(alpha: CGFloat = 1) -> UIColor { return UIColor(rgb: 0xAFAFAF, alpha: alpha) } + + static func colorDEDEDE(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xDEDEDE, alpha: alpha) + } + + static func colorFFFFFB(alpha: CGFloat = 1) -> UIColor { + return UIColor(rgb: 0xFFFFFB, alpha: alpha) + } } diff --git a/Veloria/Base/Extension/UIFont+VPAdd.swift b/Veloria/Base/Extension/UIFont+VPAdd.swift index 026ead5..2ad466f 100644 --- a/Veloria/Base/Extension/UIFont+VPAdd.swift +++ b/Veloria/Base/Extension/UIFont+VPAdd.swift @@ -31,4 +31,8 @@ extension UIFont { static func fontBold(ofSize: CGFloat) -> UIFont { return .systemFont(ofSize: ofSize, weight: .bold) } + + static func numberFont(ofSize: CGFloat) -> UIFont { + return UIFont(name: "Playfair Display", size: ofSize) ?? fontMedium(ofSize: ofSize) + } } diff --git a/Veloria/Base/Extension/UIScrollView+VPRefresh.swift b/Veloria/Base/Extension/UIScrollView+VPRefresh.swift new file mode 100644 index 0000000..fc915e3 --- /dev/null +++ b/Veloria/Base/Extension/UIScrollView+VPRefresh.swift @@ -0,0 +1,59 @@ +// +// UIScrollView+VPRefresh.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +extension UIScrollView { + + func vp_addRefreshHeader(insetTop: CGFloat = 0, block: (() -> Void)?) { + + + self.mj_header = MJRefreshNormalHeader(refreshingBlock: { + block?() + }) + self.mj_header?.ignoredScrollViewContentInsetTop = insetTop + } + + func vp_addRefreshFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { + let footer = MJRefreshAutoNormalFooter(refreshingBlock: { + block?() + }) + footer.ignoredScrollViewContentInsetBottom = insetBottom + + self.mj_footer = footer + + + } + + + func vp_addRefreshBackFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { + self.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: { + block?() + }) + + self.mj_footer?.ignoredScrollViewContentInsetBottom = insetBottom + } + + func vp_endHeaderRefreshing() { + self.mj_header?.endRefreshing() + } + + func vp_endFooterRefreshing() { + if self.mj_footer?.state == .noMoreData { return } + self.mj_footer?.endRefreshing() + } + + ///重置没有更多 + func vp_resetNoMoreData() { + self.mj_footer?.resetNoMoreData() + } + + func vp_endRefreshingWithNoMoreData() { +// self.mj_footer?.state = .noMoreData + self.mj_footer?.endRefreshingWithNoMoreData() + } +} diff --git a/Veloria/Base/Networking/API/VPVideoAPI.swift b/Veloria/Base/Networking/API/VPVideoAPI.swift new file mode 100644 index 0000000..cb3d0e6 --- /dev/null +++ b/Veloria/Base/Networking/API/VPVideoAPI.swift @@ -0,0 +1,29 @@ +// +// VPVideoAPI.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +class VPVideoAPI: NSObject { + + + ///获取分类短剧 + static func requestCategoryVideoList(id: String, page: Int, completer: ((_ listModel: VPListModel?) -> Void)?) { + + var param = VPNetworkParameters(path: "/videoList") + param.method = .get + param.parameters = [ + "category_id" : id, + "current_page" : page, + "page_size" : 20 + ] + + VPNetwork.request(parameters: param) { (response: VPNetworkResponse>) in + completer?(response.data) + } + } + +} diff --git a/Veloria/Base/View/VPCollectionViewCell.swift b/Veloria/Base/View/VPCollectionViewCell.swift new file mode 100644 index 0000000..d2c169d --- /dev/null +++ b/Veloria/Base/View/VPCollectionViewCell.swift @@ -0,0 +1,20 @@ +// +// VPCollectionViewCell.swift +// Veloria +// +// Created by Veloria on 2025/5/20. +// + +import UIKit + +class VPCollectionViewCell: UICollectionViewCell { + override init(frame: CGRect) { + super.init(frame: frame) + self.layer.rasterizationScale = UIScreen.main.scale + self.layer.shouldRasterize = true + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} diff --git a/Veloria/Class/Home/Controller/VPHomeListViewController.swift b/Veloria/Class/Home/Controller/VPHomeListViewController.swift new file mode 100644 index 0000000..e7e9fce --- /dev/null +++ b/Veloria/Class/Home/Controller/VPHomeListViewController.swift @@ -0,0 +1,119 @@ +// +// VPHomeListViewController.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +class VPHomeListViewController: VPViewController, WMZPageProtocol { + + + var categoryId: String? + + private lazy var page: Int = 1 + private lazy var dataArr: [VPShortModel] = [] + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let width = floor((UIScreen.width - 30 - 14) / 3) + let height = 148 / 110 * width + 31 + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: width, height: height) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 7 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + return layout + }() + + private lazy var collectionView: VPCollectionView = { + let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.customTabBarHeight + 10, right: 0) + collectionView.vp_addRefreshBackFooter(insetBottom: collectionView.contentInset.bottom) { [weak self] in + self?.handleFooterRefresh(nil) + } + collectionView.register(VPHomeListCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + bgImageView.isHidden = true + view.backgroundColor = .clear + + vp_setupUI() + + requestDataArr(page: 1, completer: nil) + } + + + func getMyScrollView() -> UIScrollView { + return collectionView + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: self.page + 1) { [weak self] in + self?.collectionView.vp_endFooterRefreshing() + completer?() + } + } + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: 1, completer: completer) + } + +} + +extension VPHomeListViewController { + private func vp_setupUI() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview() + } + + } +} + +//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- +extension VPHomeListViewController: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPHomeListCell + cell.model = dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension VPHomeListViewController { + + private func requestDataArr(page: Int, completer: (() -> Void)?) { + guard let id = self.categoryId else { return } + + VPVideoAPI.requestCategoryVideoList(id: id, page: page) { [weak self] listModel in + guard let self = self else { return } + if let list = listModel?.list { + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + self.page = page + + self.collectionView.reloadData() + } + completer?() + } + + } + +} diff --git a/Veloria/Class/Home/Controller/VPHomePageViewController.swift b/Veloria/Class/Home/Controller/VPHomePageViewController.swift index 19a40d5..a4dc9f7 100644 --- a/Veloria/Class/Home/Controller/VPHomePageViewController.swift +++ b/Veloria/Class/Home/Controller/VPHomePageViewController.swift @@ -14,9 +14,12 @@ class VPHomePageViewController: VPViewController { private lazy var pageParam: WMZPageParam = { let param = WMZPageParam() - param.wTitleArr = ["All".localized, "Romance", "Revenge"] + param.wTitleArr = self.viewModel.categoryTitleList param.wViewController = { [weak self] index in - return VPViewController() + let categoryModel = self?.viewModel.categoryList[index] + let vc = VPHomeListViewController() + vc.categoryId = categoryModel?.category_id + return vc } param.wTopSuspension = true @@ -26,16 +29,23 @@ class VPHomePageViewController: VPViewController { param.wMenuTitleSelectColor = .colorFFFFFF() param.wMenuTitleUIFont = .fontRegular(ofSize: 12) param.wMenuTitleSelectUIFont = .fontMedium(ofSize: 12) + param.wMenuTitleRadios = 17 + param.wMenuHeight = 34 + param.wMenuTitleOffset = 9 + param.wMenuInsets = UIEdgeInsets(top: 42, left: 15, bottom: 17, right: 15) param.wMenuBgColor = .clear param.wBgColor = .clear + param.wCustomMenuTitle = { [weak self] buttons in - - vpLog(message: "++++++++++\(buttons ?? [])") - + buttons?.forEach({ button in + self?.setButtonState(button: button) + }) } - param.wCustomMenuSelectTitle = { buttons in - vpLog(message: "----------\(buttons ?? [])") + param.wCustomMenuSelectTitle = { [weak self] buttons in + buttons?.forEach({ button in + self?.setButtonState(button: button) + }) } param.wCustomNaviBarY = { _ in @@ -44,35 +54,79 @@ class VPHomePageViewController: VPViewController { param.wCustomTabbarY = { _ in return 0 } + param.wCustomDataViewTopOffset = 0 return param }() private lazy var pageView: WMZPageView = { - let y = UIScreen.statusBarHeight + let y = UIScreen.statusBarHeight + 115 let width = UIScreen.width let height = UIScreen.height - y let pageView = WMZPageView(frame: CGRect(x: 0, y: y, width: width, height: height), param: pageParam, parentReponder: self) pageView.param = pageParam - pageView.backgroundColor = .clear - pageView.downSc?.backgroundColor = .clear // pageView.downSc?.delegate = self pageView.downSc?.dataSource = self pageView.downSc?.isScrollEnabled = true + pageView.downSc?.separatorStyle = .none pageView.downSc?.register(VPHomeItemContentCell.self, forCellReuseIdentifier: "cell") pageView.downSc?.register(VPHomeBannerContentCell.self, forCellReuseIdentifier: VPHomeBannerContentCell.moduleKey.rawValue) pageView.downSc?.register(VPHomeRecommandContentCell.self, forCellReuseIdentifier: VPHomeRecommandContentCell.moduleKey.rawValue) -// pageView.downSc?.rowHeight = UITableView.automaticDimension -// pageView.downSc?.estimatedRowHeight = 100 + pageView.downSc?.register(VPHomeRankingContentCell.self, forCellReuseIdentifier: VPHomeRankingContentCell.moduleKey.rawValue) + pageView.downSc?.vp_addRefreshHeader(block: { [weak self] in + self?.handleHeaderRefresh(nil) + }) + return pageView }() + private lazy var menuTitleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 17) + label.textColor = .colorFFFFFF() + label.text = "kHomeMenuTitle".localized + return label + }() + + private lazy var menuBgView: UIView = { + let view = UIImageView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 94)) + view.image = UIImage(named: "menu_bg_image_01") + + view.addSubview(menuTitleLabel) + menuTitleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview().offset(9) + } + + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 16) + label.textColor = .colorFFFFFF() + label.text = "kHomeTitleText".localized + return label + }() + + private lazy var searchButton: VPHomeSearchButton = { + let button = VPHomeSearchButton() + return button + }() + + private lazy var historyButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "history_icon_01"), for: .normal) + return button + }() + override func viewDidLoad() { super.viewDidLoad() requestHomeData() + setupPageView() vp_setupUI() } @@ -81,22 +135,67 @@ class VPHomePageViewController: VPViewController { self.navigationController?.setNavigationBarHidden(true, animated: true) } - private func createView(title: String) -> WMZPageNaviBtn { - let button = WMZPageNaviBtn(type: .custom) - button.setTitle(title, for: .normal) - button.setTitleColor(.red, for: .normal) - button.setTitleColor(.green, for: .selected) - return button + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + if let vc = self.pageView.upSc.currentVC as? VPViewController { + vc.handleHeaderRefresh { [weak self] in + self?.pageView.downSc?.vp_endHeaderRefreshing() + } + } else { + self.pageView.downSc?.vp_endHeaderRefreshing() + } + } + + private func setButtonState(button: UIButton) { + button.layer.masksToBounds = true + if button.isSelected { + button.vp_setGradient() + button.vp_removeGradientBorder() + } else { + button.vp_removeGradient() + button.vp_setGradientBorder() + } + } + + private func setupPageView() { + self.pageView.downSc?.backgroundColor = .clear + self.pageView.backgroundColor = .clear + + self.pageView.upSc.addSubview(menuBgView) + + self.pageView.upSc.sendSubviewToBack(menuBgView) + + } + } extension VPHomePageViewController { private func vp_setupUI() { - + view.addSubview(titleLabel) + view.addSubview(searchButton) + view.addSubview(historyButton) view.addSubview(pageView) + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 28) + } + + searchButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.equalTo(historyButton.snp.left).offset(-10) + make.top.equalToSuperview().offset(UIScreen.statusBarHeight + 65) + make.height.equalTo(40) + } + + historyButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-15) + make.width.height.equalTo(40) + make.centerY.equalTo(searchButton) + } + } } @@ -106,7 +205,6 @@ extension VPHomePageViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = self.viewModel.newModuleList[indexPath.row] - let cell = tableView.dequeueReusableCell(withIdentifier: item.module_key?.rawValue ?? "cell") as! VPHomeItemContentCell cell.item = item return cell @@ -129,6 +227,10 @@ extension VPHomePageViewController { guard let self = self else { return } if let list = list { self.viewModel.oldModuleList = list + self.pageParam.wTitleArr = self.viewModel.categoryTitleList + self.pageView.updateMenuData() + self.setupPageView() + self.pageView.downSc?.reloadData() } } diff --git a/Veloria/Class/Home/Model/VPCategoryModel.swift b/Veloria/Class/Home/Model/VPCategoryModel.swift new file mode 100644 index 0000000..16bc881 --- /dev/null +++ b/Veloria/Class/Home/Model/VPCategoryModel.swift @@ -0,0 +1,15 @@ +// +// VPCategoryModel.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit +import SmartCodable + +class VPCategoryModel: VPModel, SmartCodable { + + var category_name: String? + var category_id: String? +} diff --git a/Veloria/Class/Home/Model/VPHomeModuleItem.swift b/Veloria/Class/Home/Model/VPHomeModuleItem.swift index b843322..06f2f71 100644 --- a/Veloria/Class/Home/Model/VPHomeModuleItem.swift +++ b/Veloria/Class/Home/Model/VPHomeModuleItem.swift @@ -13,12 +13,15 @@ class VPHomeModuleItem: VPModel, SmartCodable { enum ModuleKey: String, SmartCaseDefaultable { case banner = "home_banner" case v3_recommand = "home_v3_recommand" + case week_ranking = "week_ranking" + case category_navigation = "category_navigation" } var module_key: ModuleKey? var title: String? var list: [VPShortModel]? + var categoryList: [VPCategoryModel]? @SmartAny var data: Any? @@ -26,7 +29,11 @@ class VPHomeModuleItem: VPModel, SmartCodable { func didFinishMapping() { if let data = data as? [[String : Any]] { - self.list = [VPShortModel].deserialize(from: data) + if module_key == .category_navigation { + self.categoryList = [VPCategoryModel].deserialize(from: data) + } else { + self.list = [VPShortModel].deserialize(from: data) + } } else if let data = data as? [String : Any] { title = data["title"] as? String list = [VPShortModel].deserialize(from: data["list"] as? [[String : Any]]) diff --git a/Veloria/Class/Home/View/VPHomeBannerCell.swift b/Veloria/Class/Home/View/VPHomeBannerCell.swift index 3bc3a6a..b878374 100644 --- a/Veloria/Class/Home/View/VPHomeBannerCell.swift +++ b/Veloria/Class/Home/View/VPHomeBannerCell.swift @@ -51,6 +51,7 @@ class VPHomeBannerCell: ZKCycleScrollViewCell { button.jx_font = .fontRegular(ofSize: 10) button.setTitleColor(.colorAFAFAF(), for: .normal) button.setImage(UIImage(named: "hot_icon_01"), for: .normal) + button.space = 1 return button }() diff --git a/Veloria/Class/Home/View/VPHomeListCell.swift b/Veloria/Class/Home/View/VPHomeListCell.swift new file mode 100644 index 0000000..6727c95 --- /dev/null +++ b/Veloria/Class/Home/View/VPHomeListCell.swift @@ -0,0 +1,64 @@ +// +// VPHomeListCell.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +class VPHomeListCell: VPCollectionViewCell { + + var model: VPShortModel? { + didSet { + coverImageView.vp_setImage(url: model?.image_url) + titleLabel.text = model?.name + } + } + + private lazy var coverImageView: VPImageView = { + let imageView = VPImageView() + imageView.layer.cornerRadius = 10 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .colorFFFFFF() + label.numberOfLines = 2 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + vp_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension VPHomeListCell { + + private func vp_setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + + coverImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-31) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalTo(coverImageView.snp.bottom).offset(4) + make.right.lessThanOrEqualToSuperview() + } + } + +} diff --git a/Veloria/Class/Home/View/VPHomeRankingCell.swift b/Veloria/Class/Home/View/VPHomeRankingCell.swift new file mode 100644 index 0000000..7db5daa --- /dev/null +++ b/Veloria/Class/Home/View/VPHomeRankingCell.swift @@ -0,0 +1,146 @@ +// +// VPHomeRankingCell.swift +// Veloria +// +// Created by Veloria on 2025/5/21. +// + +import UIKit + +class VPHomeRankingCell: VPCollectionViewCell { + + var model: VPShortModel? { + didSet { + coverImageView.vp_setImage(url: model?.image_url) + titleLabel.text = model?.name + + if let category = model?.category?.first, !category.isEmpty { + tagView.isHidden = false + tagView.setTitle(category, for: .normal) + } else { + tagView.isHidden = true + } + + let watchCount = model?.watch_total ?? 0 + if watchCount > 1000 { + let numStr = NSNumber(floatLiteral: CGFloat(watchCount) / 1000).toString(maximumFractionDigits: 1) + hotView.setTitle("\(numStr)K", for: .normal) + } else { + hotView.setTitle("\(watchCount)", for: .normal) + } + } + } + + var row: Int = 0 { + didSet { + numLabel.text = "\(row + 1)" + } + } + + private lazy var coverImageView: VPImageView = { + let imageView = VPImageView() + imageView.layer.cornerRadius = 6 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .colorFFFFFF() + label.numberOfLines = 2 + return label + }() + + private lazy var tagView: JXButton = { + let view = JXButton(type: .custom) + view.isUserInteractionEnabled = false + view.jx_font = .fontRegular(ofSize: 10) + view.setTitleColor(.colorAFAFAF(), for: .normal) + view.backgroundColor = .colorFFFFFF(alpha: 0.1) + view.layer.cornerRadius = 3 + view.layer.masksToBounds = true + view.leftAndRightMargin = 5 + return view + }() + + private lazy var hotView: UIButton = { + let button = JXButton(type: .custom) + button.isUserInteractionEnabled = false + button.jx_font = .fontRegular(ofSize: 10) + button.setTitleColor(.colorDEDEDE(), for: .normal) + button.setImage(UIImage(named: "hot_icon_01"), for: .normal) + button.space = 1 + return button + }() + + private lazy var numView: UIView = { + let view = UIImageView(image: UIImage(named: "num_icon_01")) + return view + }() + + private lazy var numLabel: UILabel = { + let label = UILabel() + label.textColor = .colorFFFFFB() + label.font = .numberFont(ofSize: 18) + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + vp_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension VPHomeRankingCell { + + private func vp_setupUI() { + contentView.addSubview(numView) + contentView.addSubview(numLabel) + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(tagView) + contentView.addSubview(hotView) + + + numView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(2) + } + + numLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.centerY.equalToSuperview().offset(3) + } + + coverImageView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.left.equalToSuperview().offset(20) + make.width.equalTo(68) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(10) + make.centerY.equalTo(coverImageView.snp.top).offset(21) + make.right.lessThanOrEqualToSuperview() + } + + tagView.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalToSuperview().offset(43) + make.height.equalTo(16) + } + + hotView.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview() + } + } + +} diff --git a/Veloria/Class/Home/View/VPHomeRankingContentCell.swift b/Veloria/Class/Home/View/VPHomeRankingContentCell.swift new file mode 100644 index 0000000..c8e60a3 --- /dev/null +++ b/Veloria/Class/Home/View/VPHomeRankingContentCell.swift @@ -0,0 +1,108 @@ +// +// VPHomeRankingContentCell.swift +// Veloria +// +// Created by Veloria on 2025/5/21. +// + +import UIKit + +class VPHomeRankingContentCell: VPHomeItemContentCell { + + override class var moduleKey: VPHomeModuleItem.ModuleKey { + return .week_ranking + } + + override var item: VPHomeModuleItem? { + didSet { + collectionView.reloadData() + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "ranking_icon_01")) + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 15) + label.textColor = .colorFFFFFF() + label.text = "Drama Champions".localized + return label + }() + + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: 216, height: 90) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 14 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + return layout + }() + + private lazy var collectionView: UICollectionView = { + let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.register(VPHomeRankingCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + vp_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension VPHomeRankingContentCell { + + private func vp_setupUI() { + containerView.addSubview(iconImageView) + containerView.addSubview(titleLabel) + containerView.addSubview(collectionView) + + iconImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.top.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalTo(iconImageView) + make.left.equalTo(iconImageView.snp.right).offset(3) + } + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(32) + make.height.equalTo(collectionViewLayout.itemSize.height * 3 + collectionViewLayout.minimumInteritemSpacing * 2) + } + + } + +} + +//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- +extension VPHomeRankingContentCell: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let model = item?.list?[indexPath.row] + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPHomeRankingCell + cell.model = model + cell.row = indexPath.row + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return item?.list?.count ?? 0 + } + +} diff --git a/Veloria/Class/Home/View/VPHomeRecommandCell.swift b/Veloria/Class/Home/View/VPHomeRecommandCell.swift new file mode 100644 index 0000000..b999dd5 --- /dev/null +++ b/Veloria/Class/Home/View/VPHomeRecommandCell.swift @@ -0,0 +1,112 @@ +// +// VPHomeRecommandCell.swift +// Veloria +// +// Created by Veloria on 2025/5/20. +// + +import UIKit + +class VPHomeRecommandCell: VPCollectionViewCell { + + enum CellPoint { + case start + case end + case other + } + + var model: VPShortModel? { + didSet { + coverImageView.vp_setImage(url: model?.image_url) + titleLabel.text = model?.name + } + } + + var cellPoint = CellPoint.other { + didSet { + applyDiagonalMask() + } + } + + private lazy var coverImageView: VPImageView = { + let imageView = VPImageView() + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .colorFFFFFF() + label.numberOfLines = 2 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + vp_setupUI() + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + private func applyDiagonalMask() { + let maskLayer = CAShapeLayer() + let path = UIBezierPath() + let width = 137.0 + let height = 152.0 + let cut: CGFloat = 19.5 // 控制斜切角度 + let radius: CGFloat = 14 // 圆角半径 + + if cellPoint == .start { + path.move(to: CGPoint(x: 0, y: radius)) + path.addQuadCurve(to: CGPoint(x: radius, y: 0), controlPoint: CGPoint(x: 0, y: 0)) + } else { + path.move(to: CGPoint(x: cut - 2, y: radius)) + path.addQuadCurve(to: CGPoint(x: cut + radius, y: 0), controlPoint: CGPoint(x: cut, y: 0)) + } + + // 右上角 + path.addLine(to: CGPoint(x: width - radius, y: 0)) + path.addQuadCurve(to: CGPoint(x: width, y: radius), controlPoint: CGPoint(x: width, y: 0)) + // 右下角 + if cellPoint == .end { + path.addLine(to: CGPoint(x: width, y: height - radius)) + path.addQuadCurve(to: CGPoint(x: width - radius, y: height), controlPoint: CGPoint(x: width, y: height)) + } else { + path.addLine(to: CGPoint(x: width - cut, y: height - radius)) + path.addQuadCurve(to: CGPoint(x: width - cut - radius, y: height), controlPoint: CGPoint(x: width - cut, y: height)) + } + // 左下角 + path.addLine(to: CGPoint(x: radius, y: height)) + path.addQuadCurve(to: CGPoint(x: 0, y: height - radius), controlPoint: CGPoint(x: 0, y: height)) + path.close() + + maskLayer.path = path.cgPath + coverImageView.layer.mask = maskLayer + } +} + +extension VPHomeRecommandCell { + + private func vp_setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + + coverImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.right.equalToSuperview() + make.top.equalToSuperview() + make.height.equalTo(152) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalTo(coverImageView.snp.bottom).offset(4) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + } + +} diff --git a/Veloria/Class/Home/View/VPHomeRecommandContentCell.swift b/Veloria/Class/Home/View/VPHomeRecommandContentCell.swift index 625ae9e..13107c2 100644 --- a/Veloria/Class/Home/View/VPHomeRecommandContentCell.swift +++ b/Veloria/Class/Home/View/VPHomeRecommandContentCell.swift @@ -31,11 +31,17 @@ class VPHomeRecommandContentCell: VPHomeItemContentCell { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.itemSize = CGSize(width: 138, height: 152 + 30) + layout.minimumLineSpacing = -6 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) return layout }() private lazy var collectionView: VPCollectionView = { let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.register(VPHomeRecommandCell.self, forCellWithReuseIdentifier: "cell") return collectionView }() @@ -73,13 +79,22 @@ extension VPHomeRecommandContentCell { } //MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource -------------- -//extension VPHomeRecommandContentCell: UICollectionViewDelegate, UICollectionViewDataSource { -// -// func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { -// -// } -// -// func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { -// return self.item?.list?.count ?? 0 -// } -//} +extension VPHomeRecommandContentCell: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPHomeRecommandCell + cell.model = item?.list?[indexPath.row] + if indexPath.row == 0 { + cell.cellPoint = .start + } else if (self.item?.list?.count ?? 0) - 1 == indexPath.row { + cell.cellPoint = .end + } else { + cell.cellPoint = .other + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.item?.list?.count ?? 0 + } +} diff --git a/Veloria/Class/Home/View/VPHomeSearchButton.swift b/Veloria/Class/Home/View/VPHomeSearchButton.swift new file mode 100644 index 0000000..00fea78 --- /dev/null +++ b/Veloria/Class/Home/View/VPHomeSearchButton.swift @@ -0,0 +1,63 @@ +// +// VPHomeSearchButton.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit + +class VPHomeSearchButton: UIControl { + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "search_icon_01")) + return imageView + }() + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + label.textColor = .colorFFFFFF(alpha: 0.3) + label.text = "kSearchPlaceholderText1".localized + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + vp_setupUI() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension VPHomeSearchButton { + + private func vp_setupUI() { + layer.cornerRadius = 6 + layer.masksToBounds = true + layer.borderWidth = 0.7 + layer.borderColor = UIColor.colorFFFFFF(alpha: 0.1).cgColor + backgroundColor = UIColor.colorFFFFFF(alpha: 0.05) + + + addSubview(iconImageView) + addSubview(textLabel) + + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(10) + } + + textLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(iconImageView.snp.right).offset(6) + } + } + +} diff --git a/Veloria/Class/Home/ViewModel/VPHomeViewModel.swift b/Veloria/Class/Home/ViewModel/VPHomeViewModel.swift index 988a9b1..2c52150 100644 --- a/Veloria/Class/Home/ViewModel/VPHomeViewModel.swift +++ b/Veloria/Class/Home/ViewModel/VPHomeViewModel.swift @@ -9,11 +9,6 @@ import UIKit class VPHomeViewModel: VPModel { - var homeDataModel: VPHomeDataModel? { - didSet { - updateDataList() - } - } ///原始模版列表 不可直接使用,需要过滤 var oldModuleList: [VPHomeModuleItem]? { @@ -24,8 +19,9 @@ class VPHomeViewModel: VPModel { ///筛选后的数据 private(set) lazy var newModuleList: [VPHomeModuleItem] = [] - - private(set) lazy var homeDataList: [VPHomeDataItem] = [] + ///分类列表 + private(set) lazy var categoryList: [VPCategoryModel] = [] + private(set) lazy var categoryTitleList: [String] = [] ///筛选模版数据 @@ -36,6 +32,21 @@ class VPHomeViewModel: VPModel { if let key = $0.module_key { if key == .banner { newModuleList.insert($0, at: 0) + + } else if key == .category_navigation { + categoryList.removeAll() + categoryTitleList.removeAll() + + let allCategory = VPCategoryModel() + allCategory.category_name = "All".localized + allCategory.category_id = "0" + categoryList.append(allCategory) + categoryList += ($0.categoryList ?? []) + + categoryList.forEach { model in + categoryTitleList.append(model.category_name ?? "") + } + } else { newModuleList.append($0) } @@ -43,14 +54,4 @@ class VPHomeViewModel: VPModel { }) } - private func updateDataList() { - homeDataList.removeAll() - - if let bannerData = homeDataModel?.bannerData { - let item = VPHomeDataItem() - item.type = .banner - item.list = bannerData - } - - } } diff --git a/Veloria/Libs/Player/VPPlayer.swift b/Veloria/Libs/Player/VPPlayer.swift new file mode 100644 index 0000000..248f2cb --- /dev/null +++ b/Veloria/Libs/Player/VPPlayer.swift @@ -0,0 +1,326 @@ +// +// VPPlayer.swift +// Veloria +// +// Created by Veloria on 2025/5/22. +// + +import UIKit +import ZFPlayer + +@objc protocol VPPlayerDelegate { + ///更新当前总进度 +// @objc optional func vp_onDurationUpdate(_ player: SPPlayer, duration: Int) +// +// ///更新当前进度 +// @objc optional func vp_onCurrentPositionUpdate(_ player: SPPlayer, position: Int) + + ///播放状态变化 + @objc optional func vp_player(_ player: VPPlayer, playStateDidChanged state: VPPlayer.PlayState) + + ///加载状态发生变化 + @objc optional func vp_player(_ player: VPPlayer, loadStateDidChange state: VPPlayer.LoadState) + + ///播放时间发生变化 + @objc optional func vp_playTimeChanged(_ player: VPPlayer, currentTime: Int, duration: Int) + + ///显示首帧 + @objc optional func vp_firstRenderedStart(_ player: VPPlayer) + + ///准备完成 + @objc optional func vp_playerReadyToPlay(_ player: VPPlayer) + + ///播放完成 + @objc optional func vp_playCompletion(_ player: VPPlayer) + + ///缓冲完成 + @objc optional func vp_playLoadingEnd(_ player: VPPlayer) +} + +class VPPlayer: NSObject { + + @objc enum PlayState: Int { + case unknown + case playing + case paused + case failed + case stopped + } + + @objc enum LoadState: Int { + case unknown + case prepare + case playable + case playthroughOK + case stalled + } + + weak var delegate: VPPlayerDelegate? + + private(set) lazy var isPlaying = false + private(set) lazy var playState: PlayState = .unknown + private(set) lazy var loadState: LoadState = .unknown + + /** + 是否添加息屏监控 + */ + private var isAddIdleTimerDisabledObserver = false + + ///音频占用情况 + private var interruptionType: AVAudioSession.InterruptionType? + + ///总进度 + var duration: Int { + return Int(self.player.totalTime) + } + ///当前进度 + var currentPosition: Int { + return Int(self.player.currentTime) + } + + ///0.5 - 2 + var rate: Float { + set { + player.rate = newValue + } + get { + return player.rate + } + } + + var playerView: UIView? { + didSet { + playerView?.addSubview(player.view) + player.view.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + } + + private lazy var player: ZFAVPlayerManager = { + let player = ZFAVPlayerManager() + player.shouldAutoPlay = false + return player + }() + + var isLoop = true + + deinit { + NotificationCenter.default.removeObserver(self) + self.stop() + } + + override init() { + super.init() + //声音被打断的通知(电话打来) + NotificationCenter.default.addObserver(self, selector: #selector(interruptionNotification(sender:)), name: AVAudioSession.interruptionNotification, object: nil) + //耳机插入和拔出的通知 + NotificationCenter.default.addObserver(self, selector: #selector(routeChangeNotification(sender:)), name: AVAudioSession.routeChangeNotification, object: nil) + + player.scalingMode = .aspectFill + vp_addAction() + } + + /** + 添加息屏监控 + */ + private func addIdleTimerDisabledObserver() { + if !isAddIdleTimerDisabledObserver { + isAddIdleTimerDisabledObserver = true + UIApplication.shared.addObserver(self, forKeyPath: "idleTimerDisabled", options: NSKeyValueObservingOptions.new, context: nil) + } + } + /** + 删除息屏监控 + */ + private func removeIdleTimerDisabledObserver() { + if isAddIdleTimerDisabledObserver { + isAddIdleTimerDisabledObserver = false + UIApplication.shared.removeObserver(self, forKeyPath: "idleTimerDisabled") + } + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if !UIApplication.shared.isIdleTimerDisabled { + UIApplication.shared.isIdleTimerDisabled = true + } + } + + func setPlayUrl(url: String) { + let proxyURL = KTVHTTPCache.proxyURL(withOriginalURL: URL(string: url)) + if proxyURL != self.player.assetURL { + self.player.assetURL = proxyURL + } +// self.prepare() + } + + ///准备播放 + func prepare() { + self.player.prepareToPlay() + } + + func stop() { + self.isPlaying = false + player.stop() + + self.removeIdleTimerDisabledObserver() + UIApplication.shared.isIdleTimerDisabled = false + } + + func start() { + self.isPlaying = true + if self.interruptionType != .began { + player.play() + } + + UIApplication.shared.isIdleTimerDisabled = true + self.addIdleTimerDisabledObserver() + + } + + ///暂停 + func pause() { + self.isPlaying = false + player.pause() + + self.removeIdleTimerDisabledObserver() + UIApplication.shared.isIdleTimerDisabled = false + } + + ///从头播放 + func replay() { + self.isPlaying = true + self.player.replay() + + UIApplication.shared.isIdleTimerDisabled = true + self.addIdleTimerDisabledObserver() + } + + func seekToTime(toTime: Int) { + var time = toTime + if time < 0 { + time = 0 + } + if time > self.duration { + time = self.duration + } + self.player.seek(toTime: TimeInterval(time), completionHandler: nil) + } + + +} + +extension VPPlayer { + + private func vp_addAction() { + //进度发生变化 + player.playerPlayTimeChanged = { [weak self] (asset, currentTime, duration) in + guard let self = self else { return } + self.delegate?.vp_playTimeChanged?(self, currentTime: Int(currentTime), duration: Int(duration)) + } + + //播放状态 + player.playerPlayStateChanged = { [weak self] (asset, playState) in + guard let self = self else { return } + if playState == .playStatePlaying && !isPlaying { + self.pause() + } else if playState == .playStatePaused, isPlaying, self.interruptionType != .began { + self.start() + } + switch playState { + case .playStateUnknown: + self.playState = .unknown + case .playStatePlaying: + self.playState = .playing + case .playStatePaused: + self.playState = .paused + case .playStatePlayStopped: + self.playState = .stopped + case .playStatePlayFailed: + self.playState = .failed + + default: + self.playState = .unknown + } + self.delegate?.vp_player?(self, playStateDidChanged: self.playState) + vpLog(message: "播放状态====\(playState)") + } + + //加载状态 + player.playerLoadStateChanged = { [weak self] (asset, loadState) in + guard let self = self else { return } + if loadState == .playable && !isPlaying { + self.pause() + } else if loadState == .playable, isPlaying, self.player.playState != .playStatePlaying, self.interruptionType != .began { + self.start() + } + + switch loadState { + case .prepare: + self.loadState = .prepare + case .playable: + self.loadState = .playable + case .playthroughOK: + self.loadState = .playthroughOK + case .stalled: + self.loadState = .stalled + default: + self.loadState = .unknown + break + } + self.delegate?.vp_player?(self, loadStateDidChange: self.loadState) + } + + //错误信息 + player.playerPlayFailed = { [weak self] (asset, error) in + vpLog(message: "错误信息====\(error)") + } + + //播放结束 + player.playerDidToEnd = { [weak self] (asset) in + guard let self = self else { return } + if isLoop { + self.replay() + } else { + self.isPlaying = false + self.prepare() + self.delegate?.vp_playCompletion?(self) + } + } + + player.playerReadyToPlay = { [weak self] (asset, assetURL) in + guard let self = self else { return } + do { + try AVAudioSession.sharedInstance().setCategory(.playback) + try AVAudioSession.sharedInstance().setActive(true) + } catch { + + } + self.delegate?.vp_playerReadyToPlay?(self) + } + } + +} + +//MARK: -------------- 监听视频中断 -------------- +extension VPPlayer { + + @objc private func interruptionNotification(sender: Notification) { + guard let userInfo = sender.userInfo else { return } + guard let interruptionType = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt else { return } + + self.interruptionType = AVAudioSession.InterruptionType(rawValue: interruptionType) + if self.interruptionType == .began { + self.player.pause() + } else { + if self.isPlaying { + self.player.play() + } + } + + } + + @objc private func routeChangeNotification(sender: Notification) { +// self.pause() + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@2x.png b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@2x.png new file mode 100644 index 0000000..b9e80c2 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@3x.png b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@3x.png new file mode 100644 index 0000000..111dd77 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@3x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5987f00 --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Component 20@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Component 20@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Contents.json new file mode 100644 index 0000000..d9d9a88 --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Ellipse 4@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Ellipse 4@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@2x.png b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@2x.png new file mode 100644 index 0000000..f5bf43e Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@3x.png b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@3x.png new file mode 100644 index 0000000..632ad9c Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@3x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@2x.png b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@2x.png new file mode 100644 index 0000000..ad33e5b Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@3x.png b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@3x.png new file mode 100644 index 0000000..261ddfe Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@3x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Contents.json new file mode 100644 index 0000000..77c7666 --- /dev/null +++ b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@2x.png b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@2x.png new file mode 100644 index 0000000..1c2741c Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@3x.png b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@3x.png new file mode 100644 index 0000000..7206843 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@3x.png differ diff --git a/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..4c04d3d --- /dev/null +++ b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "顶部bg1 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "顶部bg1 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@2x.png b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@2x.png new file mode 100644 index 0000000..9069eef Binary files /dev/null and b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@2x.png differ diff --git a/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@3x.png b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@3x.png new file mode 100644 index 0000000..63e7e58 Binary files /dev/null and b/Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@3x.png differ diff --git a/Veloria/Source/Font/PlayfairDisplay-VariableFont_wght.ttf b/Veloria/Source/Font/PlayfairDisplay-VariableFont_wght.ttf new file mode 100644 index 0000000..ded0ae5 Binary files /dev/null and b/Veloria/Source/Font/PlayfairDisplay-VariableFont_wght.ttf differ diff --git a/Veloria/Source/Info.plist b/Veloria/Source/Info.plist index 0eb786d..50cf834 100644 --- a/Veloria/Source/Info.plist +++ b/Veloria/Source/Info.plist @@ -2,6 +2,10 @@ + UIAppFonts + + PlayfairDisplay-VariableFont_wght.ttf + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Veloria/Source/Veloria-Bridging-Header.h b/Veloria/Source/Veloria-Bridging-Header.h index 1e816d2..07194d1 100644 --- a/Veloria/Source/Veloria-Bridging-Header.h +++ b/Veloria/Source/Veloria-Bridging-Header.h @@ -6,3 +6,6 @@ #import #import "JXUUID.h" #import +#import +#import +#import diff --git a/Veloria/Source/en.lproj/Localizable.strings b/Veloria/Source/en.lproj/Localizable.strings index ca69445..d760936 100644 --- a/Veloria/Source/en.lproj/Localizable.strings +++ b/Veloria/Source/en.lproj/Localizable.strings @@ -8,3 +8,11 @@ "Home" = "Home"; "All" = "All"; +"Drama Champions" = "Drama Champions"; +"Explore" = "Explore"; + + + +"kHomeTitleText" = "10,000+ addictive shorts await!"; +"kSearchPlaceholderText1" = "Search dramas"; +"kHomeMenuTitle" = "Select Categories";