充值会员相关UI开发

This commit is contained in:
zeng 2025-04-29 13:27:40 +08:00
parent c3944c5342
commit 05e80f50e2
9 changed files with 399 additions and 3 deletions

View File

@ -17,6 +17,8 @@ extension AppDelegate {
SPToast.config()
SPHUD.config()
UIBarButtonItem.appearance().sp_setTitleTextAttributes()
}
@ -70,8 +72,6 @@ extension AppDelegate {
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance
}
}
}

View File

@ -0,0 +1,36 @@
//
// UIBarButtonItem+SPAdd.swift
// MoviaBox
//
// Created by on 2025/4/29.
//
import UIKit
extension UIBarButtonItem {
static let sp_normalTitleFont = UIFont.fontRegular(ofSize: 14)
static var sp_normalTitleColor: UIColor {
get {
return UIColor.colorFFFFFF(alpha: 0.5)
}
}
/**
*/
static var sp_normalTitleTextAttributes: [NSAttributedString.Key : Any] {
get {
return [
NSAttributedString.Key.font : sp_normalTitleFont,
NSAttributedString.Key.foregroundColor : sp_normalTitleColor
]
}
}
func sp_setTitleTextAttributes(normalAttributes: [NSAttributedString.Key : Any] = UIBarButtonItem.sp_normalTitleTextAttributes) {
self.setTitleTextAttributes(normalAttributes, for: .normal)
self.setTitleTextAttributes(normalAttributes, for: .highlighted)
}
}

View File

@ -94,7 +94,7 @@ class SPMineWalletView: UIView {
extension SPMineWalletView {
@objc private func handleStoreButton() {
let vc = SPWalletViewController()
let vc = SPStoreViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}

View File

@ -0,0 +1,138 @@
//
// SPStoreViewController.swift
// MoviaBox
//
// Created by on 2025/4/29.
//
import UIKit
class SPStoreViewController: SPViewController {
//MARK: UI
private lazy var scrollView: SPScrollView = {
let scrollView = SPScrollView()
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
}()
private lazy var tipTitleLabel: UILabel = {
let label = UILabel()
label.font = .fontMedium(ofSize: 12)
label.textColor = .colorFFFFFF(alpha: 0.7)
return label
}()
private lazy var tipTextLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .colorFFFFFF(alpha: 0.5)
label.font = .fontRegular(ofSize: 12)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Store".localized
self.edgesForExtendedLayout = .top
setBackgroundView(isShowGradient: false, bgImage: UIImage(named: "buy_bg_image_02"))
let rightBarButton = UIBarButtonItem(title: "Restore".localized, style: .plain, target: self, action: #selector(handelRightBarButton))
self.navigationItem.rightBarButtonItem = rightBarButton
tipTitleLabel.text = "kStoreTipTitle".localized
tipTextLabel.text = "kStoreTipText".localized
_setupUI()
requestPayTemplate()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
setNavigationNormalStyle(backgroundColor: .clear, isTranslucent: true)
}
}
extension SPStoreViewController {
@objc private func handelRightBarButton() {
}
}
extension SPStoreViewController {
private func _setupUI() {
view.addSubview(scrollView)
scrollView.addSubview(stackView)
scrollView.addSubview(tipTitleLabel)
scrollView.addSubview(tipTextLabel)
scrollView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(kSPNavBarHeight)
}
stackView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
// make.bottom.equalTo(-(kSPTabbarSafeBottomMargin + 10))
make.width.equalTo(kSPScreenWidth)
}
tipTitleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(24)
make.top.equalTo(stackView.snp.bottom).offset(34)
}
tipTextLabel.snp.makeConstraints { make in
make.left.equalTo(tipTitleLabel)
make.right.lessThanOrEqualTo(stackView).offset(-24)
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
make.bottom.equalTo(-(kSPTabbarSafeBottomMargin + 10))
}
}
}
extension SPStoreViewController {
///
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.memberView)
self.stackView.addArrangedSubview(self.rechargeView)
}
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -73,4 +73,6 @@
"kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy";
"kBuyMemberTipText" = "Auto renew · Cancel anytime";
"kStoreTipTitle" = "Related terms and conditions:";
"kStoreTipText" = "1. Coins can only be used within this application.2. Payment: The purchase will be charged to your iTunes account. 3. Renewal: Your Apple iTunes account will be charged within 24 hours before the expiration and the subscription period will be extended for another subscription cycle upon successful deduction.4. Cancellation: To cancel the subscription renewal, please turn off the automatic renewal function in the iTunes/Apple ID settings at least 24 hours before the current subscription period expires. If canceled within the last 24 hours before expiration, a subscription fee will still be charged. 5. Payment successful but recharge not taking effect for an extended period? Click here to refresh or send an email to: cs.jiaer.developer@icloud.com. 6. Manage your subscriptions: You can view, change, or cancel your subscriptions. Terms of Service | Privacy Policy Renewal Agreement";

View File

@ -0,0 +1,198 @@
//
// JXIAPManager.swift
// BoJia
//
// Created by on 2024/6/3.
//
import UIKit
import StoreKit
@objc protocol JXIAPManagerDelegate {
///
@objc optional func jx_iapPayGotProducts(productIds: [String])
///
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?)
///
@objc optional func jx_iapPayFailed(productId: String, code: JXIAPManagerCode)
///
@objc optional func iapPayRestore(productIds: [String], transactionIds: [String])
// ///
// @objc optional func iapPayShowHud()
// ///
// @objc optional func iapSysWrong()
// ///
// @objc optional func verifySuccess()
// ///
// @objc optional func verifyFailed()
}
@objc enum JXIAPManagerCode: Int {
///
case unknown
///
case cancelled
///
case noProduct
}
class JXIAPManager: NSObject {
static let manager: JXIAPManager = JXIAPManager()
weak var delegate: JXIAPManagerDelegate?
private var payment: SKPayment?
private var product: SKProduct?
private var productId: String = ""
private var orderId: String?
private var applicationUsername: String? {
get {
let id = "00000000-0000-0000-0000-000000000000"
guard let orderId = orderId else { return nil }
var string = ""
for i in 0..<orderId.length {
if i == 12 || i == 16 || i == 20 || i == 24 {
string.insert("-", at: string.startIndex)
}
let s = orderId[orderId.index(orderId.endIndex, offsetBy: -(i + 1))]
string.insert(s, at: string.startIndex)
}
let length = id.length
let stringLength = string.length
if stringLength <= length {
let range = NSRange(location: length - string.length, length: string.length)
return id.ocString.replacingCharacters(in: range, with: string)
} else {
return string
}
}
}
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
func start(productId: String, orderId: String) {
self.product = nil
self.productId = productId
self.orderId = orderId
let set = Set([productId])
let productsRequest = SKProductsRequest(productIdentifiers: set)
productsRequest.delegate = self
productsRequest.start()
}
///
private func buyProduct() {
guard let product = self.product else { return }
//
let payment = SKMutablePayment(product: product)
payment.applicationUsername = applicationUsername
tmLog(message: payment.applicationUsername)
self.payment = payment
//
SKPaymentQueue.default().add(payment)
}
}
//MARK: -------------- SKProductsRequestDelegate --------------
extension JXIAPManager: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
guard let product = response.products.first else {
DispatchQueue.main.async {
self.delegate?.jx_iapPayFailed?(productId: self.productId, code: .noProduct)
}
return
}
self.product = product
self.buyProduct()
}
}
//MARK: -------------- SKPaymentTransactionObserver --------------
extension JXIAPManager: SKPaymentTransactionObserver {
///
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
DispatchQueue.main.async {
self.completeTransaction(transaction: transaction)
}
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
DispatchQueue.main.async {
self.failedTransaction(transaction: transaction)
}
SKPaymentQueue.default().finishTransaction(transaction)
// case .restored:
// self.restoreTransaction(transaction: transaction)
case .purchasing:
break
default:
SKPaymentQueue.default().finishTransaction(transaction)
break
}
}
}
// func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
// return true
// }
///
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
}
}
extension JXIAPManager {
private func completeTransaction(transaction: SKPaymentTransaction) {
//
if let _ = transaction.original, transaction.payment.applicationUsername == nil {
return
}
//
if let _ = transaction.original, transaction.payment.applicationUsername != nil {
self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown)
return
}
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return }
let receiptData = NSData(contentsOf: receiptURL)
guard let encodeStr = receiptData?.base64EncodedString(options: .endLineWithLineFeed) else { return }
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
}
private func failedTransaction(transaction: SKPaymentTransaction) {
let error = transaction.error as? SKError
switch error?.code {
case SKError.paymentCancelled:
self.delegate?.jx_iapPayFailed?(productId: productId, code: .cancelled)
default:
self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown)
}
}
}