首页功能开发
1
Podfile
@ -24,6 +24,7 @@ target 'ShortPlay' do
|
||||
pod 'Toast' #吐司提示
|
||||
pod 'ZFPlayer/AVPlayer' #播放器
|
||||
pod 'KTVHTTPCache' #视频缓存
|
||||
pod 'HWPanModal' #底部弹出控制器
|
||||
|
||||
|
||||
target 'ShortPlayTests' do
|
||||
|
@ -1,6 +1,7 @@
|
||||
PODS:
|
||||
- Alamofire (5.10.2)
|
||||
- CocoaAsyncSocket (7.6.5)
|
||||
- HWPanModal (0.9.9)
|
||||
- KTVHTTPCache (3.0.2):
|
||||
- CocoaAsyncSocket
|
||||
- MJRefresh (3.7.9)
|
||||
@ -19,6 +20,7 @@ PODS:
|
||||
- ZFPlayer/Core (4.1.4)
|
||||
|
||||
DEPENDENCIES:
|
||||
- HWPanModal
|
||||
- KTVHTTPCache
|
||||
- MJRefresh
|
||||
- Moya
|
||||
@ -32,6 +34,7 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- Alamofire
|
||||
- CocoaAsyncSocket
|
||||
- HWPanModal
|
||||
- KTVHTTPCache
|
||||
- MJRefresh
|
||||
- Moya
|
||||
@ -44,6 +47,7 @@ SPEC REPOS:
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2
|
||||
KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86
|
||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
||||
@ -53,6 +57,6 @@ SPEC CHECKSUMS:
|
||||
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
||||
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
||||
|
||||
PODFILE CHECKSUM: 422706ab4a12e286d2106acff478496d948912e8
|
||||
PODFILE CHECKSUM: 6b8807090aa7efc034949fa4dae871806b3d46fd
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
@ -444,6 +444,7 @@
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ZyreoTV;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||
@ -478,6 +479,7 @@
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShortPlay/Source/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ZyreoTV;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UIMainStoryboardFile = "";
|
||||
|
@ -10,7 +10,7 @@ import UIKit
|
||||
extension AppDelegate {
|
||||
|
||||
func appConfig() {
|
||||
// UIView.et_Awake()
|
||||
UIView.sp_Awake()
|
||||
tabBarConfig()
|
||||
// keyBoardStyle()
|
||||
|
||||
|
@ -22,12 +22,19 @@ class SPViewController: UIViewController, JYPageChildContollerProtocol {
|
||||
|
||||
private(set) var isViewDidLoad = 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() {
|
||||
super.viewDidLoad()
|
||||
self.isViewDidLoad = true
|
||||
self.edgesForExtendedLayout = []
|
||||
self.view.backgroundColor = .black
|
||||
|
||||
setBgImageView()
|
||||
|
||||
if let navi = navigationController {
|
||||
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) {
|
||||
super.viewDidAppear(animated)
|
||||
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 ?? "")")
|
||||
#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 {
|
||||
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 {
|
||||
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
|
||||
|
||||
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 {
|
||||
|
||||
///获取视频详情
|
||||
static func requestVideoDetail(videoId: String, shortPlayId: String, completer: ((_ model: SPVideoDetailModel?) -> Void)?) {
|
||||
|
||||
var param = SPNetworkParameters(path: "/getVideoDetails")
|
||||
param.method = .get
|
||||
param.parameters = [
|
||||
"video_id" : videoId,
|
||||
static func requestVideoDetail(videoId: String?, shortPlayId: String, completer: ((_ model: SPVideoDetailModel?) -> Void)?) {
|
||||
var parameters: [String : Any] = [
|
||||
"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
|
||||
completer?(response.data)
|
||||
}
|
||||
|
@ -9,10 +9,26 @@ import UIKit
|
||||
|
||||
#if DEBUG
|
||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
||||
let SPWebBaseURL = "https://test1-api.guyantv.com"
|
||||
let SPWebBaseURL = "https://www.guyantv.com"
|
||||
#else
|
||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
||||
let SPWebBaseURL = "https://test1-api.guyantv.com"
|
||||
let SPWebBaseURL = "https://www.guyantv.com"
|
||||
#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 {
|
||||
|
||||
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 = {
|
||||
let customIndicatorImage = UIImage(named: "page_indicator_icon_01")
|
||||
let customIndicator = UIImageView(image: customIndicatorImage)
|
||||
|
||||
let pageView = JYPageController()
|
||||
pageView.delegate = 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
|
||||
}()
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
sp_setupUI()
|
||||
|
||||
requestData()
|
||||
}
|
||||
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
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)
|
||||
|
||||
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 --------------
|
||||
extension SPHomePageController: JYPageControllerDelegate, JYPageControllerDataSource {
|
||||
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 {
|
||||
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 {
|
||||
return "123"
|
||||
return self.categoryArr[index].category_name ?? ""
|
||||
}
|
||||
|
||||
func childController(atIndex index: Int) -> any JYPageChildContollerProtocol {
|
||||
return SPViewController()
|
||||
let vc = SPHomeViewController()
|
||||
vc.topMargins = 15
|
||||
return vc
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
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() {
|
||||
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 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_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)
|
||||
}
|
||||
}
|
@ -8,10 +8,12 @@
|
||||
import UIKit
|
||||
|
||||
class SPMineViewController: SPViewController {
|
||||
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,10 @@ extension SPPlayerListViewController {
|
||||
view.addSubview(collectionView)
|
||||
|
||||
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 {
|
||||
|
||||
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
|
||||
guard let self = self else { return }
|
||||
|
@ -10,13 +10,13 @@ import SmartCodable
|
||||
|
||||
class SPShortModel: SPModel, SmartCodable {
|
||||
|
||||
var id: String?
|
||||
var all_coins: String?
|
||||
var buy_type: String?
|
||||
var collect_total: String?
|
||||
var sp_description: String?
|
||||
var episode_total: Int?
|
||||
var horizontally_img: String?
|
||||
var id: String?
|
||||
var image_url: String?
|
||||
var is_collect: Bool?
|
||||
var name: String?
|
||||
|
@ -123,13 +123,13 @@ extension SPPlayerControlView {
|
||||
}
|
||||
|
||||
@objc private func hadlePlayAndOrPaused() {
|
||||
// self.viewModel?.handlePauseOrPlay?()
|
||||
self.viewModel?.handlePauseOrPlay?()
|
||||
|
||||
guard let model = model as? SPShortModel else { return }
|
||||
let vc = SPTVPlayerListViewController()
|
||||
vc.shortPlayId = model.short_play_id
|
||||
vc.videoId = model.video_info?.short_play_video_id
|
||||
SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
||||
// guard let model = model as? SPShortModel else { return }
|
||||
// let vc = SPTVPlayerListViewController()
|
||||
// vc.shortPlayId = model.short_play_id
|
||||
// vc.videoId = model.video_info?.short_play_video_id
|
||||
// SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
|
||||
|
@ -243,7 +243,7 @@ extension SPPlayer {
|
||||
player.playerDidToEnd = { [weak self] (asset) in
|
||||
guard let self = self else { return }
|
||||
if isLoop {
|
||||
self.player.replay()
|
||||
self.replay()
|
||||
} else {
|
||||
self.isPlaying = false
|
||||
self.delegate?.sp_playCompletion?(self)
|
||||
|
@ -5,12 +5,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "home@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "home@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "home@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "home@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "Explore@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "Explore@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "Explore@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "Explore@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "Me@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "Me@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@2x.png",
|
||||
"filename" : "Me@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Frame@3x.png",
|
||||
"filename" : "Me@3x.png",
|
||||
"idiom" : "universal",
|
||||
"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";
|
||||
"Error" = "Error";
|
||||
"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 {
|
||||
///平铺
|
||||
case scatter
|
||||
case left
|
||||
case right
|
||||
case center
|
||||
|
@ -541,8 +541,8 @@ extension JYPageController: UITableViewDelegate, UITableViewDataSource {
|
||||
cell.backgroundColor = .clear
|
||||
cell.selectionStyle = .none
|
||||
// pageContentScrollView.frame = CGRect(x: 0, y: 0, width: childControllerViewFrame.width, height: childControllerViewFrame.height)
|
||||
cell.contentView.addSubview(segmentedView)
|
||||
cell.contentView.addSubview(pageContentScrollView)
|
||||
cell.contentView.addSubview(segmentedView)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
@ -523,12 +523,17 @@ public class JYSegmentedView: UIView {
|
||||
private func updateItemsFrame() {
|
||||
let width = calculateTotalWidth()
|
||||
var startX: CGFloat = config.leftPadding
|
||||
if config.alignment == .center, width < frame.width {
|
||||
startX = (frame.width - width)/2 + config.leftPadding
|
||||
}
|
||||
var itemsMargin = config.itemsMargin
|
||||
|
||||
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
|
||||
|
||||
} 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
|
||||
@ -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)
|
||||
totalWidth = startX + item.frame.width
|
||||
}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)
|
||||
totalWidth = totalWidth + item.frame.width + config.itemsMargin
|
||||
totalWidth = totalWidth + item.frame.width + itemsMargin
|
||||
}
|
||||
|
||||
let bgView = self.itemBackgroundViews[index]
|
||||
@ -556,6 +561,14 @@ public class JYSegmentedView: UIView {
|
||||
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 {
|
||||
var totalWidth: CGFloat = 0
|
||||
for (index,item) in items.enumerated() {
|
||||
|