首页功能开发
1
Podfile
@ -24,6 +24,7 @@ target 'ShortPlay' do
|
|||||||
pod 'Toast' #吐司提示
|
pod 'Toast' #吐司提示
|
||||||
pod 'ZFPlayer/AVPlayer' #播放器
|
pod 'ZFPlayer/AVPlayer' #播放器
|
||||||
pod 'KTVHTTPCache' #视频缓存
|
pod 'KTVHTTPCache' #视频缓存
|
||||||
|
pod 'HWPanModal' #底部弹出控制器
|
||||||
|
|
||||||
|
|
||||||
target 'ShortPlayTests' do
|
target 'ShortPlayTests' do
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- Alamofire (5.10.2)
|
- Alamofire (5.10.2)
|
||||||
- CocoaAsyncSocket (7.6.5)
|
- CocoaAsyncSocket (7.6.5)
|
||||||
|
- HWPanModal (0.9.9)
|
||||||
- KTVHTTPCache (3.0.2):
|
- KTVHTTPCache (3.0.2):
|
||||||
- CocoaAsyncSocket
|
- CocoaAsyncSocket
|
||||||
- MJRefresh (3.7.9)
|
- MJRefresh (3.7.9)
|
||||||
@ -19,6 +20,7 @@ PODS:
|
|||||||
- ZFPlayer/Core (4.1.4)
|
- ZFPlayer/Core (4.1.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- HWPanModal
|
||||||
- KTVHTTPCache
|
- KTVHTTPCache
|
||||||
- MJRefresh
|
- MJRefresh
|
||||||
- Moya
|
- Moya
|
||||||
@ -32,6 +34,7 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- Alamofire
|
- Alamofire
|
||||||
- CocoaAsyncSocket
|
- CocoaAsyncSocket
|
||||||
|
- HWPanModal
|
||||||
- KTVHTTPCache
|
- KTVHTTPCache
|
||||||
- MJRefresh
|
- MJRefresh
|
||||||
- Moya
|
- Moya
|
||||||
@ -44,6 +47,7 @@ SPEC REPOS:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
|
HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2
|
||||||
KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86
|
KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86
|
||||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||||
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
||||||
@ -53,6 +57,6 @@ SPEC CHECKSUMS:
|
|||||||
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
||||||
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
||||||
|
|
||||||
PODFILE CHECKSUM: 422706ab4a12e286d2106acff478496d948912e8
|
PODFILE CHECKSUM: 6b8807090aa7efc034949fa4dae871806b3d46fd
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
@ -444,6 +444,7 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ZyreoTV;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||||
@ -478,6 +479,7 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ZyreoTV;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||||
|
@ -10,7 +10,7 @@ import UIKit
|
|||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
|
|
||||||
func appConfig() {
|
func appConfig() {
|
||||||
// UIView.et_Awake()
|
UIView.sp_Awake()
|
||||||
tabBarConfig()
|
tabBarConfig()
|
||||||
// keyBoardStyle()
|
// keyBoardStyle()
|
||||||
|
|
||||||
|
@ -23,11 +23,18 @@ class SPViewController: UIViewController, JYPageChildContollerProtocol {
|
|||||||
private(set) var isViewDidLoad = false
|
private(set) var isViewDidLoad = false
|
||||||
private(set) var isDidAppear = false
|
private(set) var isDidAppear = false
|
||||||
|
|
||||||
|
private(set) lazy var _bgImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "main_bg_image_01"))
|
||||||
|
imageView.isUserInteractionEnabled = true
|
||||||
|
return imageView;
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
self.isViewDidLoad = true
|
self.isViewDidLoad = true
|
||||||
self.edgesForExtendedLayout = []
|
self.edgesForExtendedLayout = []
|
||||||
self.view.backgroundColor = .black
|
|
||||||
|
setBgImageView()
|
||||||
|
|
||||||
if let navi = navigationController {
|
if let navi = navigationController {
|
||||||
if navi.visibleViewController == self {
|
if navi.visibleViewController == self {
|
||||||
@ -38,6 +45,11 @@ class SPViewController: UIViewController, JYPageChildContollerProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setBgImageView() {
|
||||||
|
self.view = self._bgImageView
|
||||||
|
self.view.backgroundColor = .black
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
isDidAppear = true
|
isDidAppear = true
|
||||||
|
@ -56,3 +56,21 @@ public func spLog(message:Any? , file: String = #file, function: String = #funct
|
|||||||
print("\n\(Date(timeIntervalSinceNow: 8 * 60 * 60)) \(file.components(separatedBy: "/").last ?? "") \(function) \(line): \(message ?? "")")
|
print("\n\(Date(timeIntervalSinceNow: 8 * 60 * 60)) \(file.components(separatedBy: "/").last ?? "") \(function) \(line): \(message ?? "")")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func sp_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!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
66
ShortPlay/Base/Extension/CGMutablePath+SPAdd.swift
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// CGMutablePath+SPAdd.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/** 圆角大小 */
|
||||||
|
struct SPCirculars {
|
||||||
|
var topLeft:CGFloat = 0
|
||||||
|
var topRight:CGFloat = 0
|
||||||
|
var bottomLeft:CGFloat = 0
|
||||||
|
var bottomRight:CGFloat = 0
|
||||||
|
|
||||||
|
public static let zero = SPCirculars(topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0)
|
||||||
|
public init(topLeft: CGFloat, topRight:CGFloat, bottomLeft:CGFloat, bottomRight:CGFloat) {
|
||||||
|
self.topLeft = topLeft
|
||||||
|
self.topRight = topRight
|
||||||
|
self.bottomLeft = bottomLeft
|
||||||
|
self.bottomRight = bottomRight
|
||||||
|
}
|
||||||
|
static func ==(v1:SPCirculars, v2:SPCirculars) -> Bool {
|
||||||
|
return v1.bottomLeft == v2.bottomLeft
|
||||||
|
&& v1.bottomRight == v2.bottomRight
|
||||||
|
&& v1.topLeft == v2.topLeft
|
||||||
|
&& v1.topRight == v2.topRight
|
||||||
|
}
|
||||||
|
static func !=(v1:SPCirculars, v2:SPCirculars) -> Bool {
|
||||||
|
return !(v1 == v2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CGMutablePath {
|
||||||
|
func addRadiusRectangle(_ circulars: SPCirculars, rect: CGRect) {
|
||||||
|
let minX = rect.minX
|
||||||
|
let minY = rect.minY
|
||||||
|
let maxX = rect.maxX
|
||||||
|
let maxY = rect.maxY
|
||||||
|
|
||||||
|
//获取四个圆心
|
||||||
|
let topLeftCenterX = minX + circulars.topLeft
|
||||||
|
let topLeftCenterY = minY + circulars.topLeft
|
||||||
|
|
||||||
|
let topRightCenterX = maxX - circulars.topRight
|
||||||
|
let topRightCenterY = minY + circulars.topRight
|
||||||
|
|
||||||
|
let bottomLeftCenterX = minX + circulars.bottomLeft
|
||||||
|
let bottomLeftCenterY = maxY - circulars.bottomLeft
|
||||||
|
|
||||||
|
let bottomRightCenterX = maxX - circulars.bottomRight
|
||||||
|
let bottomRightCenterY = maxY - circulars.bottomRight
|
||||||
|
|
||||||
|
//顶 左
|
||||||
|
addArc(center: CGPoint(x: topLeftCenterX, y: topLeftCenterY), radius: circulars.topLeft, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 3 / 2, clockwise: false)
|
||||||
|
//顶右
|
||||||
|
addArc(center: CGPoint(x: topRightCenterX, y: topRightCenterY), radius: circulars.topRight, startAngle: CGFloat.pi * 3 / 2, endAngle: 0, clockwise: false)
|
||||||
|
//底右
|
||||||
|
addArc(center: CGPoint(x: bottomRightCenterX, y: bottomRightCenterY), radius: circulars.bottomRight, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: false)
|
||||||
|
//底左
|
||||||
|
addArc(center: CGPoint(x: bottomLeftCenterX, y: bottomLeftCenterY), radius: circulars.bottomLeft, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: false)
|
||||||
|
closeSubpath();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -43,5 +43,25 @@ extension UIColor {
|
|||||||
static func color7F7F80(alpha: CGFloat = 1) -> UIColor {
|
static func color7F7F80(alpha: CGFloat = 1) -> UIColor {
|
||||||
return color(hex: 0x7F7F80, alpha: alpha)
|
return color(hex: 0x7F7F80, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func colorD2D2D2(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xD2D2D2, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorBF6BFF(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xBF6BFF, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func color121418(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0x121418, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorF564B6(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xF564B6, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorF56490(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xF56490, alpha: alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +20,8 @@ extension UIFont {
|
|||||||
static func fontBold(ofSize: CGFloat) -> UIFont {
|
static func fontBold(ofSize: CGFloat) -> UIFont {
|
||||||
return .systemFont(ofSize: ofSize, weight: .bold)
|
return .systemFont(ofSize: ofSize, weight: .bold)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func fontLight(ofSize: CGFloat) -> UIFont {
|
||||||
|
return .systemFont(ofSize: ofSize, weight: .light)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
ShortPlay/Base/Extension/UIStackView+SPAdd.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// UIStackView+SPAdd.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIStackView {
|
||||||
|
func removeAllArrangedSubview() {
|
||||||
|
let arrangedSubviews = self.arrangedSubviews
|
||||||
|
|
||||||
|
arrangedSubviews.forEach {
|
||||||
|
self.removeArrangedSubview($0)
|
||||||
|
$0.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,103 @@ import UIKit
|
|||||||
import SnapKit
|
import SnapKit
|
||||||
|
|
||||||
extension UIView {
|
extension UIView {
|
||||||
|
fileprivate struct AssociatedKeys {
|
||||||
|
static var sp_tapGesture: Int?
|
||||||
|
static var sp_effect: Int?
|
||||||
|
static var sp_circulars: Int?
|
||||||
|
}
|
||||||
|
@objc public static func sp_Awake() {
|
||||||
|
sp_swizzled_instanceMethod("sp", oldClass: self, oldSelector: "layoutSubviews", newClass: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func sp_layoutSubviews() {
|
||||||
|
sp_layoutSubviews()
|
||||||
|
_updateRadius()
|
||||||
|
|
||||||
|
if let effectView = effectView, effectView.frame != self.bounds {
|
||||||
|
effectView.frame = self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///获得当前的响应视图
|
||||||
|
func getFirstResponderView() -> UIView? {
|
||||||
|
var resultView: UIView? = nil
|
||||||
|
|
||||||
|
for view in self.subviews {
|
||||||
|
if view.isFirstResponder {
|
||||||
|
resultView = view
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
resultView = view.getFirstResponderView()
|
||||||
|
if resultView != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: -------------- 模糊效果 --------------
|
||||||
|
extension UIView {
|
||||||
|
private var effectView: UIVisualEffectView? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AssociatedKeys.sp_effect) as? UIVisualEffectView
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AssociatedKeys.sp_effect, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///添加模糊效果
|
||||||
|
func addEffectView(style: UIBlurEffect.Style = .dark) {
|
||||||
|
if self.effectView == nil {
|
||||||
|
let blur = UIBlurEffect(style: style)
|
||||||
|
let effectView = UIVisualEffectView(effect: blur)
|
||||||
|
self.addSubview(effectView)
|
||||||
|
self.sendSubviewToBack(effectView)
|
||||||
|
|
||||||
|
self.effectView = effectView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///删除模糊效果
|
||||||
|
func removeEffectView() {
|
||||||
|
self.effectView?.removeFromSuperview()
|
||||||
|
self.effectView = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- 圆角 --------------
|
||||||
|
extension UIView {
|
||||||
|
|
||||||
|
|
||||||
|
private var circulars: SPCirculars? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AssociatedKeys.sp_circulars) as? SPCirculars
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
objc_setAssociatedObject(self, &AssociatedKeys.sp_circulars, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRadius(topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) {
|
||||||
|
//清空其它设置方法
|
||||||
|
self.circulars = SPCirculars(topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight)
|
||||||
|
_updateRadius()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _updateRadius() {
|
||||||
|
guard let circulars = self.circulars else { return }
|
||||||
|
let rect = self.bounds
|
||||||
|
|
||||||
|
let path = CGMutablePath()
|
||||||
|
path.addRadiusRectangle(circulars, rect: rect)
|
||||||
|
|
||||||
|
let maskLayer = CAShapeLayer()
|
||||||
|
maskLayer.frame = self.bounds
|
||||||
|
maskLayer.path = path
|
||||||
|
self.layer.mask = maskLayer
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,4 +25,26 @@ class SPHomeAPI: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///首页顶部数据
|
||||||
|
static func requestHomeTopData(completer: ((_ model: SPHomeTopModel?) -> Void)?) {
|
||||||
|
let param = SPNetworkParameters(path: "/homeTop")
|
||||||
|
// param.method = .get
|
||||||
|
|
||||||
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPHomeTopModel>) in
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///首页模块接口
|
||||||
|
static func requestHomeModuleData(completer: ((_ model: SPHomeModuleModel?) -> Void)?) {
|
||||||
|
var param = SPNetworkParameters(path: "/homeModuleData")
|
||||||
|
param.method = .get
|
||||||
|
|
||||||
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPHomeModuleModel>) in
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,18 @@ import UIKit
|
|||||||
class SPVideoAPI: NSObject {
|
class SPVideoAPI: NSObject {
|
||||||
|
|
||||||
///获取视频详情
|
///获取视频详情
|
||||||
static func requestVideoDetail(videoId: String, shortPlayId: String, completer: ((_ model: SPVideoDetailModel?) -> Void)?) {
|
static func requestVideoDetail(videoId: String?, shortPlayId: String, completer: ((_ model: SPVideoDetailModel?) -> Void)?) {
|
||||||
|
var parameters: [String : Any] = [
|
||||||
var param = SPNetworkParameters(path: "/getVideoDetails")
|
|
||||||
param.method = .get
|
|
||||||
param.parameters = [
|
|
||||||
"video_id" : videoId,
|
|
||||||
"short_play_id" : shortPlayId
|
"short_play_id" : shortPlayId
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if let videoId = videoId {
|
||||||
|
parameters["video_id"] = videoId
|
||||||
|
}
|
||||||
|
var param = SPNetworkParameters(path: "/getVideoDetails")
|
||||||
|
param.method = .get
|
||||||
|
param.parameters = parameters
|
||||||
|
|
||||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPVideoDetailModel>) in
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPVideoDetailModel>) in
|
||||||
completer?(response.data)
|
completer?(response.data)
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,26 @@ import UIKit
|
|||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
let SPBaseURL = "https://test1-api.guyantv.com"
|
||||||
let SPWebBaseURL = "https://test1-api.guyantv.com"
|
let SPWebBaseURL = "https://www.guyantv.com"
|
||||||
#else
|
#else
|
||||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
let SPBaseURL = "https://test1-api.guyantv.com"
|
||||||
let SPWebBaseURL = "https://test1-api.guyantv.com"
|
let SPWebBaseURL = "https://www.guyantv.com"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
///用户协议
|
||||||
|
let SPUserAgreementWebUrl = SPWebBaseURL + "/user_policy.htm"
|
||||||
|
///隐私协议
|
||||||
|
let SPPrivacyPolicyWebUrl = SPWebBaseURL + "/private.htm"
|
||||||
|
///儿童个人信息保护规则
|
||||||
|
let SPInformationProtectionWebUrl = SPWebBaseURL + "/information_protection.html"
|
||||||
|
///第三方共享清单
|
||||||
|
let SPInformationSharingWebUrl = SPWebBaseURL + "/information_sharing.html"
|
||||||
|
///收集个人信息明示清单
|
||||||
|
let SPPersoInforDisclosureWebUrl = SPWebBaseURL + "/persoInfor_disclosure.html"
|
||||||
|
///全国青少年互联网文明公约
|
||||||
|
let SPCivizatioConventionWebUrl = SPWebBaseURL + "/civizatio_convention.html"
|
||||||
|
///全国青少年互联网文明公约
|
||||||
|
let SPMemberShipAgreementWebUrl = SPWebBaseURL + "/member_ship_agreement.html"
|
||||||
|
|
||||||
|
|
||||||
|
44
ShortPlay/Base/View/SPGradientView.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// SPGradientView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPGradientView: UIView {
|
||||||
|
|
||||||
|
override class var layerClass: AnyClass {
|
||||||
|
return CAGradientLayer.self
|
||||||
|
}
|
||||||
|
|
||||||
|
var gradientLayer: CAGradientLayer {
|
||||||
|
return self.layer as! CAGradientLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
var locations: [NSNumber]? {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.locations = locations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var colors: [CGColor]? {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.colors = colors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var startPoint: CGPoint = .zero {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.startPoint = startPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var endPoint: CGPoint = .zero {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.endPoint = endPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
ShortPlay/Base/View/SPTableView.swift
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// SPTableView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPTableView: UITableView {
|
||||||
|
|
||||||
|
var insetGroupedMargins: CGFloat = 12
|
||||||
|
|
||||||
|
override init(frame: CGRect, style: UITableView.Style) {
|
||||||
|
super.init(frame: frame, style: style)
|
||||||
|
// separatorColor = .lineColor()
|
||||||
|
self.backgroundColor = .clear
|
||||||
|
self.contentInsetAdjustmentBehavior = .never
|
||||||
|
|
||||||
|
if style == .insetGrouped {
|
||||||
|
sectionFooterHeight = 12
|
||||||
|
sectionHeaderHeight = 0.1
|
||||||
|
} else if style == .plain {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
sectionHeaderTopPadding = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
///修改 insetGrouped 的边距 参考https://github.com/QMUI/QMUIDemo_iOS/blob/master/QMUI/QMUIKit/UIKitExtensions/UITableView%2BQMUI.m
|
||||||
|
override var layoutMargins: UIEdgeInsets {
|
||||||
|
set {
|
||||||
|
super.layoutMargins = newValue
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
var margins = super.layoutMargins
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if self.style == .insetGrouped {
|
||||||
|
margins.left = self.safeAreaInsets.left + insetGroupedMargins
|
||||||
|
margins.right = self.safeAreaInsets.right + insetGroupedMargins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return margins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
ShortPlay/Base/View/SPTableViewCell.swift
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// SPTableViewCell.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
var indicatorMargin: CGFloat = 7.5 {
|
||||||
|
didSet {
|
||||||
|
indicatorImageView.snp.updateConstraints { make in
|
||||||
|
make.right.equalToSuperview().offset(-indicatorMargin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var showIndicator = false {
|
||||||
|
didSet {
|
||||||
|
indicatorImageView.isHidden = !showIndicator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private(set) lazy var indicatorImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "right_arrow_icon_01"))
|
||||||
|
imageView.isHidden = !showIndicator
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
self.layer.rasterizationScale = UIScreen.main.scale
|
||||||
|
self.layer.shouldRasterize = true
|
||||||
|
self.selectionStyle = .none
|
||||||
|
self.backgroundColor = .clear
|
||||||
|
|
||||||
|
contentView.addSubview(indicatorImageView)
|
||||||
|
indicatorImageView.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.right.equalToSuperview().offset(-indicatorMargin)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
// Initialization code
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||||
|
super.setSelected(selected, animated: animated)
|
||||||
|
|
||||||
|
// Configure the view for the selected state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UITableViewCell {
|
||||||
|
|
||||||
|
var sp_tableView: UITableView? {
|
||||||
|
return self.value(forKey: "_tableView") as? UITableView
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 注册
|
||||||
|
public static func registerCell(tableView: UITableView, _ reuseIdentifier: String? = nil) {
|
||||||
|
let reuseIdentifier = reuseIdentifier == nil ? NSStringFromClass(self) : reuseIdentifier
|
||||||
|
tableView.register(self, forCellReuseIdentifier: reuseIdentifier!)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 复用取值
|
||||||
|
public static func dequeueReusableCell(tableView: UITableView, indexPath: IndexPath, _ reuseIdentifier: String? = nil) -> Self {
|
||||||
|
|
||||||
|
let reuseIdentifier = reuseIdentifier == nil ? NSStringFromClass(self) : reuseIdentifier
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier!, for: indexPath)
|
||||||
|
return cell as! Self
|
||||||
|
}
|
||||||
|
}
|
201
ShortPlay/Base/WebView/SPWebView.swift
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
//
|
||||||
|
// SPWebView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
@preconcurrency import WebKit
|
||||||
|
|
||||||
|
class SPWebView: WKWebView {
|
||||||
|
|
||||||
|
weak var delegate: SPWebViewDelegate?
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
|
||||||
|
self.removeObserver(self, forKeyPath: "estimatedProgress")
|
||||||
|
self.removeObserver(self, forKeyPath: "title")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
|
||||||
|
super.init(frame: frame, configuration: configuration)
|
||||||
|
// addScriptMessageHandler()
|
||||||
|
_setupInit()
|
||||||
|
}
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
private func _setupInit() {
|
||||||
|
|
||||||
|
self.isOpaque = false
|
||||||
|
self.uiDelegate = self
|
||||||
|
self.navigationDelegate = self
|
||||||
|
self.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
|
||||||
|
self.addObserver(self, forKeyPath: "title", options: .new, context: nil)
|
||||||
|
|
||||||
|
// var userAgent = (self.value(forKey: "userAgent") as? String) ?? ""
|
||||||
|
// if !userAgent.contains(";jxdbBrowser") {
|
||||||
|
// userAgent = userAgent + ";jxdbBrowser|V\(kYDAPPVersion)"
|
||||||
|
// self.customUserAgent = userAgent
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
|
if object as? SPWebView == self {
|
||||||
|
if keyPath == "estimatedProgress", let progress = change?[NSKeyValueChangeKey.newKey] as? CGFloat {
|
||||||
|
self.delegate?.webView?(webView: self, didChangeProgress: progress)
|
||||||
|
} else if keyPath == "title", let title = change?[NSKeyValueChangeKey.newKey] as? String {
|
||||||
|
self.delegate?.webView?(webView: self, didChangeTitle: title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(urlStr: String) {
|
||||||
|
guard let url = URL(string: urlStr) else { return }
|
||||||
|
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
|
||||||
|
self.load(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK:-------------- WKUIDelegate --------------
|
||||||
|
extension SPWebView: WKUIDelegate {
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
|
||||||
|
let alertController = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
|
||||||
|
alertController.addAction(UIAlertAction(title: "确认", style: .default, handler: { (action) in
|
||||||
|
completionHandler()
|
||||||
|
}))
|
||||||
|
self.viewController?.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
|
||||||
|
let alertController = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
|
||||||
|
alertController.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (action) in
|
||||||
|
completionHandler(false)
|
||||||
|
}))
|
||||||
|
alertController.addAction(UIAlertAction(title: "确认", style: .default, handler: { (action) in
|
||||||
|
completionHandler(true)
|
||||||
|
}))
|
||||||
|
self.viewController?.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
|
||||||
|
let alertController = UIAlertController(title: prompt, message: "", preferredStyle: .alert)
|
||||||
|
alertController.addTextField { (textField) in
|
||||||
|
textField.text = defaultText
|
||||||
|
}
|
||||||
|
alertController.addAction(UIAlertAction(title: "完成", style: .default, handler: { (action) in
|
||||||
|
completionHandler(alertController.textFields?.first?.text)
|
||||||
|
}))
|
||||||
|
self.viewController?.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK:-------------- WKNavigationDelegate --------------
|
||||||
|
extension SPWebView: WKNavigationDelegate {
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
|
||||||
|
/*同步cookie*/
|
||||||
|
// let cookies = JXCookiesManager.getAllCookies()
|
||||||
|
// for cookie in cookies {
|
||||||
|
// setCookie(cookie: cookie)
|
||||||
|
// }
|
||||||
|
|
||||||
|
decisionHandler(.allow);
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||||
|
spLog(message: navigationAction.request.url)
|
||||||
|
|
||||||
|
// if navigationAction.request.url?.scheme == "tel" {
|
||||||
|
// UIApplication.shared.openURL(navigationAction.request.url!)
|
||||||
|
// decisionHandler(.cancel)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
if let url = navigationAction.request.url,
|
||||||
|
url.scheme != "http",
|
||||||
|
url.scheme != "https"
|
||||||
|
{
|
||||||
|
UIApplication.shared.open(url)
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//防止抓包
|
||||||
|
// if JXRequestHttpProxy.isIntercept() {
|
||||||
|
// decisionHandler(.cancel)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
if let result = self.delegate?.webView?(self, shouldStartLoadWith: navigationAction) {
|
||||||
|
if result {
|
||||||
|
decisionHandler(.allow)
|
||||||
|
} else {
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decisionHandler(.allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
|
self.delegate?.webViewDidStartLoad?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||||
|
|
||||||
|
// ///禁用长按展示长图
|
||||||
|
// webView.evaluateJavaScript("document.documentElement.style.webkitTouchCallout='none';", completionHandler: nil)
|
||||||
|
// ///禁用长按选择图片文字
|
||||||
|
// webView.evaluateJavaScript("document.documentElement.style.webkitUserSelect='none';", completionHandler: nil)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.delegate?.webViewDidFinishLoad?(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||||
|
self.delegate?.webView?(self, didFailLoadWithError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||||
|
self.delegate?.webView?(self, didFailLoadWithError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK:-------------- WKScriptMessageHandler --------------
|
||||||
|
extension SPWebView: WKScriptMessageHandler {
|
||||||
|
|
||||||
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||||
|
self.delegate?.userContentController?(userContentController, didReceive: message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK:-------------- YDWebViewDelegate --------------
|
||||||
|
@objc protocol SPWebViewDelegate: NSObjectProtocol {
|
||||||
|
|
||||||
|
@objc optional func webView(_ webView: SPWebView, shouldStartLoadWith navigationAction: WKNavigationAction) -> Bool
|
||||||
|
|
||||||
|
@objc optional func webViewDidStartLoad(_ webView: SPWebView)
|
||||||
|
|
||||||
|
@objc optional func webViewDidFinishLoad(_ webView: SPWebView)
|
||||||
|
|
||||||
|
@objc optional func webView(_ webView: SPWebView, didFailLoadWithError error: Error)
|
||||||
|
|
||||||
|
///进度
|
||||||
|
@objc optional func webView(webView: SPWebView, didChangeProgress progress: CGFloat)
|
||||||
|
///标题
|
||||||
|
@objc optional func webView(webView: SPWebView, didChangeTitle title: String)
|
||||||
|
|
||||||
|
///web交互用
|
||||||
|
@objc optional func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
|
||||||
|
|
||||||
|
}
|
89
ShortPlay/Base/WebView/SPWebViewController.swift
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// SPWebViewController.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
class SPWebViewController: SPViewController {
|
||||||
|
|
||||||
|
var urlStr: String?
|
||||||
|
|
||||||
|
private lazy var webView: SPWebView = {
|
||||||
|
let controller = WKUserContentController()
|
||||||
|
|
||||||
|
let config = WKWebViewConfiguration()
|
||||||
|
config.userContentController = controller
|
||||||
|
config.preferences.javaScriptEnabled = true
|
||||||
|
/** 默认是不能通过JS自动打开窗口的,必须通过用户交互才能打开 */
|
||||||
|
config.preferences.javaScriptCanOpenWindowsAutomatically = true
|
||||||
|
let webView = SPWebView(frame: self.view.bounds, configuration: config)
|
||||||
|
webView.delegate = self
|
||||||
|
return webView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
configNavigationBack()
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
|
||||||
|
if let url = urlStr {
|
||||||
|
self.load(urlString: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(false, animated: true)
|
||||||
|
self.setNavigationNormalStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(urlString: String) {
|
||||||
|
// guard let url = URL(string: "https://www.baidu.com") else { return }
|
||||||
|
var str: String = urlString
|
||||||
|
// if let userToken = ETLoginManager.manager.userInfo?.userToken {
|
||||||
|
// if urlString.contains("?") {
|
||||||
|
// str = urlString + "&userToken=\(userToken)"
|
||||||
|
// } else {
|
||||||
|
// str = urlString + "?userToken=\(userToken)"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
guard let url = URL(string: str) else { return }
|
||||||
|
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
|
||||||
|
|
||||||
|
// if ETLoginManager.manager.isLogin, let userToken = ETLoginManager.manager.userInfo?.userToken {
|
||||||
|
// request.setValue(userToken, forHTTPHeaderField: "userToken")
|
||||||
|
// }
|
||||||
|
|
||||||
|
self.webView.load(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPWebViewController {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
self.view.addSubview(webView)
|
||||||
|
self.webView.frame = self.view.bounds
|
||||||
|
self.webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- ETWebViewDelegate --------------
|
||||||
|
extension SPWebViewController: SPWebViewDelegate {
|
||||||
|
|
||||||
|
func webView(webView: SPWebView, didChangeTitle title: String) {
|
||||||
|
self.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
func webView(_ webView: SPWebView, didFailLoadWithError error: any Error) {
|
||||||
|
spLog(message: error)
|
||||||
|
}
|
||||||
|
}
|
24
ShortPlay/Class/Home/Controller/SPHomeChildController.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// SPHomeChildController.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeChildController: SPViewController {
|
||||||
|
|
||||||
|
var topMargins: CGFloat = 10
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setBgImageView() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -9,23 +9,56 @@ import UIKit
|
|||||||
|
|
||||||
class SPHomePageController: SPViewController {
|
class SPHomePageController: SPViewController {
|
||||||
|
|
||||||
|
private var topModel: SPHomeTopModel?
|
||||||
|
|
||||||
|
private lazy var categoryArr: [SPHomeCategoryModel] = {
|
||||||
|
let arr = [
|
||||||
|
SPHomeCategoryModel(category_name: "Hot Picks".localized, category_id: nil, viewController: SPHomeViewController()),
|
||||||
|
SPHomeCategoryModel(category_name: "Top 10".localized, category_id: nil, viewController: nil),
|
||||||
|
SPHomeCategoryModel(category_name: "Fresh Drops".localized, category_id: nil, viewController: nil),
|
||||||
|
SPHomeCategoryModel(category_name: "Free".localized, category_id: nil, viewController: nil),
|
||||||
|
]
|
||||||
|
return arr
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var pageView: JYPageController = {
|
private lazy var pageView: JYPageController = {
|
||||||
|
let customIndicatorImage = UIImage(named: "page_indicator_icon_01")
|
||||||
|
let customIndicator = UIImageView(image: customIndicatorImage)
|
||||||
|
|
||||||
let pageView = JYPageController()
|
let pageView = JYPageController()
|
||||||
pageView.delegate = self
|
pageView.delegate = self
|
||||||
pageView.dataSource = self
|
pageView.dataSource = self
|
||||||
|
pageView.config.normalTitleColor = .colorD2D2D2()
|
||||||
|
pageView.config.selectedTitleColor = .colorBF6BFF()
|
||||||
|
pageView.config.normalTitleFont = 14
|
||||||
|
pageView.config.selectedTitleFont = 16
|
||||||
|
pageView.config.normalTitleFontWeight = .regular
|
||||||
|
pageView.config.selectedTitleFontWeight = .medium
|
||||||
|
pageView.config.alignment = .scatter
|
||||||
|
pageView.config.customIndicator = customIndicator
|
||||||
|
pageView.config.indicatorStyle = .customView
|
||||||
|
pageView.config.indicatorWidth = customIndicatorImage?.size.width ?? 0
|
||||||
|
pageView.config.indicatorHeight = customIndicatorImage?.size.height ?? 0
|
||||||
|
pageView.config.leftPadding = 15
|
||||||
|
pageView.config.rightPadding = 15
|
||||||
|
pageView.config.itemsMargin = 24
|
||||||
return pageView
|
return pageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
sp_setupUI()
|
sp_setupUI()
|
||||||
|
|
||||||
|
requestData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
self.navigationController?.setNavigationBarHidden(false, animated: true)
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -37,7 +70,9 @@ extension SPHomePageController {
|
|||||||
view.addSubview(pageView.view)
|
view.addSubview(pageView.view)
|
||||||
|
|
||||||
pageView.view.snp.makeConstraints { make in
|
pageView.view.snp.makeConstraints { make in
|
||||||
make.edges.equalToSuperview()
|
// make.edges.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(kSPStatusbarHeight + 66)
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,26 +80,47 @@ extension SPHomePageController {
|
|||||||
//MARK: -------------- JYPageControllerDelegate & JYPageControllerDataSource --------------
|
//MARK: -------------- JYPageControllerDelegate & JYPageControllerDataSource --------------
|
||||||
extension SPHomePageController: JYPageControllerDelegate, JYPageControllerDataSource {
|
extension SPHomePageController: JYPageControllerDelegate, JYPageControllerDataSource {
|
||||||
func pageController(_ pageController: JYPageController, frameForSegmentedView segmentedView: JYSegmentedView) -> CGRect {
|
func pageController(_ pageController: JYPageController, frameForSegmentedView segmentedView: JYSegmentedView) -> CGRect {
|
||||||
return .init(x: 0, y: kSPStatusbarHeight + 10, width: kSPScreenWidth, height: 40)
|
return .init(x: 0, y: 0, width: kSPScreenWidth, height: 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pageController(_ pageController: JYPageController, frameForContainerView container: UIScrollView) -> CGRect {
|
func pageController(_ pageController: JYPageController, frameForContainerView container: UIScrollView) -> CGRect {
|
||||||
return .init(x: 0, y: 0, width: kSPScreenWidth, height: kSPScreenHeight - kSPTabBarHeight)
|
return .init(x: 0, y: 40, width: kSPScreenWidth, height: kSPScreenHeight - kSPTabBarHeight - kSPStatusbarHeight - 66 - 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pageController(_ pageController: JYPageController, titleAt index: Int) -> String {
|
func pageController(_ pageController: JYPageController, titleAt index: Int) -> String {
|
||||||
return "123"
|
return self.categoryArr[index].category_name ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func childController(atIndex index: Int) -> any JYPageChildContollerProtocol {
|
func childController(atIndex index: Int) -> any JYPageChildContollerProtocol {
|
||||||
return SPViewController()
|
let vc = SPHomeViewController()
|
||||||
|
vc.topMargins = 15
|
||||||
|
return vc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func numberOfChildControllers() -> Int {
|
func numberOfChildControllers() -> Int {
|
||||||
return 0
|
return self.categoryArr.count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension SPHomePageController {
|
||||||
|
|
||||||
|
private func requestData() {
|
||||||
|
if self.topModel != nil { return }
|
||||||
|
|
||||||
|
|
||||||
|
SPHomeAPI.requestHomeTopData { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if let model = model {
|
||||||
|
self.topModel = model
|
||||||
|
if let category = self.topModel?.category {
|
||||||
|
self.categoryArr += category
|
||||||
|
}
|
||||||
|
self.pageView.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,88 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class SPHomeViewController: SPViewController {
|
class SPHomeViewController: SPHomeChildController {
|
||||||
|
|
||||||
|
///模版数据
|
||||||
|
private var moduleModel: SPHomeModuleModel?
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var layout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.register(SPHomeHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
|
||||||
|
SPCollectionViewCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
// view.backgroundColor = .clear
|
||||||
|
requestModuleData()
|
||||||
|
_setupUI()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func fetchChildControllerScrollView() -> UIScrollView? {
|
||||||
|
return self.collectionView
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeViewController {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
view.addSubview(self.collectionView);
|
||||||
|
|
||||||
|
self.collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(topMargins)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
|
extension SPHomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||||
|
if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerView", for: indexPath) as? SPHomeHeaderView {
|
||||||
|
headerView.moduleModel = self.moduleModel
|
||||||
|
return headerView
|
||||||
|
}
|
||||||
|
return UICollectionReusableView()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension SPHomeViewController {
|
||||||
|
|
||||||
|
private func requestModuleData() {
|
||||||
|
SPHomeAPI.requestHomeModuleData { [weak self] model in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if let model = model {
|
||||||
|
self.moduleModel = model
|
||||||
|
self.layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: SPHomeHeaderView.contentHeight(model: model))
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,33 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
class SPHomeCategoryModel: SPModel {
|
//class SPHomeCategoryModel: SPModel, SmartCodable {
|
||||||
|
//
|
||||||
|
// var category_name: String?
|
||||||
|
// var category_id: String?
|
||||||
|
//
|
||||||
|
// @IgnoredKey
|
||||||
|
// var viewController: SPViewController?
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// init(category_name: String? = nil, category_id: String? = nil, viewController: SPViewController? = nil) {
|
||||||
|
// self.category_name = category_name
|
||||||
|
// self.category_id = category_id
|
||||||
|
// self.viewController = viewController
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// required init() {
|
||||||
|
// fatalError("init() has not been implemented")
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
struct SPHomeCategoryModel: SmartCodable {
|
||||||
|
|
||||||
var category_name: String?
|
var category_name: String?
|
||||||
var category_id: String?
|
var category_id: String?
|
||||||
|
|
||||||
|
@IgnoredKey
|
||||||
|
var viewController: SPViewController?
|
||||||
}
|
}
|
||||||
|
23
ShortPlay/Class/Home/Model/SPHomeModuleModel.swift
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// SPHomeModuleModel.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class SPHomeModuleModel: SPModel, SmartCodable {
|
||||||
|
|
||||||
|
var bannerData: [SPShortModel]?
|
||||||
|
///首页九宫格数据
|
||||||
|
var recommandData: [SPShortModel]?
|
||||||
|
///新剧列表
|
||||||
|
var manualNewestRecommand: [SPShortModel]?
|
||||||
|
///热门排行
|
||||||
|
var hotData: [SPShortModel]?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
15
ShortPlay/Class/Home/Model/SPHomeTopModel.swift
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// SPHomeTopModel.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class SPHomeTopModel: SPModel, SmartCodable {
|
||||||
|
|
||||||
|
var category: [SPHomeCategoryModel]?
|
||||||
|
|
||||||
|
}
|
78
ShortPlay/Class/Home/View/SPHomeBannerCell.swift
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// SPHomeBannerCell.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeBannerCell: ZKCycleScrollViewCell {
|
||||||
|
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.horizontally_img)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bottomView: SPGradientView = {
|
||||||
|
let view = SPGradientView()
|
||||||
|
view.colors = [UIColor.color121418(alpha: 0).cgColor, UIColor.color121418(alpha: 0.8).cgColor]
|
||||||
|
view.startPoint = CGPoint(x: 0.5, y: 0)
|
||||||
|
view.endPoint = CGPoint(x: 0.5, y: 1)
|
||||||
|
view.locations = [0, 1]
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontLight(ofSize: 14)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.9)
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
contentView.layer.cornerRadius = 12
|
||||||
|
contentView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeBannerCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(bottomView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(65)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(7)
|
||||||
|
make.bottom.equalToSuperview().offset(-12)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
87
ShortPlay/Class/Home/View/SPHomeDataItemView.swift
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// SPHomeDataItemView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeDataItemView: UIView {
|
||||||
|
|
||||||
|
///内容距离顶部位置
|
||||||
|
static let contentToTop: CGFloat = 36
|
||||||
|
|
||||||
|
|
||||||
|
class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
return contentToTop
|
||||||
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
let height = Self.contentHeight(dataArr: dataArr ?? [])
|
||||||
|
return CGSize(width: kSPScreenWidth, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private(set) lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 15)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var moreButton: UIButton = {
|
||||||
|
let button = UIButton(type: .custom)
|
||||||
|
button.setTitle("More", for: .normal)
|
||||||
|
button.setTitleColor(.colorF564B6(), for: .normal)
|
||||||
|
button.titleLabel?.font = .fontLight(ofSize: 12)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
private(set) lazy var contentView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeDataItemView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(moreButton)
|
||||||
|
addSubview(contentView)
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
make.height.equalTo(21)
|
||||||
|
}
|
||||||
|
|
||||||
|
moreButton.snp.makeConstraints { make in
|
||||||
|
make.centerY.equalTo(moreButton)
|
||||||
|
make.right.equalToSuperview().offset(-15)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.top.equalTo(Self.contentToTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
ShortPlay/Class/Home/View/SPHomeHeaderView.swift
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
//
|
||||||
|
// SPHomeHeaderView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeHeaderView: UICollectionReusableView {
|
||||||
|
|
||||||
|
var moduleModel: SPHomeModuleModel? {
|
||||||
|
didSet {
|
||||||
|
stackView.removeAllArrangedSubview()
|
||||||
|
|
||||||
|
stackView.addArrangedSubview(bannerView)
|
||||||
|
bannerView.reloadData()
|
||||||
|
|
||||||
|
// if (moduleModel?.recommandData?.count ?? 0) > 0 {
|
||||||
|
// }
|
||||||
|
stackView.addArrangedSubview(trendingView)
|
||||||
|
trendingView.dataArr = moduleModel?.bannerData
|
||||||
|
|
||||||
|
stackView.addArrangedSubview(hotView)
|
||||||
|
hotView.dataArr = moduleModel?.bannerData
|
||||||
|
|
||||||
|
stackView.addArrangedSubview(shortsForYouView)
|
||||||
|
shortsForYouView.dataArr = moduleModel?.bannerData
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var stackView: UIStackView = {
|
||||||
|
let view = UIStackView()
|
||||||
|
view.axis = .vertical
|
||||||
|
view.spacing = 25
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bannerView: ZKCycleScrollView = {
|
||||||
|
let bannerView = ZKCycleScrollView(frame: .zero, shouldInfiniteLoop: true);
|
||||||
|
bannerView.delegate = self
|
||||||
|
bannerView.dataSource = self
|
||||||
|
bannerView.itemSpacing = 10
|
||||||
|
bannerView.itemSize = CGSize(width: kSPScreenWidth - 30, height: Self.bannerHeight())
|
||||||
|
bannerView.register(SPHomeBannerCell.self, forCellWithReuseIdentifier: "bannerCell")
|
||||||
|
bannerView.hidesPageControl = true
|
||||||
|
bannerView.snp.makeConstraints { make in
|
||||||
|
make.width.equalTo(kSPScreenWidth)
|
||||||
|
make.height.equalTo(Self.bannerHeight())
|
||||||
|
}
|
||||||
|
return bannerView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var trendingView: SPHomeTrendingView = {
|
||||||
|
let view = SPHomeTrendingView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotView: SPHomeHotView = {
|
||||||
|
let view = SPHomeHotView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var shortsForYouView: SPHomeShortsForYouView = {
|
||||||
|
let view = SPHomeShortsForYouView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeHeaderView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
addSubview(stackView)
|
||||||
|
|
||||||
|
stackView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- ZKCycleScrollViewDelegate & ZKCycleScrollViewDataSource --------------
|
||||||
|
extension SPHomeHeaderView: ZKCycleScrollViewDelegate, ZKCycleScrollViewDataSource {
|
||||||
|
|
||||||
|
func numberOfItems(in cycleScrollView: ZKCycleScrollView) -> Int {
|
||||||
|
return moduleModel?.bannerData?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, cellForItemAt index: Int) -> ZKCycleScrollViewCell {
|
||||||
|
let cell = cycleScrollView.dequeueReusableCell(withReuseIdentifier: "bannerCell", for: index) as! SPHomeBannerCell
|
||||||
|
cell.model = moduleModel?.bannerData?[index]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, didSelectItemAt index: Int) {
|
||||||
|
let model = moduleModel?.bannerData?[index]
|
||||||
|
|
||||||
|
let vc = SPTVPlayerListViewController()
|
||||||
|
vc.shortPlayId = model?.short_play_id
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension SPHomeHeaderView {
|
||||||
|
|
||||||
|
static func contentHeight(model: SPHomeModuleModel) -> CGFloat {
|
||||||
|
var height = bannerHeight()
|
||||||
|
|
||||||
|
// if (model.recommandData?.count ?? 0) > 0 {
|
||||||
|
// }
|
||||||
|
height = height + SPHomeTrendingView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
||||||
|
|
||||||
|
|
||||||
|
height = height + SPHomeHotView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
||||||
|
|
||||||
|
height = height + SPHomeShortsForYouView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
static func bannerHeight() -> CGFloat {
|
||||||
|
return 183
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
96
ShortPlay/Class/Home/View/SPHomeHotCell.swift
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// SPHomeHotCell.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeHotCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
desLabel.text = model?.sp_description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var bgView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .colorFFFFFF(alpha: 0.1)
|
||||||
|
view.layer.cornerRadius = 12
|
||||||
|
view.layer.masksToBounds = true
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
imageView.layer.cornerRadius = 6
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontLight(ofSize: 14)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.9)
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var desLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontLight(ofSize: 14)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.28)
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeHotCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(bgView)
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
contentView.addSubview(desLabel)
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(116)
|
||||||
|
}
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
make.width.equalTo(94)
|
||||||
|
make.height.equalTo(124)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(coverImageView.snp.right).offset(13)
|
||||||
|
make.top.equalTo(bgView).offset(10)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
|
||||||
|
desLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(titleLabel)
|
||||||
|
make.bottom.equalTo(coverImageView)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
ShortPlay/Class/Home/View/SPHomeHotView.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// SPHomeHotView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeHotView: SPHomeDataItemView {
|
||||||
|
|
||||||
|
|
||||||
|
override class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
if dataArr.count == 1 {
|
||||||
|
return 139 + contentToTop
|
||||||
|
} else {
|
||||||
|
return 139 * 2 + 15 + contentToTop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .horizontal
|
||||||
|
layout.itemSize = CGSize(width: kSPScreenWidth - 15 - 20, height: 139)
|
||||||
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
|
layout.minimumLineSpacing = 10
|
||||||
|
layout.minimumInteritemSpacing = 15
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.showsVerticalScrollIndicator = false
|
||||||
|
collectionView.showsHorizontalScrollIndicator = false
|
||||||
|
SPHomeHotCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
self.titleLabel.text = "Editor's Hotlist".localized
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeHotView {
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(collectionView)
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
|
extension SPHomeHotView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPHomeHotCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.model = dataArr?[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = self.dataArr?[indexPath.row]
|
||||||
|
|
||||||
|
let vc = SPTVPlayerListViewController()
|
||||||
|
vc.shortPlayId = model?.short_play_id
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
80
ShortPlay/Class/Home/View/SPHomeShortsForYouCell.swift
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// SPHomeShortsForYouCell.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeShortsForYouCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.horizontally_img)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bottomView: SPGradientView = {
|
||||||
|
let view = SPGradientView()
|
||||||
|
view.colors = [UIColor.color121418(alpha: 0).cgColor, UIColor.color121418(alpha: 0.8).cgColor]
|
||||||
|
view.startPoint = CGPoint(x: 0.5, y: 0)
|
||||||
|
view.endPoint = CGPoint(x: 0.5, y: 1)
|
||||||
|
view.locations = [0, 1]
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontLight(ofSize: 14)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.9)
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
contentView.layer.cornerRadius = 12
|
||||||
|
contentView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeShortsForYouCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(bottomView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(65)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(7)
|
||||||
|
make.bottom.equalToSuperview().offset(-12)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-7)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
ShortPlay/Class/Home/View/SPHomeShortsForYouView.swift
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// SPHomeShortsForYouView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeShortsForYouView: SPHomeDataItemView {
|
||||||
|
|
||||||
|
|
||||||
|
override class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
return 271 + contentToTop
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .horizontal
|
||||||
|
layout.itemSize = CGSize(width: 204, height: 271)
|
||||||
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
|
layout.minimumLineSpacing = 15
|
||||||
|
layout.minimumInteritemSpacing = 15
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.showsVerticalScrollIndicator = false
|
||||||
|
collectionView.showsHorizontalScrollIndicator = false
|
||||||
|
SPHomeShortsForYouCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
self.titleLabel.text = "Shorts for You".localized
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeShortsForYouView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(collectionView)
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
|
extension SPHomeShortsForYouView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPHomeShortsForYouCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
ShortPlay/Class/Home/View/SPHomeTrendingCell.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// SPHomeTrendingCell.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeTrendingCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
imageView.layer.cornerRadius = 10
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontLight(ofSize: 13)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.9)
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotBgView: SPGradientView = {
|
||||||
|
let view = SPGradientView()
|
||||||
|
view.colors = [UIColor.colorF56490().cgColor, UIColor.colorBF6BFF().cgColor]
|
||||||
|
view.startPoint = .init(x: 0.5, y: 0)
|
||||||
|
view.endPoint = .init(x: 0.5, y: 1)
|
||||||
|
view.locations = [0, 1]
|
||||||
|
view.addRadius(topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 6)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotIconImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "hot_icon_01"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeTrendingCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
coverImageView.addSubview(hotBgView)
|
||||||
|
hotBgView.addSubview(hotIconImageView)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.left.right.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview().offset(-38)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview()
|
||||||
|
make.right.lessThanOrEqualToSuperview()
|
||||||
|
make.top.equalTo(coverImageView.snp.bottom).offset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
hotBgView.snp.makeConstraints { make in
|
||||||
|
make.left.top.equalToSuperview()
|
||||||
|
make.width.equalTo(22)
|
||||||
|
make.height.equalTo(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
hotIconImageView.snp.makeConstraints { make in
|
||||||
|
make.center.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
108
ShortPlay/Class/Home/View/SPHomeTrendingView.swift
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
//
|
||||||
|
// SPHomeTrendingView.swift
|
||||||
|
// ShortPlay
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2025/4/14.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeTrendingView: SPHomeDataItemView {
|
||||||
|
|
||||||
|
private static func itemSize() -> CGSize {
|
||||||
|
let width = floor((kSPScreenWidth - 15 * 2 - 12 * 2) / 3)
|
||||||
|
let imageScale: CGFloat = 153 / 107
|
||||||
|
let height = width * imageScale + 38
|
||||||
|
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
|
||||||
|
var height = self.contentToTop
|
||||||
|
|
||||||
|
var lineCount = dataArr.count / 3
|
||||||
|
if dataArr.count % 3 > 0 {
|
||||||
|
lineCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentHeight = itemSize().height * CGFloat(lineCount) + 12 * CGFloat(lineCount - 1)
|
||||||
|
if contentHeight > 0 {
|
||||||
|
height += contentHeight
|
||||||
|
} else {
|
||||||
|
height += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.minimumLineSpacing = 12
|
||||||
|
layout.minimumInteritemSpacing = 12
|
||||||
|
layout.itemSize = Self.itemSize()
|
||||||
|
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.isScrollEnabled = false
|
||||||
|
SPHomeTrendingCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.titleLabel.text = "Trending Now".localized
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeTrendingView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(collectionView)
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDataSource & UICollectionViewDelegate --------------
|
||||||
|
extension SPHomeTrendingView: UICollectionViewDataSource, UICollectionViewDelegate {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPHomeTrendingCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.model = dataArr?[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = self.dataArr?[indexPath.row]
|
||||||
|
|
||||||
|
let vc = SPTVPlayerListViewController()
|
||||||
|
vc.shortPlayId = model?.short_play_id
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ import UIKit
|
|||||||
|
|
||||||
class SPMineViewController: SPViewController {
|
class SPMineViewController: SPViewController {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -160,7 +160,10 @@ extension SPPlayerListViewController {
|
|||||||
view.addSubview(collectionView)
|
view.addSubview(collectionView)
|
||||||
|
|
||||||
collectionView.snp.makeConstraints { make in
|
collectionView.snp.makeConstraints { make in
|
||||||
make.edges.equalToSuperview()
|
// make.edges.equalToSuperview()
|
||||||
|
make.center.equalToSuperview()
|
||||||
|
make.width.equalTo(self.contentSize.width)
|
||||||
|
make.height.equalTo(self.contentSize.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ extension SPTVPlayerListViewController: SPPlayerListViewControllerDataSource {
|
|||||||
extension SPTVPlayerListViewController {
|
extension SPTVPlayerListViewController {
|
||||||
|
|
||||||
private func requestDetailData() {
|
private func requestDetailData() {
|
||||||
guard let videoId = self.videoId, let shortPlayId = self.shortPlayId else { return }
|
guard let shortPlayId = self.shortPlayId else { return }
|
||||||
|
|
||||||
SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId) { [weak self] model in
|
SPVideoAPI.requestVideoDetail(videoId: videoId, shortPlayId: shortPlayId) { [weak self] model in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
@ -10,13 +10,13 @@ import SmartCodable
|
|||||||
|
|
||||||
class SPShortModel: SPModel, SmartCodable {
|
class SPShortModel: SPModel, SmartCodable {
|
||||||
|
|
||||||
|
var id: String?
|
||||||
var all_coins: String?
|
var all_coins: String?
|
||||||
var buy_type: String?
|
var buy_type: String?
|
||||||
var collect_total: String?
|
var collect_total: String?
|
||||||
var sp_description: String?
|
var sp_description: String?
|
||||||
var episode_total: Int?
|
var episode_total: Int?
|
||||||
var horizontally_img: String?
|
var horizontally_img: String?
|
||||||
var id: String?
|
|
||||||
var image_url: String?
|
var image_url: String?
|
||||||
var is_collect: Bool?
|
var is_collect: Bool?
|
||||||
var name: String?
|
var name: String?
|
||||||
|
@ -123,13 +123,13 @@ extension SPPlayerControlView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func hadlePlayAndOrPaused() {
|
@objc private func hadlePlayAndOrPaused() {
|
||||||
// self.viewModel?.handlePauseOrPlay?()
|
self.viewModel?.handlePauseOrPlay?()
|
||||||
|
|
||||||
guard let model = model as? SPShortModel else { return }
|
// guard let model = model as? SPShortModel else { return }
|
||||||
let vc = SPTVPlayerListViewController()
|
// let vc = SPTVPlayerListViewController()
|
||||||
vc.shortPlayId = model.short_play_id
|
// vc.shortPlayId = model.short_play_id
|
||||||
vc.videoId = model.video_info?.short_play_video_id
|
// vc.videoId = model.video_info?.short_play_video_id
|
||||||
SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
// SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ extension SPPlayer {
|
|||||||
player.playerDidToEnd = { [weak self] (asset) in
|
player.playerDidToEnd = { [weak self] (asset) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if isLoop {
|
if isLoop {
|
||||||
self.player.replay()
|
self.replay()
|
||||||
} else {
|
} else {
|
||||||
self.isPlaying = false
|
self.isPlaying = false
|
||||||
self.delegate?.sp_playCompletion?(self)
|
self.delegate?.sp_playCompletion?(self)
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "home@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "home@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 957 B |
Before Width: | Height: | Size: 1.8 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_01.imageset/home@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.0 KiB |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "home@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "home@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 1.0 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_01_selected.imageset/home@3x.png
vendored
Normal file
After Width: | Height: | Size: 4.4 KiB |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "Explore@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "Explore@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Explore@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_02.imageset/Explore@3x.png
vendored
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.3 KiB |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "Explore@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "Explore@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Explore@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_02_selected.imageset/Explore@3x.png
vendored
Normal file
After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "Me@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "Me@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.3 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_05.imageset/Me@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_05.imageset/Me@3x.png
vendored
Normal file
After Width: | Height: | Size: 3.0 KiB |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@2x.png",
|
"filename" : "Me@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "Frame@3x.png",
|
"filename" : "Me@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 799 B |
Before Width: | Height: | Size: 1.4 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_05_selected.imageset/Me@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_05_selected.imageset/Me@3x.png
vendored
Normal file
After Width: | Height: | Size: 4.1 KiB |
6
ShortPlay/Source/Assets.xcassets/icon/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
22
ShortPlay/Source/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "hot@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "hot@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/hot_icon_01.imageset/hot@2x.png
vendored
Normal file
After Width: | Height: | Size: 533 B |
BIN
ShortPlay/Source/Assets.xcassets/icon/hot_icon_01.imageset/hot@3x.png
vendored
Normal file
After Width: | Height: | Size: 931 B |
22
ShortPlay/Source/Assets.xcassets/icon/page_indicator_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "路径 497@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "路径 497@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/icon/page_indicator_icon_01.imageset/路径 497@2x.png
vendored
Normal file
After Width: | Height: | Size: 843 B |
BIN
ShortPlay/Source/Assets.xcassets/icon/page_indicator_icon_01.imageset/路径 497@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
6
ShortPlay/Source/Assets.xcassets/image/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
22
ShortPlay/Source/Assets.xcassets/image/main_bg_image_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "bg@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "bg@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ShortPlay/Source/Assets.xcassets/image/main_bg_image_01.imageset/bg@2x.png
vendored
Normal file
After Width: | Height: | Size: 268 KiB |
BIN
ShortPlay/Source/Assets.xcassets/image/main_bg_image_01.imageset/bg@3x.png
vendored
Normal file
After Width: | Height: | Size: 416 KiB |
@ -10,3 +10,10 @@
|
|||||||
"For You" = "For You";
|
"For You" = "For You";
|
||||||
"Error" = "Error";
|
"Error" = "Error";
|
||||||
"Profile" = "Profile";
|
"Profile" = "Profile";
|
||||||
|
"Hot Picks" = "Hot Picks";
|
||||||
|
"Top 10" = "Top 10";
|
||||||
|
"Fresh Drops" = "Fresh Drops";
|
||||||
|
"Free" = "Free";
|
||||||
|
"Trending Now" = "Trending Now";
|
||||||
|
"Editor's Hotlist" = "Editor's Hotlist";
|
||||||
|
"Shorts for You" = "Shorts for You";
|
||||||
|
@ -14,6 +14,8 @@ import UIKit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc public enum JYSegmentedViewAlignment: Int {
|
@objc public enum JYSegmentedViewAlignment: Int {
|
||||||
|
///平铺
|
||||||
|
case scatter
|
||||||
case left
|
case left
|
||||||
case right
|
case right
|
||||||
case center
|
case center
|
||||||
|
@ -541,8 +541,8 @@ extension JYPageController: UITableViewDelegate, UITableViewDataSource {
|
|||||||
cell.backgroundColor = .clear
|
cell.backgroundColor = .clear
|
||||||
cell.selectionStyle = .none
|
cell.selectionStyle = .none
|
||||||
// pageContentScrollView.frame = CGRect(x: 0, y: 0, width: childControllerViewFrame.width, height: childControllerViewFrame.height)
|
// pageContentScrollView.frame = CGRect(x: 0, y: 0, width: childControllerViewFrame.width, height: childControllerViewFrame.height)
|
||||||
cell.contentView.addSubview(segmentedView)
|
|
||||||
cell.contentView.addSubview(pageContentScrollView)
|
cell.contentView.addSubview(pageContentScrollView)
|
||||||
|
cell.contentView.addSubview(segmentedView)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,12 +523,17 @@ public class JYSegmentedView: UIView {
|
|||||||
private func updateItemsFrame() {
|
private func updateItemsFrame() {
|
||||||
let width = calculateTotalWidth()
|
let width = calculateTotalWidth()
|
||||||
var startX: CGFloat = config.leftPadding
|
var startX: CGFloat = config.leftPadding
|
||||||
if config.alignment == .center, width < frame.width {
|
var itemsMargin = config.itemsMargin
|
||||||
startX = (frame.width - width)/2 + config.leftPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.alignment == .right, width < frame.width {
|
if config.alignment == .center, width < frame.width {
|
||||||
|
startX = (frame.width - width)/2
|
||||||
|
|
||||||
|
} else if config.alignment == .right, width < frame.width {
|
||||||
startX = frame.width - width
|
startX = frame.width - width
|
||||||
|
|
||||||
|
} else if config.alignment == .scatter, width < frame.width {
|
||||||
|
let itemTotalWidth = itemTotalWidth()
|
||||||
|
itemsMargin = (self.frame.size.width - itemTotalWidth - config.leftPadding - config.rightPadding) / CGFloat(items.count - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalWidth: CGFloat = 0
|
var totalWidth: CGFloat = 0
|
||||||
@ -538,9 +543,9 @@ public class JYSegmentedView: UIView {
|
|||||||
item.badgeView?.frame = CGRect(x: item.frame.width + config.badgeViewOffset.x, y: item.frame.origin.y - item.badgeViewHeight/2 + config.badgeViewOffset.y, width: item.badgeViewWidth, height: item.badgeViewHeight)
|
item.badgeView?.frame = CGRect(x: item.frame.width + config.badgeViewOffset.x, y: item.frame.origin.y - item.badgeViewHeight/2 + config.badgeViewOffset.y, width: item.badgeViewWidth, height: item.badgeViewHeight)
|
||||||
totalWidth = startX + item.frame.width
|
totalWidth = startX + item.frame.width
|
||||||
}else{
|
}else{
|
||||||
item.frame = CGRect(x: totalWidth + config.itemsMargin , y: item.frame.origin.y, width: item.frame.width, height: item.frame.height)
|
item.frame = CGRect(x: totalWidth + itemsMargin , y: item.frame.origin.y, width: item.frame.width, height: item.frame.height)
|
||||||
item.badgeView?.frame = CGRect(x: item.frame.width + item.frame.origin.x + config.badgeViewOffset.x, y: item.frame.origin.y - item.badgeViewHeight/2 + config.badgeViewOffset.y, width: item.badgeViewWidth, height: item.badgeViewHeight)
|
item.badgeView?.frame = CGRect(x: item.frame.width + item.frame.origin.x + config.badgeViewOffset.x, y: item.frame.origin.y - item.badgeViewHeight/2 + config.badgeViewOffset.y, width: item.badgeViewWidth, height: item.badgeViewHeight)
|
||||||
totalWidth = totalWidth + item.frame.width + config.itemsMargin
|
totalWidth = totalWidth + item.frame.width + itemsMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
let bgView = self.itemBackgroundViews[index]
|
let bgView = self.itemBackgroundViews[index]
|
||||||
@ -556,6 +561,14 @@ public class JYSegmentedView: UIView {
|
|||||||
contentView.contentSize = CGSize(width: totalWidth + config.rightPadding, height: frame.size.height)
|
contentView.contentSize = CGSize(width: totalWidth + config.rightPadding, height: frame.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func itemTotalWidth() -> CGFloat {
|
||||||
|
var totalWidth: CGFloat = 0
|
||||||
|
for (index,item) in items.enumerated() {
|
||||||
|
totalWidth = totalWidth + item.frame.width
|
||||||
|
}
|
||||||
|
return totalWidth
|
||||||
|
}
|
||||||
|
|
||||||
private func calculateTotalWidth() -> CGFloat {
|
private func calculateTotalWidth() -> CGFloat {
|
||||||
var totalWidth: CGFloat = 0
|
var totalWidth: CGFloat = 0
|
||||||
for (index,item) in items.enumerated() {
|
for (index,item) in items.enumerated() {
|
||||||
|