充值会员页面开发

This commit is contained in:
zeng 2025-04-29 11:08:49 +08:00
parent 490dc77b1e
commit c3944c5342
63 changed files with 1402 additions and 5 deletions

View File

@ -25,6 +25,12 @@ extension String: SmartCodable {
return String(format: "GMT+0%d:00", timeZoneSecondsFromGMT)
}
///
func capitalizingFirstLetter() -> String {
guard let first = self.first else { return self }
return first.uppercased() + self.dropFirst()
}
}
extension String {

View File

@ -256,5 +256,113 @@ extension UIColor {
static func colorF2C879(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xF2C879, alpha: alpha)
}
static func color202531(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x202531, alpha: alpha)
}
static func colorFDE9A6(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFDE9A6, alpha: alpha)
}
static func colorFFBD39(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFFBD39, alpha: alpha)
}
static func colorFFAC38(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFFAC38, alpha: alpha)
}
static func color2B2826(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x2B2826, alpha: alpha)
}
static func color100F0B(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x100F0B, alpha: alpha)
}
static func colorFEE3B5(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFEE3B5, alpha: alpha)
}
static func colorFCCE7D(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFCCE7D, alpha: alpha)
}
static func color2AAED3(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x2AAED3, alpha: alpha)
}
static func color9CD5E5(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x9CD5E5, alpha: alpha)
}
static func colorFAFCFE(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFAFCFE, alpha: alpha)
}
static func colorCED6FA(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xCED6FA, alpha: alpha)
}
static func color0588DB(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x0588DB, alpha: alpha)
}
static func colorA9E1F2(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xA9E1F2, alpha: alpha)
}
static func colorF2F4F4(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xF2F4F4, alpha: alpha)
}
static func colorC8E1E8(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xC8E1E8, alpha: alpha)
}
static func color6B3308(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x6B3308, alpha: alpha)
}
static func colorFFB559(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFFB559, alpha: alpha)
}
static func color9F5300(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x9F5300, alpha: alpha)
}
static func color0D4E64(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x0D4E64, alpha: alpha)
}
static func color020926(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x020926, alpha: alpha)
}
static func colorEF7301(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xEF7301, alpha: alpha)
}
static func colorFFCF93(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFFCF93, alpha: alpha)
}
static func colorFFF0DE(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFFF0DE, alpha: alpha)
}
static func colorFF1A35(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFF1A35, alpha: alpha)
}
static func colorFF569C(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFF569C, alpha: alpha)
}
static func colorFF8E33(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xFF8E33, alpha: alpha)
}
}

View File

@ -11,7 +11,7 @@ class SPRewardsAPI: NSObject {
///
static func requestUploadOpenNotify(completer: ((_ finish: Bool) -> Void)?) {
var param = SPNetworkParameters(path: "openNotify")
let param = SPNetworkParameters(path: "openNotify")
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<String>) in
if response.code == SPNetworkCodeSucceed {

View File

@ -0,0 +1,23 @@
//
// SPWalletAPI.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPWalletAPI: NSObject {
///
static func requestPayTemplate(completer: ((_ model: SPPayTemplateModel?) -> Void)?) {
var param = SPNetworkParameters(path: "/paySettingsV3")
param.method = .get
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPPayTemplateModel>) in
completer?(response.data)
}
}
}

View File

@ -122,7 +122,9 @@ extension SPMineViewController: UITableViewDelegate, UITableViewDataSource {
let vc = SPFeedbackViewController()
self.navigationController?.pushViewController(vc, animated: true)
// case .settings:
case .settings:
let vc = SPSettingsViewController()
self.navigationController?.pushViewController(vc, animated: true)
default:

View File

@ -0,0 +1,76 @@
//
// SPSettingsViewController.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPSettingsViewController: SPViewController {
//MARK: UI
private lazy var tableView: SPTableView = {
let tableView = SPTableView(frame: .zero, style: .insetGrouped)
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.rowHeight = 48
tableView.sectionFooterHeight = 0
SPSettingsCell.registerCell(tableView: tableView)
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.setBackgroundView(isShowGradient: false, bgImage: nil)
self.title = "Settings".localized
_setupUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.setNavigationNormalStyle()
}
}
extension SPSettingsViewController {
private func _setupUI() {
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
//MARK: -------------- UITableViewDelegate & UITableViewDataSource --------------
extension SPSettingsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = SPSettingsCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 12
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
}

View File

@ -0,0 +1,23 @@
//
// SPSettingsCell.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPSettingsCell: SPTableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.backgroundColor = .color202531()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -11,10 +11,64 @@ class SPPlayBuyView: HWPanModalContentView {
//MARK: UI
private lazy var bgView: UIImageView = {
let view = UIImageView(image: UIImage(named: "buy_bg_image_01"))
return view
}()
private lazy var indicatorView: UIView = {
let view = UIView()
view.backgroundColor = .colorFFFFFF(alpha: 0.4)
view.layer.cornerRadius = 2.5
view.layer.masksToBounds = true
return view
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .fontMedium(ofSize: 16)
label.textColor = .colorFFFFFF()
label.text = "Stroe".localized
return label
}()
private lazy var restoreButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("Restore".localized, for: .normal)
button.setTitleColor(.colorFFFFFF(alpha: 0.5), for: .normal)
button.titleLabel?.font = .fontRegular(ofSize: 14)
return button
}()
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
return scrollView
}()
private lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 16
return view
}()
private lazy var rechargeView: SPCoinRechargeView = {
let view = SPCoinRechargeView()
return view
}()
private lazy var memberView: SPMemberRechargeView = {
let view = SPMemberRechargeView()
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .red
_setupUI()
requestPayTemplate()
}
required init?(coder: NSCoder) {
@ -24,7 +78,7 @@ class SPPlayBuyView: HWPanModalContentView {
//MARK: HWPanModalPresentable
override func panScrollable() -> UIScrollView? {
return nil
return scrollView
}
override func longFormHeight() -> PanModalHeight {
@ -41,3 +95,71 @@ class SPPlayBuyView: HWPanModalContentView {
return config
}
}
extension SPPlayBuyView {
private func _setupUI() {
addSubview(bgView)
addSubview(indicatorView)
addSubview(titleLabel)
addSubview(restoreButton)
addSubview(scrollView)
scrollView.addSubview(stackView)
bgView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
indicatorView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(8)
make.width.equalTo(40)
make.height.equalTo(5)
}
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview().offset(30)
}
restoreButton.snp.makeConstraints { make in
make.centerY.equalTo(titleLabel)
make.right.equalToSuperview().offset(-16)
}
scrollView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(76)
make.left.right.equalToSuperview()
make.bottom.equalToSuperview()
}
stackView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.bottom.equalTo(-(kSPTabbarSafeBottomMargin + 10))
make.width.equalTo(kSPScreenWidth)
}
}
}
extension SPPlayBuyView {
///
private func requestPayTemplate() {
SPWalletAPI.requestPayTemplate { [weak self] templateModel in
guard let self = self else { return }
self.rechargeView.dataArr = templateModel?.list_coins
self.memberView.dataArr = templateModel?.list_sub_vip
self.stackView.removeAllArrangedSubview()
self.stackView.addArrangedSubview(self.rechargeView)
self.stackView.addArrangedSubview(self.memberView)
self.panModalSetNeedsLayoutUpdate()
}
}
}

View File

@ -0,0 +1,61 @@
//
// SPPayTemplateItem.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
import SmartCodable
class SPPayTemplateItem: SPModel, SmartCodable {
enum BuyType: String, SmartCaseDefaultable {
case coins = "coins"
case subVip = "sub_vip"
}
enum VipTypeKey: String, SmartCaseDefaultable {
case week = "week"
case month = "month"
case quarter = "quarter"
case year = "year"
}
var id: String?
var status: String?
var price: String?
var origin_price: String?
var backhaul_price: String?
var coins: Int?
var send_coins: Int?
///coins sub_vip
var buy_type: BuyType?
var sort: String?
var title: String?
var brief: String?
var sp_description: String?
var vip_type: String?
var vip_type_key: VipTypeKey?
var ios_template_id: String?
///
var currency: String?
///
var platform: String?
///
var corner_marker: String?
static func mappingForKey() -> [SmartKeyTransformer]? {
return [
CodingKeys.sp_description <--- ["description"]
]
}
}

View File

@ -0,0 +1,15 @@
//
// SPPayTemplateModel.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
import SmartCodable
class SPPayTemplateModel: SPModel, SmartCodable {
var list_coins: [SPPayTemplateItem]?
var list_sub_vip: [SPPayTemplateItem]?
}

View File

@ -0,0 +1,130 @@
//
// SPCoinRechargeCell.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPCoinRechargeCell: SPCollectionViewCell {
var sp_isSelected = false {
didSet {
if sp_isSelected {
containerView.image = UIImage(named: "recharge_bg_image_01")
moneyLabel.backgroundColor = .colorFFF0DE(alpha: 0.1)
} else {
containerView.image = UIImage(named: "recharge_bg_image_02")
moneyLabel.backgroundColor = .color000000(alpha: 0.8)
}
}
}
var model: SPPayTemplateItem? {
didSet {
coinLabel.text = "\(model?.coins ?? 0)"
bonusLabel.text = String(format: "%@ Bonus".localized, "+\(model?.send_coins ?? 0)")
moneyLabel.text = "\(model?.currency ?? "")\(model?.price ?? "0")"
}
}
//MARK: UI
private lazy var containerView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
private lazy var coinBgView: UIView = {
let view = UIView()
return view
}()
private lazy var coinIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_icon_04"))
return imageView
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = .fontBold(ofSize: 20)
label.textColor = .colorFFFFFF()
return label
}()
private lazy var bonusLabel: UILabel = {
let label = UILabel()
label.font = .fontMedium(ofSize: 14)
label.textColor = .colorEF7301()
return label
}()
private lazy var moneyLabel: UILabel = {
let label = UILabel()
label.layer.cornerRadius = 10
label.layer.masksToBounds = true
label.textAlignment = .center
label.font = .fontMedium(ofSize: 14)
label.textColor = .colorFFCF93()
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
_setupUI()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SPCoinRechargeCell {
private func _setupUI() {
contentView.addSubview(containerView)
containerView.addSubview(coinBgView)
coinBgView.addSubview(coinIconImageView)
coinBgView.addSubview(coinLabel)
containerView.addSubview(bonusLabel)
containerView.addSubview(moneyLabel)
containerView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
coinBgView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(24)
make.height.equalTo(25)
}
coinIconImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview()
}
coinLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalTo(coinIconImageView.snp.right).offset(4)
make.right.equalToSuperview()
}
bonusLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(coinBgView.snp.bottom).offset(8)
}
moneyLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(2)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-2)
make.height.equalTo(30)
}
}
}

View File

@ -0,0 +1,130 @@
//
// SPCoinRechargeView.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPCoinRechargeView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: kSPScreenWidth, height: 20 + 14 + 125)
}
private lazy var currentIndexPath: IndexPath = .init(row: 0, section: 0)
var dataArr: [SPPayTemplateItem]? {
didSet {
self.collectionView.reloadData()
}
}
//MARK: UI
private lazy var coinNameLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 14)
label.textColor = .colorFFFFFF(alpha: 0.7)
label.text = "Coins Balance:"
return label
}()
private lazy var coinIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_icon_04"))
return imageView
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 14)
label.textColor = .colorFFFFFF()
return label
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 8
layout.minimumInteritemSpacing = 8
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
layout.itemSize = CGSize(width: 120, height: 125)
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
SPCoinRechargeCell.registerCell(collectionView: collectionView)
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
coinLabel.text = "0"
_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SPCoinRechargeView {
private func _setupUI() {
addSubview(coinNameLabel)
addSubview(coinIconImageView)
addSubview(coinLabel)
addSubview(collectionView)
coinNameLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview()
make.height.equalTo(20)
}
coinIconImageView.snp.makeConstraints { make in
make.centerY.equalTo(coinNameLabel)
make.left.equalTo(coinNameLabel.snp.right).offset(4)
}
coinLabel.snp.makeConstraints { make in
make.centerY.equalTo(coinNameLabel)
make.left.equalTo(coinIconImageView.snp.right).offset(4)
}
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(125)
}
}
}
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
extension SPCoinRechargeView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = SPCoinRechargeCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
cell.sp_isSelected = indexPath == currentIndexPath
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) {
currentIndexPath = indexPath
collectionView.reloadData()
}
}

View File

@ -0,0 +1,265 @@
//
// SPMemberRechargeCell.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPMemberRechargeCell: SPCollectionViewCell {
var model: SPPayTemplateItem? {
didSet {
desLabel.text = model?.sp_description
typeLabel.text = model?.vip_type_key?.rawValue.capitalizingFirstLetter()
currencyLabel.text = model?.currency
moneyLabel.text = model?.price
if let sendCoins = model?.send_coins, sendCoins > 0 {
markBgView.isHidden = false
sendCoinLabel.text = String(format: "+ Extra %@".localized, "\(sendCoins)")
} else {
markBgView.isHidden = true
}
switch model?.vip_type_key {
case .year:
vipImageView.image = UIImage(named: "vip_image_01")
downBgView.colors = [UIColor.colorFDE9A6().cgColor, UIColor.colorFFAC38().cgColor]
topBgView.colors = [UIColor.color2B2826().cgColor, UIColor.color100F0B().cgColor]
desLabel.textColor = .color6B3308()
iconImageView.image = UIImage(named: "vip_icon_03")
typeLabel.textColor = .colorFFB559()
typeIconImageView.image = UIImage(named: "vip_icon_05")
case .quarter:
vipImageView.image = UIImage(named: "vip_image_02")
downBgView.colors = [UIColor.colorFDE9A6().cgColor, UIColor.colorFFAC38().cgColor]
topBgView.colors = [UIColor.colorFEE3B5().cgColor, UIColor.colorFCCE7D().cgColor]
desLabel.textColor = .color6B3308()
iconImageView.image = UIImage(named: "vip_icon_03")
typeLabel.textColor = .color9F5300()
typeIconImageView.image = UIImage(named: "vip_icon_06")
case .month:
vipImageView.image = UIImage(named: "vip_image_03")
downBgView.colors = [UIColor.color2AAED3().cgColor, UIColor.color9CD5E5().cgColor]
topBgView.colors = [UIColor.colorF2F4F4().cgColor, UIColor.colorC8E1E8().cgColor]
desLabel.textColor = .colorFFFFFF()
iconImageView.image = UIImage(named: "vip_icon_04")
typeLabel.textColor = .color0D4E64()
typeIconImageView.image = UIImage(named: "vip_icon_07")
case .week:
vipImageView.image = UIImage(named: "vip_image_04")
downBgView.colors = [UIColor.color0588DB().cgColor, UIColor.colorA9E1F2().cgColor]
topBgView.colors = [UIColor.colorFAFCFE().cgColor, UIColor.colorCED6FA().cgColor]
desLabel.textColor = .colorFFFFFF()
iconImageView.image = UIImage(named: "vip_icon_04")
typeLabel.textColor = .color020926()
typeIconImageView.image = UIImage(named: "vip_icon_08")
default:
break
}
currencyLabel.textColor = typeLabel.textColor
moneyLabel.textColor = typeLabel.textColor
tipLabel.textColor = typeLabel.textColor
}
}
//MARK: UI
private lazy var downBgView: SPGradientView = {
let view = SPGradientView()
view.locations = [0, 1]
view.startPoint = .init(x: 0, y: 0.5)
view.endPoint = .init(x: 1, y: 0.5)
view.layer.cornerRadius = 12
view.layer.masksToBounds = true
return view
}()
private lazy var topBgView: SPGradientView = {
let view = SPGradientView()
view.locations = [0, 1]
view.startPoint = .init(x: 0, y: 0.5)
view.endPoint = .init(x: 1, y: 0.5)
view.layer.cornerRadius = 11
view.layer.masksToBounds = true
return view
}()
private lazy var vipImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
private lazy var desLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 12)
return label
}()
private lazy var typeLabel: UILabel = {
let label = UILabel()
label.font = .fontMedium(ofSize: 16)
return label
}()
private lazy var typeIconImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
private lazy var currencyLabel: UILabel = {
let label = UILabel()
label.font = .fontBold(ofSize: 32)
return label
}()
private lazy var moneyLabel: UILabel = {
let label = UILabel()
label.font = .fontBold(ofSize: 32)
return label
}()
private lazy var tipLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 11)
label.text = "kBuyMemberTipText".localized
return label
}()
private lazy var markBgView: SPGradientView = {
let view = SPGradientView()
view.colors = [UIColor.colorFF1A35().cgColor, UIColor.colorFF569C().cgColor, UIColor.colorFF8E33().cgColor]
view.locations = [0, 0.5, 1]
view.startPoint = .init(x: 0, y: 0.5)
view.endPoint = .init(x: 1, y: 0.5)
view.addRadius(topLeft: 0, topRight: 0, bottomLeft: 11, bottomRight: 0)
return view
}()
private lazy var sendCoinLabel: UILabel = {
let label = UILabel()
label.font = .fontBold(ofSize: 11)
label.textColor = .colorFFFFFF()
return label
}()
private lazy var coinImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_icon_05"))
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
_setupUI()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SPMemberRechargeCell {
private func _setupUI() {
contentView.addSubview(downBgView)
downBgView.addSubview(iconImageView)
downBgView.addSubview(desLabel)
contentView.addSubview(topBgView)
topBgView.addSubview(vipImageView)
topBgView.addSubview(typeLabel)
topBgView.addSubview(typeIconImageView)
topBgView.addSubview(currencyLabel)
topBgView.addSubview(moneyLabel)
topBgView.addSubview(tipLabel)
topBgView.addSubview(markBgView)
markBgView.addSubview(sendCoinLabel)
markBgView.addSubview(coinImageView)
downBgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(17)
make.bottom.equalToSuperview().offset(-7)
}
desLabel.snp.makeConstraints { make in
make.centerY.equalTo(iconImageView)
make.left.equalTo(iconImageView.snp.right).offset(4)
}
topBgView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(1)
make.right.equalToSuperview().offset(-1)
make.top.equalToSuperview().offset(1)
make.bottom.equalToSuperview().offset(-25)
}
vipImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-7)
}
typeLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(kSPMainW(23))
make.top.equalToSuperview().offset(19)
}
typeIconImageView.snp.makeConstraints { make in
make.centerY.equalTo(typeLabel)
make.left.equalTo(typeLabel.snp.right).offset(3)
}
currencyLabel.snp.makeConstraints { make in
make.left.equalTo(typeLabel)
make.top.equalTo(typeLabel.snp.bottom).offset(8)
}
moneyLabel.snp.makeConstraints { make in
make.centerY.equalTo(currencyLabel)
make.left.equalTo(currencyLabel.snp.right).offset(5)
}
tipLabel.snp.makeConstraints { make in
make.left.equalTo(typeLabel)
make.top.equalTo(moneyLabel.snp.bottom).offset(8)
}
markBgView.snp.makeConstraints { make in
make.top.right.equalToSuperview()
make.height.equalTo(18)
}
sendCoinLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(5)
}
coinImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalTo(sendCoinLabel.snp.right).offset(2)
make.right.equalToSuperview().offset(-5)
}
}
}

View File

@ -0,0 +1,101 @@
//
// SPMemberRechargeView.swift
// MoviaBox
//
// Created by on 2025/4/28.
//
import UIKit
class SPMemberRechargeView: UIView {
override var intrinsicContentSize: CGSize {
let count = CGFloat(dataArr?.count ?? 0)
let height = 32 + count * collectionViewLayout.itemSize.height + (count - 1) * collectionViewLayout.minimumInteritemSpacing
return CGSize(width: kSPScreenWidth, height: height)
}
var dataArr: [SPPayTemplateItem]? {
didSet {
self.invalidateIntrinsicContentSize()
self.collectionView.reloadData()
}
}
//MARK: UI
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 14)
label.textColor = .colorFFFFFF(alpha: 0.7)
label.text = "Membership".localized
return label
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
layout.itemSize = CGSize(width: kSPScreenWidth - 32, height: 152)
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 10
return layout
}()
private lazy var collectionView: SPCollectionView = {
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.isScrollEnabled = false
collectionView.delegate = self
collectionView.dataSource = self
SPMemberRechargeCell.registerCell(collectionView: collectionView)
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SPMemberRechargeView {
private func _setupUI() {
addSubview(titleLabel)
addSubview(collectionView)
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.height.equalTo(20)
make.top.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(32)
}
}
}
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
extension SPMemberRechargeView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = SPMemberRechargeCell.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) {
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 1912056653@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 1912056653@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "image 21@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "image 21@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Subtract@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Subtract@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Subtract@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Subtract@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Union@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Union@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Union@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Union@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Union@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Union@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Union@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Union@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Stroe@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Stroe@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 1912056629@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 1912056629@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame 1912056630@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame 1912056630@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "分组 3@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "分组 3@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "分组 2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "分组 2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "分组 2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "分组 2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "分组 2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "分组 2@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -64,8 +64,13 @@
"Consumption records" = "Consumption records";
"Purchase records" = "Purchase records";
"Reward Coins" = "Reward Coins";
"Stroe" = "Stroe";
"Coins Balance:" = "Coins Balance:";
"Membership" = "Membership";
"%@ Bonus" = "%@ Bonus";
"+ Extra %@" = "+ Extra %@";
"kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy";
"kBuyMemberTipText" = "Auto renew · Cancel anytime";