首页开发完成
@ -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 = "<group>"; };
|
||||
BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRecommandContentCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionView.swift; sourceTree = "<group>"; };
|
||||
BF0FA7272DDC91F800C9E5F2 /* VPCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA7292DDC922F00C9E5F2 /* VPHomeRecommandCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRecommandCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA72B2DDD7B7300C9E5F2 /* VPHomeRankingContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRankingContentCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA72D2DDD7DD400C9E5F2 /* VPHomeRankingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRankingCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA72F2DDEBB1600C9E5F2 /* UIButton+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+VPAdd.swift"; sourceTree = "<group>"; };
|
||||
BF0FA7312DDEBD6400C9E5F2 /* AppDelegate+Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Config.swift"; sourceTree = "<group>"; };
|
||||
BF0FA7332DDEC74500C9E5F2 /* VPCategoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCategoryModel.swift; sourceTree = "<group>"; };
|
||||
BF0FA7362DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "PlayfairDisplay-VariableFont_wght.ttf"; sourceTree = "<group>"; };
|
||||
BF0FA7382DDECF8900C9E5F2 /* VPHomeListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeListViewController.swift; sourceTree = "<group>"; };
|
||||
BF0FA73A2DDED1C700C9E5F2 /* VPHomeListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeListCell.swift; sourceTree = "<group>"; };
|
||||
BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVideoAPI.swift; sourceTree = "<group>"; };
|
||||
BF0FA73E2DDEF26E00C9E5F2 /* VPHomeSearchButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeSearchButton.swift; sourceTree = "<group>"; };
|
||||
BF0FA7402DDEFBC700C9E5F2 /* UIScrollView+VPRefresh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+VPRefresh.swift"; sourceTree = "<group>"; };
|
||||
BF0FA7442DDF027900C9E5F2 /* VPPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPPlayer.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -183,6 +211,7 @@
|
||||
children = (
|
||||
1B056E212DDAC0FD007EE38D /* AppDelegate.swift */,
|
||||
1B056E282DDAC0FD007EE38D /* SceneDelegate.swift */,
|
||||
BF0FA7312DDEBD6400C9E5F2 /* AppDelegate+Config.swift */,
|
||||
);
|
||||
path = AppDelegate;
|
||||
sourceTree = "<group>";
|
||||
@ -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 = "<group>";
|
||||
@ -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 = "<group>";
|
||||
@ -329,6 +364,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1B056E562DDACC6B007EE38D /* VPHomePageViewController.swift */,
|
||||
BF0FA7382DDECF8900C9E5F2 /* VPHomeListViewController.swift */,
|
||||
);
|
||||
path = Controller;
|
||||
sourceTree = "<group>";
|
||||
@ -416,6 +452,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF0FA6F82DDC64E700C9E5F2 /* VPHomeAPI.swift */,
|
||||
BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */,
|
||||
);
|
||||
path = API;
|
||||
sourceTree = "<group>";
|
||||
@ -426,6 +463,7 @@
|
||||
BF0FA6FB2DDC657500C9E5F2 /* VPHomeDataModel.swift */,
|
||||
BF0FA7062DDC687C00C9E5F2 /* VPHomeDataItem.swift */,
|
||||
BF0FA7112DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift */,
|
||||
BF0FA7332DDEC74500C9E5F2 /* VPCategoryModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -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 = "<group>";
|
||||
@ -483,6 +528,43 @@
|
||||
path = JXButton;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF0FA7352DDECC4600C9E5F2 /* Font */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF0FA7362DDECC5400C9E5F2 /* PlayfairDisplay-VariableFont_wght.ttf */,
|
||||
);
|
||||
path = Font;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF0FA7422DDF024400C9E5F2 /* Explore */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Explore;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF0FA7432DDF025A00C9E5F2 /* Player */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF0FA7442DDF027900C9E5F2 /* VPPlayer.swift */,
|
||||
);
|
||||
path = Player;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF0FA7462DDF03AD00C9E5F2 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF0FA7472DDF03B600C9E5F2 /* Controller */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Controller;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* 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 */,
|
||||
);
|
||||
|
16
Veloria/AppDelegate/AppDelegate+Config.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// AppDelegate+Config.swift
|
||||
// Veloria
|
||||
//
|
||||
// Created by Veloria on 2025/5/22.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension AppDelegate {
|
||||
|
||||
func appConfig() {
|
||||
UIButton.vp_Awake()
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
|
||||
|
@ -27,7 +27,9 @@ class VPViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func handleHeaderRefresh(_ completer: (() -> Void)?) {}
|
||||
|
||||
func handleFooterRefresh(_ completer: (() -> Void)?) {}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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!)
|
||||
}
|
||||
}
|
||||
|
118
Veloria/Base/Extension/UIButton+VPAdd.swift
Normal file
@ -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()
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
59
Veloria/Base/Extension/UIScrollView+VPRefresh.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
29
Veloria/Base/Networking/API/VPVideoAPI.swift
Normal file
@ -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<VPShortModel>?) -> 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<VPListModel<VPShortModel>>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
Veloria/Base/View/VPCollectionViewCell.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
119
Veloria/Class/Home/Controller/VPHomeListViewController.swift
Normal file
@ -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?()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
15
Veloria/Class/Home/Model/VPCategoryModel.swift
Normal file
@ -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?
|
||||
}
|
@ -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]])
|
||||
|
@ -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
|
||||
}()
|
||||
|
||||
|
64
Veloria/Class/Home/View/VPHomeListCell.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
146
Veloria/Class/Home/View/VPHomeRankingCell.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
108
Veloria/Class/Home/View/VPHomeRankingContentCell.swift
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
112
Veloria/Class/Home/View/VPHomeRecommandCell.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
63
Veloria/Class/Home/View/VPHomeSearchButton.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
326
Veloria/Libs/Player/VPPlayer.swift
Normal file
@ -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()
|
||||
}
|
||||
}
|
BIN
Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Component 20@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
22
Veloria/Source/Assets.xcassets/icon/history_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
22
Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
BIN
Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Veloria/Source/Assets.xcassets/icon/num_icon_01.imageset/Ellipse 4@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.5 KiB |
22
Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
BIN
Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Veloria/Source/Assets.xcassets/icon/ranking_icon_01.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.9 KiB |
22
Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
BIN
Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@2x.png
vendored
Normal file
After Width: | Height: | Size: 877 B |
BIN
Veloria/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame 2@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
22
Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/Contents.json
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
BIN
Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@2x.png
vendored
Normal file
After Width: | Height: | Size: 155 KiB |
BIN
Veloria/Source/Assets.xcassets/image/menu_bg_image_01.imageset/顶部bg1 1@3x.png
vendored
Normal file
After Width: | Height: | Size: 265 KiB |
BIN
Veloria/Source/Font/PlayfairDisplay-VariableFont_wght.ttf
Normal file
@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>PlayfairDisplay-VariableFont_wght.ttf</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
@ -6,3 +6,6 @@
|
||||
#import <WMZPageController/WMZPageController.h>
|
||||
#import "JXUUID.h"
|
||||
#import <Toast.h>
|
||||
#import <MJRefresh/MJRefresh.h>
|
||||
#import <ZFPlayer.h>
|
||||
#import <KTVHTTPCache/KTVHTTPCache.h>
|
||||
|
@ -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";
|
||||
|