1.文件目录修改
This commit is contained in:
parent
41b7bc80c4
commit
b7a0a86a1a
@ -685,6 +685,7 @@
|
|||||||
03B1A8EA2EC72BFB006C353F /* Player */ = {
|
03B1A8EA2EC72BFB006C353F /* Player */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
03B1A9242ECBFF03006C353F /* V */,
|
||||||
03B1A9202ECB2F25006C353F /* VC */,
|
03B1A9202ECB2F25006C353F /* VC */,
|
||||||
03B1A8EB2EC72C0E006C353F /* M */,
|
03B1A8EB2EC72C0E006C353F /* M */,
|
||||||
03B1A9232ECBFEF9006C353F /* VM */,
|
03B1A9232ECBFEF9006C353F /* VM */,
|
||||||
@ -715,7 +716,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
03B1A9252ECBFF31006C353F /* SRShortPlayerViewModel.swift */,
|
03B1A9252ECBFF31006C353F /* SRShortPlayerViewModel.swift */,
|
||||||
03B1A9242ECBFF03006C353F /* V */,
|
|
||||||
03980F4E2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift */,
|
03980F4E2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = VM;
|
path = VM;
|
||||||
|
|||||||
@ -26,8 +26,8 @@ class SRVipRetainAlert: SRBaseAlert {
|
|||||||
private lazy var titleView: SRLabel = {
|
private lazy var titleView: SRLabel = {
|
||||||
let label = SRLabel()
|
let label = SRLabel()
|
||||||
label.font = .font(ofSize: 24, weight: .init(900))
|
label.font = .font(ofSize: 24, weight: .init(900))
|
||||||
label.text = "fableo_weekly_refill".localized.uppercased()
|
label.text = "synthreel_weekly_refill".localized.uppercased()
|
||||||
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
|
label.textColors = [UIColor._4_CFFD_4.cgColor,UIColor.white.cgColor, UIColor._51_D_4_FF.cgColor]
|
||||||
label.textStartPoint = .init(x: 0.5, y: 0)
|
label.textStartPoint = .init(x: 0.5, y: 0)
|
||||||
label.textEndPoint = .init(x: 0.5, y: 1)
|
label.textEndPoint = .init(x: 0.5, y: 1)
|
||||||
return label
|
return label
|
||||||
@ -76,7 +76,7 @@ class SRVipRetainAlert: SRBaseAlert {
|
|||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
contentWidth = UIScreen.width - 55
|
contentWidth = UIScreen.width - 55
|
||||||
contentView.backgroundColor = .clear
|
contentView.backgroundColor = .clear
|
||||||
|
closeButton.isHidden = false
|
||||||
fa_setupLayout()
|
fa_setupLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,11 +112,10 @@ extension SRVipRetainAlert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buyButton.snp.makeConstraints { make in
|
buyButton.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(20)
|
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalTo(textLabel.snp.bottom).offset(12)
|
make.top.equalTo(textLabel.snp.bottom).offset(12)
|
||||||
make.height.equalTo(48)
|
make.height.equalTo(50)
|
||||||
make.bottom.equalToSuperview()
|
// make.bottom.equalToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -142,32 +141,8 @@ extension SRVipRetainAlert {
|
|||||||
let imageView = UIImageView(image: UIImage(named: "coinPackCell"))
|
let imageView = UIImageView(image: UIImage(named: "coinPackCell"))
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
// private lazy var bgView: FAGradientView = {
|
|
||||||
// let view = FAGradientView()
|
|
||||||
// view.fa_colors = [UIColor._524_B_8_E.cgColor, UIColor._303265.cgColor]
|
|
||||||
// view.fa_locations = [0, 1]
|
|
||||||
// view.fa_startPoint = .init(x: 0, y: 0.5)
|
|
||||||
// view.fa_endPoint = .init(x: 1, y: 0.5)
|
|
||||||
// view.layer.cornerRadius = 12
|
|
||||||
// view.layer.masksToBounds = true
|
|
||||||
// view.layer.borderWidth = 1
|
|
||||||
// view.layer.borderColor = UIColor.E_5_E_5_E_5.cgColor
|
|
||||||
// return view
|
|
||||||
// }()
|
|
||||||
|
|
||||||
private lazy var bgIconImageView1: UIImageView = {
|
private lazy var vipIconImageView = UIImageView(image: UIImage(named: "vicon"))
|
||||||
let imageView = UIImageView(image: UIImage(named: "coin_attachment_01"))
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var bgIconImageView2: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "coin_attachment_02"))
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
private lazy var bgIconImageView3 = UIImageView(image: UIImage(named: "coin_attachment_04"))
|
|
||||||
private lazy var bgIconImageView4 = UIImageView(image: UIImage(named: "coin_attachment_05"))
|
|
||||||
|
|
||||||
private lazy var vipIconImageView = UIImageView(image: UIImage(named: "皇冠-金"))
|
|
||||||
|
|
||||||
private lazy var nameLabel: UILabel = {
|
private lazy var nameLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
@ -248,10 +223,6 @@ extension SRVipRetainAlert {
|
|||||||
|
|
||||||
|
|
||||||
addSubview(bgView)
|
addSubview(bgView)
|
||||||
bgView.addSubview(bgIconImageView4)
|
|
||||||
bgView.addSubview(bgIconImageView3)
|
|
||||||
bgView.addSubview(bgIconImageView1)
|
|
||||||
bgView.addSubview(bgIconImageView2)
|
|
||||||
bgView.addSubview(vipIconImageView)
|
bgView.addSubview(vipIconImageView)
|
||||||
bgView.addSubview(nameLabel)
|
bgView.addSubview(nameLabel)
|
||||||
bgView.addSubview(textLabel)
|
bgView.addSubview(textLabel)
|
||||||
@ -261,28 +232,9 @@ extension SRVipRetainAlert {
|
|||||||
make.edges.equalToSuperview()
|
make.edges.equalToSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
bgIconImageView1.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(0)
|
|
||||||
make.top.equalToSuperview().offset(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
bgIconImageView2.snp.makeConstraints { make in
|
|
||||||
make.right.equalToSuperview().offset(-26)
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
bgIconImageView3.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.centerX.equalTo(bgIconImageView2)
|
|
||||||
}
|
|
||||||
|
|
||||||
bgIconImageView4.snp.makeConstraints { make in
|
|
||||||
make.left.bottom.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
vipIconImageView.snp.makeConstraints { make in
|
vipIconImageView.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(16)
|
make.left.equalToSuperview().offset(16)
|
||||||
make.top.equalToSuperview().offset(18)
|
make.bottom.equalToSuperview().offset(-22)
|
||||||
}
|
}
|
||||||
|
|
||||||
nameLabel.snp.makeConstraints { make in
|
nameLabel.snp.makeConstraints { make in
|
||||||
@ -291,8 +243,8 @@ extension SRVipRetainAlert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
textLabel.snp.makeConstraints { make in
|
textLabel.snp.makeConstraints { make in
|
||||||
make.left.equalTo(vipIconImageView)
|
make.top.equalTo(6)
|
||||||
make.bottom.equalToSuperview().offset(-18)
|
make.left.equalTo(14)
|
||||||
}
|
}
|
||||||
|
|
||||||
priceView.snp.makeConstraints { make in
|
priceView.snp.makeConstraints { make in
|
||||||
@ -1,79 +0,0 @@
|
|||||||
//
|
|
||||||
// SRDetailRecommendCell.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by CSGY on 2025/11/27.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import FSPagerView
|
|
||||||
import JXPlayer
|
|
||||||
|
|
||||||
class SRDetailRecommendCell: FSPagerViewCell {
|
|
||||||
|
|
||||||
var model: SRShortModel? {
|
|
||||||
didSet {
|
|
||||||
player.coverImageView?.sr_setImage(model?.image_url)
|
|
||||||
player.setPlayUrl(url: model?.video_url ?? "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ① 新增背景图
|
|
||||||
private lazy var bgImageView: UIImageView = {
|
|
||||||
let iv = UIImageView()
|
|
||||||
iv.image = .homeViralHitsCell
|
|
||||||
iv.contentMode = .scaleAspectFill
|
|
||||||
iv.clipsToBounds = true
|
|
||||||
return iv
|
|
||||||
}()
|
|
||||||
|
|
||||||
|
|
||||||
private lazy var player: JXPlayer = {
|
|
||||||
let player = JXPlayer(controlView: nil)
|
|
||||||
player.playerView = self.playerView
|
|
||||||
player.isLoop = true
|
|
||||||
return player
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var playerView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.isUserInteractionEnabled = false
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
contentView.addSubview(bgImageView)
|
|
||||||
bgImageView.snp.makeConstraints { make in
|
|
||||||
make.edges.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 播放器画面
|
|
||||||
contentView.addSubview(playerView)
|
|
||||||
playerView.snp.makeConstraints { make in
|
|
||||||
// make.edges.equalToSuperview()
|
|
||||||
make.edges.equalTo(UIEdgeInsets(top: 15, left: 8, bottom: 15, right: 8))
|
|
||||||
}
|
|
||||||
// addSubview(playerView)
|
|
||||||
//
|
|
||||||
// playerView.snp.makeConstraints { make in
|
|
||||||
// make.edges.equalToSuperview()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func play() {
|
|
||||||
self.player.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func pause() {
|
|
||||||
self.player.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,175 +0,0 @@
|
|||||||
//
|
|
||||||
// SRDetailRecommendview.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by CSGY on 2025/11/27.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import FSPagerView
|
|
||||||
|
|
||||||
class SRDetailRecommendview: SRBaseAlert {
|
|
||||||
|
|
||||||
var clickCloseButton: (() -> Void)?
|
|
||||||
var didSelectedVideo: ((_ model: SRShortModel) -> Void)?
|
|
||||||
|
|
||||||
var dataArr: [SRShortModel] = [] {
|
|
||||||
didSet {
|
|
||||||
self.pagerView.reloadData()
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
||||||
self?.updateCurrentData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private weak var currentCell: SRDetailRecommendCell? {
|
|
||||||
didSet {
|
|
||||||
oldValue?.pause()
|
|
||||||
|
|
||||||
currentCell?.play()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var titleLabel: SRLabel = {
|
|
||||||
let label = SRLabel()
|
|
||||||
label.font = .font(ofSize: 16, weight: .init(900))
|
|
||||||
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
|
|
||||||
label.textStartPoint = .init(x: 0.5, y: 0)
|
|
||||||
label.textEndPoint = .init(x: 0.5, y: 1)
|
|
||||||
label.text = "Keep the Drama Going".localized
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var bgView: UIImageView = {
|
|
||||||
let view = UIImageView(image: UIImage(named: "recommendBg"))
|
|
||||||
view.isUserInteractionEnabled = true
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var cancelButton: UIButton = {
|
|
||||||
let button = UIButton(type: .custom)
|
|
||||||
button.setImage(UIImage(named: "Close"), for: .normal)
|
|
||||||
button.addTarget(self, action: #selector(handleCancelButton), for: .touchUpInside)
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var pagerView: FSPagerView = {
|
|
||||||
let transformer = SRPagerViewTransformer(type: .linear)
|
|
||||||
transformer.minimumScale = 1
|
|
||||||
|
|
||||||
let view = FSPagerView()
|
|
||||||
view.itemSize = .init(width: 132, height: 185)
|
|
||||||
view.transformer = transformer
|
|
||||||
view.delegate = self
|
|
||||||
view.dataSource = self
|
|
||||||
view.isInfinite = true
|
|
||||||
view.interitemSpacing = 8
|
|
||||||
view.register(SRDetailRecommendCell.self, forCellWithReuseIdentifier: "cell")
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
contentWidth = UIScreen.width
|
|
||||||
self.closeButton.isHidden = true
|
|
||||||
|
|
||||||
self.contentView.backgroundColor = .clear
|
|
||||||
self.contentView.layer.cornerRadius = 0
|
|
||||||
self.contentView.layer.masksToBounds = false
|
|
||||||
|
|
||||||
fa_setupLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@objc private func handleCancelButton() {
|
|
||||||
self.dismiss()
|
|
||||||
self.clickCloseButton?()
|
|
||||||
}
|
|
||||||
|
|
||||||
///更新当前数据
|
|
||||||
private func updateCurrentData() {
|
|
||||||
guard let cell = self.pagerView.cellForItem(at: self.pagerView.currentIndex) as? SRDetailRecommendCell else { return }
|
|
||||||
|
|
||||||
self.currentCell = cell
|
|
||||||
// let model = cell.model
|
|
||||||
// self.videoNameLabel.text = model?.name
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRDetailRecommendview {
|
|
||||||
|
|
||||||
private func fa_setupLayout() {
|
|
||||||
contentView.addSubview(bgView)
|
|
||||||
contentView.addSubview(cancelButton)
|
|
||||||
bgView.addSubview(titleLabel)
|
|
||||||
bgView.addSubview(pagerView)
|
|
||||||
|
|
||||||
|
|
||||||
bgView.snp.makeConstraints { make in
|
|
||||||
make.left.right.equalToSuperview().inset(15)
|
|
||||||
make.top.equalToSuperview().offset(-25)
|
|
||||||
// make.bottom.e()
|
|
||||||
}
|
|
||||||
|
|
||||||
titleLabel.snp.makeConstraints { make in
|
|
||||||
make.top.equalTo(17)
|
|
||||||
make.centerX.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
pagerView.snp.makeConstraints { make in
|
|
||||||
make.left.right.equalToSuperview().inset(10)
|
|
||||||
make.bottom.equalTo(-17)
|
|
||||||
make.top.equalTo(titleLabel.snp.bottom).offset(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelButton.snp.makeConstraints { make in
|
|
||||||
make.centerX.equalToSuperview()
|
|
||||||
make.top.equalTo(bgView.snp.bottom).offset(30)
|
|
||||||
make.bottom.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: FSPagerViewDelegate FSPagerViewDataSource
|
|
||||||
extension SRDetailRecommendview: FSPagerViewDelegate, FSPagerViewDataSource {
|
|
||||||
|
|
||||||
func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell {
|
|
||||||
let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) as! SRDetailRecommendCell
|
|
||||||
cell.model = self.dataArr[index]
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
func numberOfItems(in pagerView: FSPagerView) -> Int {
|
|
||||||
return self.dataArr.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) {
|
|
||||||
didSelectedVideo?(self.dataArr[index])
|
|
||||||
self.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
func pagerViewDidEndDecelerating(_ pagerView: FSPagerView) {
|
|
||||||
self.updateCurrentData()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRDetailRecommendview {
|
|
||||||
|
|
||||||
class PageControl: UIPageControl{
|
|
||||||
override func size(forNumberOfPages pageCount: Int) -> CGSize {
|
|
||||||
return .init(width: 4, height: 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
//
|
|
||||||
// SREpSelectorCell.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/19.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import SnapKit
|
|
||||||
|
|
||||||
class SREpSelectorCell: UICollectionViewCell {
|
|
||||||
|
|
||||||
|
|
||||||
var model: SRVideoInfoModel? {
|
|
||||||
didSet {
|
|
||||||
numLabel.text = model?.episode
|
|
||||||
lockImageview.isHidden = !(model?.is_lock ?? true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sr_isSelected: Bool = false {
|
|
||||||
didSet {
|
|
||||||
if sr_isSelected {
|
|
||||||
numLabel.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor]
|
|
||||||
boderView.isHidden = false
|
|
||||||
} else {
|
|
||||||
numLabel.textColors = [UIColor.white.cgColor, UIColor.white.cgColor]
|
|
||||||
boderView.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var numLabel: SRLabel = {
|
|
||||||
let label = SRLabel()
|
|
||||||
label.font = .font(ofSize: 14, weight: .regular)
|
|
||||||
label.textStartPoint = .init(x: 0.5, y: 0)
|
|
||||||
label.textEndPoint = .init(x: 0.5, y: 1)
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var boderView: SRGradientView = {
|
|
||||||
let view = SRGradientView()
|
|
||||||
view.colors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor]
|
|
||||||
view.startPoint = .init(x: 0.5, y: 0)
|
|
||||||
view.endPoint = .init(x: 0.5, y: 1)
|
|
||||||
view.layer.cornerRadius = 10
|
|
||||||
view.layer.masksToBounds = true
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var lockImageview = UIImageView.init(image: .lock)
|
|
||||||
|
|
||||||
lazy var boderLayer: CAShapeLayer = {
|
|
||||||
let layer = CAShapeLayer()
|
|
||||||
return layer
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
contentView.layer.cornerRadius = 10
|
|
||||||
contentView.layer.masksToBounds = true
|
|
||||||
contentView.backgroundColor = ._1_B_1_B_1_B
|
|
||||||
boderLayer.fillColor = contentView.backgroundColor?.cgColor // 设置为透明填充,实现镂空效果
|
|
||||||
|
|
||||||
|
|
||||||
contentView.addSubview(boderView)
|
|
||||||
boderView.layer.addSublayer(boderLayer)
|
|
||||||
contentView.addSubview(numLabel)
|
|
||||||
contentView.addSubview(lockImageview)
|
|
||||||
|
|
||||||
numLabel.snp.makeConstraints { make in
|
|
||||||
make.center.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
boderView.snp.makeConstraints { make in
|
|
||||||
make.edges.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
lockImageview.snp.makeConstraints { make in
|
|
||||||
make.right.top.equalToSuperview().inset(4)
|
|
||||||
make.width.height.equalTo(12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
let size = self.bounds.size
|
|
||||||
let boderWidth: CGFloat = 1
|
|
||||||
|
|
||||||
boderLayer.path = UIBezierPath(roundedRect: .init(x: boderWidth, y: boderWidth, width: size.width - boderWidth * 2, height: size.height - boderWidth * 2), cornerRadius: 9.5).cgPath
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
//
|
|
||||||
// SREpSelectorView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/18.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import HWPanModal
|
|
||||||
import SnapKit
|
|
||||||
|
|
||||||
class SREpSelectorView: SRPanModalContentView {
|
|
||||||
|
|
||||||
var didSelected: ((_ index: Int) -> Void)?
|
|
||||||
|
|
||||||
var model: SRShortDetailModel? {
|
|
||||||
didSet {
|
|
||||||
coverImageView.sr_setImage(model?.shortPlayInfo?.image_url)
|
|
||||||
shortNameLabel.text = model?.shortPlayInfo?.name
|
|
||||||
desLabel.text = model?.shortPlayInfo?.sr_description
|
|
||||||
|
|
||||||
subtitleLabel.text = "all_episodes_text".localizedReplace(text: "\(model?.shortPlayInfo?.episode_total ?? 0)")
|
|
||||||
|
|
||||||
if let text = model?.shortPlayInfo?.category?.first, text.count > 0 {
|
|
||||||
cagetoryLabel.text = "#" + text
|
|
||||||
} else {
|
|
||||||
cagetoryLabel.text = ""
|
|
||||||
}
|
|
||||||
self.collectionView.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var selectedIndex: Int = 0 {
|
|
||||||
didSet {
|
|
||||||
self.collectionView.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var coverBgView = UIImageView(image: UIImage(named: "ep_cover_bg_image"))
|
|
||||||
lazy var coverImageView: UIImageView = {
|
|
||||||
let imageView = SRImageView()
|
|
||||||
imageView.layer.cornerRadius = 2
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var shortNameLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 15, weight: .semibold)
|
|
||||||
label.textColor = .srBlue
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var cagetoryLabel: SRLabel = {
|
|
||||||
let label = SRLabel()
|
|
||||||
label.font = .font(ofSize: 12, weight: .regular)
|
|
||||||
label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor]
|
|
||||||
label.textStartPoint = .init(x: 0.5, y: 0)
|
|
||||||
label.textEndPoint = .init(x: 0.5, y: 1)
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var desLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 12, weight: .regular)
|
|
||||||
label.textColor = .A_6_A_6_A_6
|
|
||||||
label.numberOfLines = 3
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var titleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 15, weight: .medium)
|
|
||||||
label.textColor = .white
|
|
||||||
label.text = "Select Episode".localized
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var subtitleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 12, weight: .regular)
|
|
||||||
label.textColor = .CCCCCC
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
|
||||||
let itemWidth = (UIScreen.width - 30 - 40) / 5
|
|
||||||
|
|
||||||
let layout = UICollectionViewFlowLayout()
|
|
||||||
layout.minimumLineSpacing = 10
|
|
||||||
layout.minimumInteritemSpacing = 10
|
|
||||||
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
|
|
||||||
layout.itemSize = .init(width: floor(itemWidth), height: 50)
|
|
||||||
return layout
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var collectionView: SRCollectionView = {
|
|
||||||
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
|
||||||
collectionView.delegate = self
|
|
||||||
collectionView.dataSource = self
|
|
||||||
collectionView.showsVerticalScrollIndicator = false
|
|
||||||
collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
|
|
||||||
collectionView.register(SREpSelectorCell.self, forCellWithReuseIdentifier: "cell")
|
|
||||||
return collectionView
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
sr_setupUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension SREpSelectorView {
|
|
||||||
|
|
||||||
private func sr_setupUI() {
|
|
||||||
addSubview(coverBgView)
|
|
||||||
addSubview(coverImageView)
|
|
||||||
addSubview(shortNameLabel)
|
|
||||||
addSubview(cagetoryLabel)
|
|
||||||
addSubview(desLabel)
|
|
||||||
addSubview(titleLabel)
|
|
||||||
addSubview(subtitleLabel)
|
|
||||||
addSubview(collectionView)
|
|
||||||
|
|
||||||
coverBgView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(15)
|
|
||||||
make.top.equalToSuperview().offset(18)
|
|
||||||
}
|
|
||||||
|
|
||||||
coverImageView.snp.makeConstraints { make in
|
|
||||||
make.center.equalTo(coverBgView)
|
|
||||||
make.width.equalTo(63)
|
|
||||||
make.height.equalTo(84)
|
|
||||||
}
|
|
||||||
|
|
||||||
shortNameLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalTo(coverImageView.snp.right).offset(10)
|
|
||||||
make.top.equalToSuperview().offset(24)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-15)
|
|
||||||
}
|
|
||||||
|
|
||||||
cagetoryLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalTo(shortNameLabel)
|
|
||||||
make.top.equalTo(shortNameLabel.snp.bottom).offset(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
desLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalTo(shortNameLabel)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-15)
|
|
||||||
make.top.equalTo(shortNameLabel.snp.bottom).offset(32)
|
|
||||||
}
|
|
||||||
|
|
||||||
titleLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(15)
|
|
||||||
make.top.equalTo(coverBgView.snp.bottom).offset(16)
|
|
||||||
}
|
|
||||||
|
|
||||||
subtitleLabel.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalTo(titleLabel)
|
|
||||||
make.left.equalTo(titleLabel.snp.right).offset(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectionView.snp.makeConstraints { make in
|
|
||||||
make.left.right.equalToSuperview()
|
|
||||||
make.top.equalToSuperview().offset(166)
|
|
||||||
make.bottom.equalToSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: UICollectionViewDelegate UICollectionViewDataSource
|
|
||||||
extension SREpSelectorView: UICollectionViewDelegate, UICollectionViewDataSource {
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SREpSelectorCell
|
|
||||||
cell.model = self.model?.episodeList?[indexPath.row]
|
|
||||||
cell.sr_isSelected = indexPath.row == self.selectedIndex
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
||||||
return self.model?.episodeList?.count ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
||||||
|
|
||||||
guard let epList = self.model?.episodeList else { return }
|
|
||||||
if self.selectedIndex == indexPath.row { return }
|
|
||||||
|
|
||||||
let lastIndex = indexPath.row - 1
|
|
||||||
var lastIsLock = false
|
|
||||||
if lastIndex > 0 && lastIndex < epList.count {
|
|
||||||
let lastModel = epList[lastIndex]
|
|
||||||
lastIsLock = lastModel.is_lock ?? false
|
|
||||||
}
|
|
||||||
if lastIsLock {
|
|
||||||
SRToast.show(text: "buy_fail_toast_02".localized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.didSelected?(indexPath.row)
|
|
||||||
Task {
|
|
||||||
await self.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
//
|
|
||||||
// SRProgressView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/18.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import YYText
|
|
||||||
import YYCategories
|
|
||||||
|
|
||||||
class SRProgressView: UIView {
|
|
||||||
|
|
||||||
///滑动开始
|
|
||||||
var panStart: (() -> Void)?
|
|
||||||
|
|
||||||
///滑动中
|
|
||||||
var panChange: ((_ progress: CGFloat) -> Void)?
|
|
||||||
|
|
||||||
///滑动完成回调
|
|
||||||
var panFinish: ((_ progress: CGFloat) -> Void)?
|
|
||||||
|
|
||||||
var progress: CGFloat = 0 {
|
|
||||||
didSet {
|
|
||||||
if !isPaning {
|
|
||||||
setNeedsDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///用来记录滑动时的当前进度
|
|
||||||
private var tempProgress: CGFloat = 0
|
|
||||||
|
|
||||||
///滑动进度
|
|
||||||
private var panProgress: CGFloat = 0
|
|
||||||
|
|
||||||
var progressColor = UIColor.white.withAlphaComponent(0.2)
|
|
||||||
var currentProgress = UIColor.white
|
|
||||||
|
|
||||||
var lineWidth: CGFloat = 3
|
|
||||||
|
|
||||||
///加载中状态
|
|
||||||
var isLoading = false {
|
|
||||||
didSet {
|
|
||||||
if isLoading {
|
|
||||||
if gradientTimer == nil {
|
|
||||||
gradientTimer = Timer.scheduledTimer(timeInterval: 0.05, target: YYTextWeakProxy(target: self), selector: #selector(handleGradientTimer), userInfo: nil, repeats: true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gradientTimer?.invalidate()
|
|
||||||
gradientTimer = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var insets: UIEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: 0) {
|
|
||||||
didSet {
|
|
||||||
self.invalidateIntrinsicContentSize()
|
|
||||||
setNeedsDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private(set) lazy var panGesture: UIPanGestureRecognizer = {
|
|
||||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(sender:)))
|
|
||||||
return pan
|
|
||||||
}()
|
|
||||||
|
|
||||||
private(set) lazy var tagGesture: UITapGestureRecognizer = {
|
|
||||||
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(sender:)))
|
|
||||||
return tap
|
|
||||||
}()
|
|
||||||
|
|
||||||
///是否在滑动中
|
|
||||||
private var isPaning: Bool = false
|
|
||||||
|
|
||||||
private var gradientTimer: Timer?
|
|
||||||
|
|
||||||
private var gradientValue: CGFloat = 0
|
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
|
||||||
return .init(width: UIScreen.width, height: lineWidth + insets.top + insets.bottom)
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
// self.backgroundColor = progressColor
|
|
||||||
self.backgroundColor = .clear
|
|
||||||
|
|
||||||
self.addGestureRecognizer(panGesture)
|
|
||||||
self.addGestureRecognizer(tagGesture)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
||||||
super.traitCollectionDidChange(previousTraitCollection)
|
|
||||||
setNeedsDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleGradientTimer() {
|
|
||||||
gradientValue += 0.1
|
|
||||||
if gradientValue > 1 {
|
|
||||||
gradientValue = 0
|
|
||||||
}
|
|
||||||
setNeedsDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func draw(_ rect: CGRect) {
|
|
||||||
super.draw(rect)
|
|
||||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
|
||||||
let width = rect.width
|
|
||||||
|
|
||||||
let progressX = insets.left
|
|
||||||
let progressY = insets.top
|
|
||||||
let progressWidth = width - insets.left - insets.right
|
|
||||||
|
|
||||||
if isLoading, !isPaning {
|
|
||||||
// 定义颜色空间
|
|
||||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
|
||||||
let colors: [CGColor] = [
|
|
||||||
UIColor.clear.cgColor,
|
|
||||||
UIColor.white.cgColor,
|
|
||||||
UIColor.clear.cgColor
|
|
||||||
]
|
|
||||||
let locations: [CGFloat] = [0.0, gradientValue, 1.0]
|
|
||||||
|
|
||||||
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let gradientRect = CGRect(x: progressX,
|
|
||||||
y: progressY,
|
|
||||||
width: progressWidth,
|
|
||||||
height: lineWidth)
|
|
||||||
|
|
||||||
// 定义渐变的起点和终点
|
|
||||||
let startPoint = CGPoint(x: rect.minX, y: rect.minY)
|
|
||||||
let endPoint = CGPoint(x: rect.maxX, y: rect.maxY)
|
|
||||||
|
|
||||||
// 裁剪到渐变区域
|
|
||||||
context.saveGState()
|
|
||||||
context.clip(to: gradientRect)
|
|
||||||
|
|
||||||
// 绘制渐变
|
|
||||||
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
|
|
||||||
} else {
|
|
||||||
var progress = self.progress
|
|
||||||
if self.isPaning {
|
|
||||||
progress = self.panProgress
|
|
||||||
}
|
|
||||||
let currentProgressWidth = progressWidth * progress
|
|
||||||
|
|
||||||
///绘制进度
|
|
||||||
let progressPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth, height: lineWidth), cornerRadius: lineWidth / 2)
|
|
||||||
context.addPath(progressPath.cgPath)
|
|
||||||
context.setFillColor(progressColor.cgColor)
|
|
||||||
context.fillPath()
|
|
||||||
|
|
||||||
///绘制当前进度
|
|
||||||
let currentPath = UIBezierPath(roundedRect: CGRect(x: progressX, y: progressY, width: progressWidth * progress, height: lineWidth), cornerRadius: lineWidth / 2)
|
|
||||||
context.addPath(currentPath.cgPath)
|
|
||||||
context.setFillColor(currentProgress.cgColor)
|
|
||||||
context.fillPath()
|
|
||||||
|
|
||||||
///绘制一个点
|
|
||||||
let path = UIBezierPath(arcCenter: .init(x: currentProgressWidth + progressX, y: progressY + lineWidth / 2), radius: 3, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
|
|
||||||
context.addPath(path.cgPath)
|
|
||||||
context.setFillColor(currentProgress.cgColor)
|
|
||||||
context.fillPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRProgressView {
|
|
||||||
@objc func handlePanGesture(sender: UIPanGestureRecognizer) {
|
|
||||||
|
|
||||||
switch sender.state {
|
|
||||||
case .began:
|
|
||||||
self.isPaning = true
|
|
||||||
self.tempProgress = self.progress
|
|
||||||
sender.setTranslation(CGPoint(x: 0, y: 0), in: self)
|
|
||||||
self.panStart?()
|
|
||||||
|
|
||||||
case .changed:
|
|
||||||
let point = sender.translation(in: self)
|
|
||||||
let offsetX = point.x / (self.width - self.insets.left - self.insets.right)
|
|
||||||
self.panProgress = self.tempProgress + offsetX
|
|
||||||
if self.panProgress < 0 {
|
|
||||||
self.panProgress = 0
|
|
||||||
}
|
|
||||||
self.panChange?(self.panProgress)
|
|
||||||
setNeedsDisplay()
|
|
||||||
|
|
||||||
default:
|
|
||||||
self.isPaning = false
|
|
||||||
self.panFinish?(self.panProgress)
|
|
||||||
|
|
||||||
self.panProgress = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func handleTapGesture(sender: UITapGestureRecognizer) {
|
|
||||||
let point = sender.location(in: self)
|
|
||||||
let offsetX = (point.x - self.insets.left) / (self.width - self.insets.left - self.insets.right)
|
|
||||||
self.panFinish?(offsetX)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// SRRecommendPlayerCell.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/20.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import JXPlayer
|
|
||||||
|
|
||||||
class SRRecommendPlayerCell: JXPlayerListCell {
|
|
||||||
|
|
||||||
override var ControlViewClass: JXPlayerListControlView.Type {
|
|
||||||
return SRRecommendPlayerControlView.self
|
|
||||||
}
|
|
||||||
|
|
||||||
override var model: Any? {
|
|
||||||
didSet {
|
|
||||||
let model = self.model as? SRShortModel
|
|
||||||
let videoInfo = model?.video_info
|
|
||||||
self.player.setPlayUrl(url: videoInfo?.video_url ?? "")
|
|
||||||
self.player.coverImageView?.sr_setImage(model?.image_url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
//
|
|
||||||
// SRRecommendPlayerControlView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/20.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import JXPlayer
|
|
||||||
import SnapKit
|
|
||||||
import YYCategories
|
|
||||||
|
|
||||||
class SRRecommendPlayerControlView: JXPlayerListControlView {
|
|
||||||
|
|
||||||
override var viewModel: JXPlayerListViewModel? {
|
|
||||||
didSet {
|
|
||||||
self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var model: Any? {
|
|
||||||
didSet {
|
|
||||||
let model = model as! SRShortModel
|
|
||||||
|
|
||||||
shortNameLabel.text = model.name
|
|
||||||
|
|
||||||
stackView.sr_removeAllArrangedSubview()
|
|
||||||
if let text = model.category?.first, text.count > 0 {
|
|
||||||
categoryLabel.text = "#" + text
|
|
||||||
stackView.addArrangedSubview(categoryLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let text = model.sr_description, text.count > 0 {
|
|
||||||
desLabel.text = text
|
|
||||||
stackView.addArrangedSubview(desLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isCurrent: Bool {
|
|
||||||
didSet {
|
|
||||||
updatePlayerViewStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var controlerView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "short_progress_bg_image"))
|
|
||||||
imageView.isUserInteractionEnabled = true
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var shortNameLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 14, weight: .semibold)
|
|
||||||
label.textColor = .srBlue
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var stackView: UIStackView = {
|
|
||||||
let view = UIStackView()
|
|
||||||
view.axis = .vertical
|
|
||||||
view.spacing = 8
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var categoryLabel: SRLabel = {
|
|
||||||
let label = SRLabel()
|
|
||||||
label.font = .font(ofSize: 11, weight: .regular)
|
|
||||||
label.textColors = [UIColor.srGreen.cgColor, UIColor.srBlue.cgColor]
|
|
||||||
label.textStartPoint = .init(x: 0.5, y: 0)
|
|
||||||
label.textEndPoint = .init(x: 0.5, y: 1)
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var desLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 11, weight: .regular)
|
|
||||||
label.textColor = .A_6_A_6_A_6
|
|
||||||
label.numberOfLines = 2
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var epBgView: UIView = {
|
|
||||||
let view = SRGradientView()
|
|
||||||
view.colors = [UIColor._51_D_4_FF.withAlphaComponent(0.5).cgColor, UIColor._4_CFFD_4.withAlphaComponent(0.1).cgColor]
|
|
||||||
view.startPoint = .init(x: 0, y: 0.5)
|
|
||||||
view.endPoint = .init(x: 1, y: 0.5)
|
|
||||||
view.layer.cornerRadius = 2
|
|
||||||
view.layer.masksToBounds = true
|
|
||||||
let tap = UITapGestureRecognizer { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let vc = SRDetailPlayerViewController()
|
|
||||||
vc.shortId = (self.model as? SRShortModel)?.short_play_id
|
|
||||||
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
|
||||||
}
|
|
||||||
view.addGestureRecognizer(tap)
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var epIconImageView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "ep_icon_02"))
|
|
||||||
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
|
||||||
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var epTextLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 12, weight: .regular)
|
|
||||||
label.textColor = .white
|
|
||||||
label.text = "recommend_ep_text".localized
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var indicatorImageView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "arrow_right_icon_02"))
|
|
||||||
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
|
||||||
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var playerImageView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "play_icon_02"))
|
|
||||||
imageView.isHidden = true
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.viewModel?.removeObserver(self, forKeyPath: "isPlaying")
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
sr_setupUI()
|
|
||||||
|
|
||||||
let tap = UITapGestureRecognizer { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.viewModel?.userSwitchPlayAndPause()
|
|
||||||
}
|
|
||||||
self.addGestureRecognizer(tap)
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
||||||
if keyPath == "isPlaying" {
|
|
||||||
updatePlayerViewStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func updatePlayerViewStatus() {
|
|
||||||
if self.viewModel?.isPlaying == true || !isCurrent {
|
|
||||||
playerImageView.isHidden = true
|
|
||||||
} else {
|
|
||||||
playerImageView.isHidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRRecommendPlayerControlView {
|
|
||||||
|
|
||||||
private func sr_setupUI() {
|
|
||||||
addSubview(controlerView)
|
|
||||||
controlerView.addSubview(shortNameLabel)
|
|
||||||
controlerView.addSubview(stackView)
|
|
||||||
controlerView.addSubview(epBgView)
|
|
||||||
epBgView.addSubview(epIconImageView)
|
|
||||||
epBgView.addSubview(epTextLabel)
|
|
||||||
epBgView.addSubview(indicatorImageView)
|
|
||||||
addSubview(playerImageView)
|
|
||||||
|
|
||||||
controlerView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(15)
|
|
||||||
make.centerX.equalToSuperview()
|
|
||||||
make.bottom.equalToSuperview().offset(-10)
|
|
||||||
}
|
|
||||||
|
|
||||||
shortNameLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(12)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-12)
|
|
||||||
make.top.equalToSuperview().offset(13)
|
|
||||||
}
|
|
||||||
|
|
||||||
stackView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(12)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-12)
|
|
||||||
make.top.equalTo(shortNameLabel.snp.bottom).offset(8)
|
|
||||||
make.bottom.equalToSuperview().offset(-52)
|
|
||||||
}
|
|
||||||
|
|
||||||
epBgView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(12)
|
|
||||||
make.right.equalToSuperview().offset(-12)
|
|
||||||
make.bottom.equalToSuperview().offset(-18)
|
|
||||||
make.height.equalTo(26)
|
|
||||||
}
|
|
||||||
|
|
||||||
epIconImageView.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.left.equalToSuperview().offset(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
epTextLabel.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.left.equalTo(epIconImageView.snp.right).offset(4)
|
|
||||||
make.right.lessThanOrEqualTo(self.indicatorImageView.snp.left).offset(-5)
|
|
||||||
}
|
|
||||||
|
|
||||||
indicatorImageView.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.right.equalToSuperview().offset(-12)
|
|
||||||
}
|
|
||||||
|
|
||||||
playerImageView.snp.makeConstraints { make in
|
|
||||||
make.center.equalToSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,240 +0,0 @@
|
|||||||
//
|
|
||||||
// SRShortDetailControlView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/18.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import JXPlayer
|
|
||||||
import SnapKit
|
|
||||||
|
|
||||||
class SRShortDetailControlView: JXPlayerListControlView {
|
|
||||||
|
|
||||||
var sr_viewModel: SRShortPlayerViewModel? {
|
|
||||||
return self.viewModel as? SRShortPlayerViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
override var viewModel: JXPlayerListViewModel? {
|
|
||||||
didSet {
|
|
||||||
self.viewModel?.addObserver(self, forKeyPath: "isPlaying", context: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var shortModel: SRShortModel? {
|
|
||||||
didSet {
|
|
||||||
titleLabel.text = shortModel?.name
|
|
||||||
collectButton.isSelected = shortModel?.is_collect == true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var durationTime: TimeInterval {
|
|
||||||
didSet {
|
|
||||||
updateProgress()
|
|
||||||
let (_, m, s) = Int(durationTime).formatTimeGroup()
|
|
||||||
totalTimeLabel.text = "\(m):\(s)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var currentTime: TimeInterval {
|
|
||||||
didSet {
|
|
||||||
updateProgress()
|
|
||||||
let (_, m, s) = Int(currentTime).formatTimeGroup()
|
|
||||||
currentTimeLabel.text = "\(m):\(s)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isLoading: Bool {
|
|
||||||
didSet {
|
|
||||||
progressView.isLoading = isLoading
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var progressBgView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "short_progress_bg_image"))
|
|
||||||
imageView.isUserInteractionEnabled = true
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var titleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 14, weight: .medium)
|
|
||||||
label.textColor = .srBlue
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var progressView: SRProgressView = {
|
|
||||||
let view = SRProgressView()
|
|
||||||
view.insets = .init(top: 10, left: 5, bottom: 10, right: 5)
|
|
||||||
view.panFinish = { [weak self] progress in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.viewModel?.seekTo(Float(progress))
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var totalTimeLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 10, weight: .regular)
|
|
||||||
label.textColor = .DFDFDF
|
|
||||||
label.text = "00:00"
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var currentTimeLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 10, weight: .regular)
|
|
||||||
label.textColor = .DFDFDF
|
|
||||||
label.text = "00:00"
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var epButton: UIButton = {
|
|
||||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.sr_viewModel?.onEpSelectorView()
|
|
||||||
}))
|
|
||||||
button.setImage(UIImage(named: "ep_icon_01"), for: .normal)
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var collectButton: UIButton = {
|
|
||||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let shortId = self.shortModel?.short_play_id else { return }
|
|
||||||
let videoId = (self.model as? SRVideoInfoModel)?.short_play_video_id
|
|
||||||
let isCollect = !(self.shortModel?.is_collect ?? false)
|
|
||||||
|
|
||||||
Task {
|
|
||||||
await SRShortApi.requestShortCollect(shortId: shortId, videoId: videoId, isCollect: isCollect)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
button.setImage(UIImage(named: "collect_icon_01"), for: .normal)
|
|
||||||
button.setImage(UIImage(named: "collect_icon_01_selected"), for: .selected)
|
|
||||||
button.setImage(UIImage(named: "collect_icon_01_selected"), for: [.selected, .highlighted])
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
|
|
||||||
lazy var playerImageView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "play_icon_02"))
|
|
||||||
imageView.isHidden = true
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.viewModel?.removeObserver(self, forKeyPath: "isPlaying")
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(updateShortCollectStateNotification), name: SRShortApi.updateShortCollectStateNotification, object: nil)
|
|
||||||
|
|
||||||
let tap = UITapGestureRecognizer { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.viewModel?.userSwitchPlayAndPause()
|
|
||||||
}
|
|
||||||
self.addGestureRecognizer(tap)
|
|
||||||
|
|
||||||
sr_setupUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func updateShortCollectStateNotification(sender: Notification) {
|
|
||||||
guard let userInfo = sender.userInfo else { return }
|
|
||||||
guard let shortId = userInfo["id"] as? String else { return }
|
|
||||||
guard let state = userInfo["state"] as? Bool else { return }
|
|
||||||
guard shortId == self.shortModel?.short_play_id else { return }
|
|
||||||
self.shortModel?.is_collect = state
|
|
||||||
|
|
||||||
collectButton.isSelected = state
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateProgress() {
|
|
||||||
guard durationTime > 0 else {
|
|
||||||
progressView.progress = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
progressView.progress = currentTime / durationTime
|
|
||||||
}
|
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
||||||
if keyPath == "isPlaying" {
|
|
||||||
updatePlayerViewStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func updatePlayerViewStatus() {
|
|
||||||
if self.viewModel?.isPlaying == true || !isCurrent {
|
|
||||||
playerImageView.isHidden = true
|
|
||||||
} else {
|
|
||||||
playerImageView.isHidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRShortDetailControlView {
|
|
||||||
|
|
||||||
private func sr_setupUI() {
|
|
||||||
addSubview(progressBgView)
|
|
||||||
progressBgView.addSubview(titleLabel)
|
|
||||||
progressBgView.addSubview(progressView)
|
|
||||||
progressBgView.addSubview(totalTimeLabel)
|
|
||||||
progressBgView.addSubview(currentTimeLabel)
|
|
||||||
addSubview(epButton)
|
|
||||||
addSubview(collectButton)
|
|
||||||
addSubview(playerImageView)
|
|
||||||
|
|
||||||
progressBgView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(15)
|
|
||||||
make.centerX.equalToSuperview()
|
|
||||||
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 5))
|
|
||||||
make.height.equalTo(88)
|
|
||||||
}
|
|
||||||
|
|
||||||
titleLabel.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalTo(progressBgView.snp.top).offset(23)
|
|
||||||
make.left.equalToSuperview().offset(9)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-9)
|
|
||||||
}
|
|
||||||
|
|
||||||
progressView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(4)
|
|
||||||
make.right.equalToSuperview().offset(-6)
|
|
||||||
make.bottom.equalToSuperview().offset(-30)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalTimeLabel.snp.makeConstraints { make in
|
|
||||||
make.right.equalToSuperview().offset(-11)
|
|
||||||
make.bottom.equalToSuperview().offset(-24)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTimeLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(9)
|
|
||||||
make.bottom.equalToSuperview().offset(-24)
|
|
||||||
}
|
|
||||||
|
|
||||||
epButton.snp.makeConstraints { make in
|
|
||||||
make.right.equalToSuperview().offset(-15)
|
|
||||||
make.bottom.equalTo(progressBgView.snp.top).offset(-44)
|
|
||||||
}
|
|
||||||
|
|
||||||
collectButton.snp.makeConstraints { make in
|
|
||||||
make.centerX.equalTo(epButton)
|
|
||||||
make.bottom.equalTo(epButton.snp.top).offset(-25)
|
|
||||||
}
|
|
||||||
|
|
||||||
playerImageView.snp.makeConstraints { make in
|
|
||||||
make.center.equalToSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// SRShortDetailPlayerCell.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by 湖北秦九 on 2025/11/18.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import JXPlayer
|
|
||||||
|
|
||||||
class SRShortDetailPlayerCell: JXPlayerListCell {
|
|
||||||
|
|
||||||
override var ControlViewClass: JXPlayerListControlView.Type {
|
|
||||||
return SRShortDetailControlView.self
|
|
||||||
}
|
|
||||||
|
|
||||||
var sr_controlView: SRShortDetailControlView {
|
|
||||||
return self.controlView as! SRShortDetailControlView
|
|
||||||
}
|
|
||||||
|
|
||||||
var sr_viewModel: SRShortPlayerViewModel? {
|
|
||||||
return self.viewModel as? SRShortPlayerViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasLastEpisodeUnlocked: Bool = false {
|
|
||||||
didSet {
|
|
||||||
self.lockView.hasLastEpisodeUnlocked = hasLastEpisodeUnlocked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var lockView: SRVideoLockView = {
|
|
||||||
let view = SRVideoLockView()
|
|
||||||
view.clickUnlockButton = { [weak self] in
|
|
||||||
Task {
|
|
||||||
await self?.sr_viewModel?.handleUnlockVideo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
view.adUnlockButton = { [weak self] in
|
|
||||||
Task {
|
|
||||||
await self?.sr_viewModel?.handleAdUnlockVideo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
override var model: Any? {
|
|
||||||
didSet {
|
|
||||||
let model = self.model as? SRVideoInfoModel
|
|
||||||
self.player.setPlayUrl(url: model?.video_url ?? "")
|
|
||||||
|
|
||||||
self.lockView.isHidden = !(model?.is_lock ?? true)
|
|
||||||
lockView.videoInfo = model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
sr_setupLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
var shortModel: SRShortModel? {
|
|
||||||
didSet {
|
|
||||||
self.sr_controlView.shortModel = shortModel
|
|
||||||
self.player.coverImageView?.sr_setImage(shortModel?.image_url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension SRShortDetailPlayerCell {
|
|
||||||
|
|
||||||
private func sr_setupLayout() {
|
|
||||||
addSubview(lockView)
|
|
||||||
|
|
||||||
lockView.snp.makeConstraints { make in
|
|
||||||
make.edges.equalToSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
//
|
|
||||||
// SRVideoLockView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by CSGY on 2025/12/1.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class SRVideoLockView: UIView {
|
|
||||||
|
|
||||||
var clickUnlockButton: (() -> Void)?
|
|
||||||
|
|
||||||
var adUnlockButton: (() -> Void)?
|
|
||||||
|
|
||||||
var videoInfo: SRVideoInfoModel? {
|
|
||||||
didSet {
|
|
||||||
unlockButton.setNeedsUpdateConfiguration()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasLastEpisodeUnlocked = false {
|
|
||||||
didSet {
|
|
||||||
unlockButton.setNeedsUpdateConfiguration()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var unlockStackView: UIStackView = {
|
|
||||||
let stack = UIStackView()
|
|
||||||
stack.axis = .vertical
|
|
||||||
stack.spacing = 12
|
|
||||||
stack.distribution = .fillEqually
|
|
||||||
return stack
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var unlockButton: UIButton = {
|
|
||||||
var config = UIButton.Configuration.plain()
|
|
||||||
config.image = UIImage(named: "lock")
|
|
||||||
config.imagePadding = 6
|
|
||||||
// 设置背景图片(使用 UIImage 作为背景)
|
|
||||||
config.background.image = UIImage(named: "unlockButtonBg")
|
|
||||||
config.background.imageContentMode = .scaleToFill // 让背景图铺满按钮
|
|
||||||
|
|
||||||
let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.clickUnlockButton?()
|
|
||||||
}))
|
|
||||||
button.configurationUpdateHandler = { [weak self] button in
|
|
||||||
guard let self = self else { return }
|
|
||||||
let attributeContainer = AttributeContainer([
|
|
||||||
.font : UIFont.font(ofSize: 14, weight: .medium),
|
|
||||||
.foregroundColor : UIColor._51_D_4_FF
|
|
||||||
])
|
|
||||||
if hasLastEpisodeUnlocked {
|
|
||||||
button.configuration?.attributedTitle = .init("video_lock_tip_text".localized, attributes: attributeContainer)
|
|
||||||
} else {
|
|
||||||
button.configuration?.attributedTitle = .init("synthreel_unlocking_coins_notice".localizedReplace(text: "\(videoInfo?.coins ?? 0)"), attributes: attributeContainer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var adlockButton: UIButton = {
|
|
||||||
var config = UIButton.Configuration.plain()
|
|
||||||
config.image = UIImage(named: "adlock")
|
|
||||||
config.imagePadding = 6
|
|
||||||
// 设置背景图片(使用 UIImage 作为背景)
|
|
||||||
config.background.image = UIImage(named: "unlockButtonBg")
|
|
||||||
config.background.imageContentMode = .scaleToFill // 让背景图铺满按钮
|
|
||||||
let attr = AttributeContainer([
|
|
||||||
.font: UIFont.font(ofSize: 14, weight: .medium),
|
|
||||||
.foregroundColor: UIColor.white
|
|
||||||
])
|
|
||||||
config.attributedTitle = AttributedString("Watch 2ads to unlock".localized, attributes: attr)
|
|
||||||
|
|
||||||
let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.adUnlockButton?()
|
|
||||||
}))
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
backgroundColor = ._000000.withAlphaComponent(0.6)
|
|
||||||
sr_setupLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRVideoLockView {
|
|
||||||
|
|
||||||
private func sr_setupLayout() {
|
|
||||||
addSubview(unlockStackView)
|
|
||||||
|
|
||||||
unlockStackView.addArrangedSubview(unlockButton)
|
|
||||||
unlockStackView.addArrangedSubview(adlockButton)
|
|
||||||
|
|
||||||
|
|
||||||
unlockStackView.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(45)
|
|
||||||
make.right.equalToSuperview().offset(-45)
|
|
||||||
make.height.equalTo(43 * 2 + 30) // 两个按钮 + 间距
|
|
||||||
make.centerY.equalToSuperview() // 可自定义
|
|
||||||
}
|
|
||||||
|
|
||||||
unlockButton.snp.makeConstraints { make in
|
|
||||||
make.height.equalTo(43)
|
|
||||||
}
|
|
||||||
|
|
||||||
adlockButton.snp.makeConstraints { make in
|
|
||||||
make.height.equalTo(43)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,320 +0,0 @@
|
|||||||
//
|
|
||||||
// SRVideoRechargeView.swift
|
|
||||||
// SynthReel
|
|
||||||
//
|
|
||||||
// Created by CSGY on 2025/12/5.
|
|
||||||
// Copyright © 2025 SR. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class SRVideoRechargeView: SRPanModalContentView {
|
|
||||||
|
|
||||||
var buyFinishHandle: (() -> Void)?
|
|
||||||
var didDismissHandle: (() -> Void)?
|
|
||||||
|
|
||||||
var model: SRPayDateModel? {
|
|
||||||
didSet {
|
|
||||||
self.stackView.sr_removeAllArrangedSubview()
|
|
||||||
self.vipView.dataArr = model?.list_sub_vip ?? []
|
|
||||||
self.coinsView.setDataArr(model?.list_coins ?? [])
|
|
||||||
|
|
||||||
if let sort = model?.sort, sort.count > 0 {
|
|
||||||
sort.forEach {
|
|
||||||
if $0 == .vip, model?.list_sub_vip?.isEmpty == false {
|
|
||||||
self.stackView.addArrangedSubview(self.vipView)
|
|
||||||
} else if $0 == .coin, model?.list_coins?.isEmpty == false {
|
|
||||||
self.stackView.addArrangedSubview(self.coinsView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if model?.list_sub_vip?.isEmpty == false {
|
|
||||||
self.stackView.addArrangedSubview(self.vipView)
|
|
||||||
}
|
|
||||||
if model?.list_coins?.isEmpty == false {
|
|
||||||
self.stackView.addArrangedSubview(self.coinsView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stackView.addArrangedSubview(self.tipView)
|
|
||||||
|
|
||||||
self.setNeedsLayoutUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var videoInfo: SRVideoInfoModel? {
|
|
||||||
didSet {
|
|
||||||
self.coinsView.videoId = videoInfo?.short_play_video_id
|
|
||||||
self.coinsView.shortPlayId = videoInfo?.short_play_id
|
|
||||||
self.vipView.videoId = videoInfo?.short_play_video_id
|
|
||||||
self.vipView.shortPlayId = videoInfo?.short_play_id
|
|
||||||
videoCoinsView.coins = videoInfo?.coins ?? 0
|
|
||||||
|
|
||||||
self.requestRestore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var closeButton: UIButton = {
|
|
||||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
// FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: self.videoInfo?.short_play_id, videoId: self.videoInfo?.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
|
|
||||||
// "event_name" : "pay cancel"
|
|
||||||
// ])
|
|
||||||
Task {
|
|
||||||
await self.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
self.didDismissHandle?()
|
|
||||||
}))
|
|
||||||
button.setImage(UIImage(named: "Close"), for: .normal)
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var scrollView: SRScrollView = {
|
|
||||||
let scrollView = SRScrollView()
|
|
||||||
// scrollView.clipsToBounds = false
|
|
||||||
return scrollView
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var stackView: UIStackView = {
|
|
||||||
let view = UIStackView()
|
|
||||||
view.axis = .vertical
|
|
||||||
view.spacing = 18
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var coinsView: SRStoreCoinsView = {
|
|
||||||
let view = SRStoreCoinsView()
|
|
||||||
view.buyFinishHandle = { [weak self] in
|
|
||||||
self?.buyFinishHandle?()
|
|
||||||
Task {
|
|
||||||
await self?.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var vipView: SRStoreVipView = {
|
|
||||||
let view = SRStoreVipView()
|
|
||||||
view.buyFinishHandle = { [weak self] in
|
|
||||||
self?.buyFinishHandle?()
|
|
||||||
Task {
|
|
||||||
await self?.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var videoCoinsView: SRVideoRechargeView.CoinsView = {
|
|
||||||
let view = SRVideoRechargeView.CoinsView()
|
|
||||||
view.title = "synthreel_price".localized + ":"
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var totalCoinsView: SRVideoRechargeView.CoinsView = {
|
|
||||||
let view = SRVideoRechargeView.CoinsView()
|
|
||||||
view.title = "synthreel_balance".localized + ":"
|
|
||||||
view.coins = SRLogin.manager.userInfo?.totalCoins ?? 0
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var tipView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.addSubview(tipTitleLabel)
|
|
||||||
view.addSubview(tipTextLabel)
|
|
||||||
|
|
||||||
tipTitleLabel.snp.makeConstraints { make in
|
|
||||||
make.top.equalToSuperview().offset(7)
|
|
||||||
make.left.equalToSuperview().offset(16)
|
|
||||||
}
|
|
||||||
|
|
||||||
tipTextLabel.snp.makeConstraints { make in
|
|
||||||
make.left.equalToSuperview().offset(16)
|
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-16)
|
|
||||||
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
|
|
||||||
make.bottom.equalToSuperview()
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var tipTitleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 12, weight: .medium)
|
|
||||||
label.textColor = .white
|
|
||||||
label.text = "synthreel_tips".localized
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var tipTextLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 10, weight: .regular)
|
|
||||||
label.textColor = .white
|
|
||||||
label.text = "store_tips".localized
|
|
||||||
label.numberOfLines = 0
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: SRLogin.userInfoUpdateNotification, object: nil)
|
|
||||||
self.contentHeight = UIScreen.height - UIScreen.safeTop
|
|
||||||
// self.backgroundColor = ._000000.withAlphaComponent(0.6)
|
|
||||||
self.backgroundColor = .clear
|
|
||||||
self.mainScrollView = self.scrollView
|
|
||||||
fa_setupLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func userInfoUpdateNotification() {
|
|
||||||
totalCoinsView.coins = SRLogin.manager.userInfo?.totalCoins ?? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override func present(in view: UIView?) {
|
|
||||||
super.present(in: view)
|
|
||||||
// self.hw_contentView.sr_addEffectView(style: .dark)
|
|
||||||
// self.hw_contentView.sr_setRoundedCorner(topLeft: 24, topRight: 24, bottomLeft: 0, bottomRight: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func allowsTapBackgroundToDismiss() -> Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRVideoRechargeView {
|
|
||||||
|
|
||||||
private func fa_setupLayout() {
|
|
||||||
addSubview(closeButton)
|
|
||||||
addSubview(videoCoinsView)
|
|
||||||
addSubview(totalCoinsView)
|
|
||||||
// addSubview(titleLabel)
|
|
||||||
addSubview(scrollView)
|
|
||||||
scrollView.addSubview(stackView)
|
|
||||||
|
|
||||||
closeButton.snp.makeConstraints { make in
|
|
||||||
make.right.equalToSuperview().offset(-16)
|
|
||||||
make.top.equalToSuperview().offset(12)
|
|
||||||
}
|
|
||||||
|
|
||||||
videoCoinsView.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalTo(closeButton)
|
|
||||||
make.left.equalToSuperview().offset(16)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalCoinsView.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalTo(closeButton)
|
|
||||||
make.left.equalTo(videoCoinsView.snp.right).offset(15)
|
|
||||||
}
|
|
||||||
|
|
||||||
// titleLabel.snp.makeConstraints { make in
|
|
||||||
// make.left.equalToSuperview().offset(16)
|
|
||||||
// make.top.equalToSuperview().offset(39)
|
|
||||||
// }
|
|
||||||
|
|
||||||
scrollView.snp.makeConstraints { make in
|
|
||||||
make.left.right.equalToSuperview()
|
|
||||||
make.top.equalToSuperview().offset(74)
|
|
||||||
make.bottom.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
stackView.snp.makeConstraints { make in
|
|
||||||
make.left.centerX.equalToSuperview()
|
|
||||||
make.top.equalToSuperview()
|
|
||||||
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SRVideoRechargeView {
|
|
||||||
|
|
||||||
@objc private func requestRestore() {
|
|
||||||
guard let shortPlayId = self.videoInfo?.short_play_id, let videoId = self.videoInfo?.short_play_video_id else { return }
|
|
||||||
|
|
||||||
SRIapManager.manager.restore(isLoding: false, shortPlayId: shortPlayId, videoId: videoId) { [weak self] isFinish, buyType in
|
|
||||||
if isFinish {
|
|
||||||
Task {
|
|
||||||
await SRLogin.manager.requestUserInfo(completer: nil)
|
|
||||||
self?.buyFinishHandle?()
|
|
||||||
await self?.dismiss(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension SRVideoRechargeView {
|
|
||||||
|
|
||||||
class CoinsView: UIView {
|
|
||||||
var title: String? {
|
|
||||||
didSet {
|
|
||||||
titleLabel.text = title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var coins: Int = 0 {
|
|
||||||
didSet {
|
|
||||||
coinsLabel.text = "\(coins)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var titleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 14, weight: .regular)
|
|
||||||
label.textColor = .white
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var iconImageView: UIImageView = {
|
|
||||||
let imageView = UIImageView(image: UIImage(named: "bigCoins"))
|
|
||||||
return imageView
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var coinsLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.font = .font(ofSize: 14, weight: .bold)
|
|
||||||
label.textColor = .white
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
addSubview(titleLabel)
|
|
||||||
addSubview(iconImageView)
|
|
||||||
addSubview(coinsLabel)
|
|
||||||
|
|
||||||
titleLabel.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.bottom.lessThanOrEqualToSuperview()
|
|
||||||
make.left.equalToSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
iconImageView.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.bottom.lessThanOrEqualToSuperview()
|
|
||||||
make.left.equalTo(titleLabel.snp.right).offset(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
coinsLabel.snp.makeConstraints { make in
|
|
||||||
make.centerY.equalToSuperview()
|
|
||||||
make.bottom.lessThanOrEqualToSuperview()
|
|
||||||
make.left.equalTo(iconImageView.snp.right).offset(4)
|
|
||||||
make.right.equalToSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -35,7 +35,7 @@ class SRBaseAlert: UIView {
|
|||||||
|
|
||||||
private(set) lazy var closeButton: UIButton = {
|
private(set) lazy var closeButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setImage(UIImage(named: "close_icon_03"), for: .normal)
|
button.setImage(UIImage(named: "Close"), for: .normal)
|
||||||
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
|
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -85,6 +85,7 @@
|
|||||||
"synthreel_coin_bag_buy_tip_title" = "How Do I Receive Coins?";
|
"synthreel_coin_bag_buy_tip_title" = "How Do I Receive Coins?";
|
||||||
"synthreel_continue" = "Claim Now";
|
"synthreel_continue" = "Claim Now";
|
||||||
"vip_retain_alert_text" = "Unlock every show you love!";
|
"vip_retain_alert_text" = "Unlock every show you love!";
|
||||||
|
"vip_tip_01" = "Unlimited access to all series";
|
||||||
|
|
||||||
"coins_pack_tips" = "1.Coins are delivered instantly upon purchase.<br>2.Daily bonus coins available from the next day.<br>3.All coins will be revoked when the subscription expires, including both initial and daily coins.";
|
"coins_pack_tips" = "1.Coins are delivered instantly upon purchase.<br>2.Daily bonus coins available from the next day.<br>3.All coins will be revoked when the subscription expires, including both initial and daily coins.";
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user