Commit 855a80da authored by shenyong's avatar shenyong

年费会员界面,启动引导页结构更改

parent 5e825e99
...@@ -27,13 +27,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -27,13 +27,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
window?.backgroundColor = .white window?.backgroundColor = .white
window?.overrideUserInterfaceStyle = .light window?.overrideUserInterfaceStyle = .light
let Ssoryboard = UIStoryboard(name: "LauchVC", bundle: nil)
if let current = Ssoryboard.instantiateViewController(identifier: "LauchVCID") as? LauchVC { let rootNav = BaseNavViewController(rootViewController: HomeViewController())
window?.rootViewController = rootNav
window?.rootViewController = current window?.makeKeyAndVisible()
window?.makeKeyAndVisible()
} // let Ssoryboard = UIStoryboard(name: "LauchVC", bundle: nil)
//
// if let current = Ssoryboard.instantiateViewController(identifier: "LauchVCID") as? LauchVC {
//
// window?.rootViewController = current
// window?.makeKeyAndVisible()
// }
let battery = WidgetPublicModel.battery() let battery = WidgetPublicModel.battery()
let storage = WidgetPublicModel.UseDiskSpace() * 100 let storage = WidgetPublicModel.UseDiskSpace() * 100
widgetAppgourp.share.PushWidgetData(battery: Int(battery), storage: Int(storage)) widgetAppgourp.share.PushWidgetData(battery: Int(battery), storage: Int(storage))
...@@ -51,6 +57,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -51,6 +57,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// 相册基本资源加载 // 相册基本资源加载
PhotoManager.shared.config() PhotoManager.shared.config()
// 设置app动态按钮
setupDynamicShortcuts()
return true return true
} }
......
//
// AppDelegateEx.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import Foundation
import UIKit
extension AppDelegate{
func setupDynamicShortcuts() {
let shortcutItem = UIApplicationShortcutItem(
type: "com.app.phonemanager.iap.distance",
localizedTitle: "Unlock Exclusive Discounts",
localizedSubtitle: "Your special offer is awaiting—don't miss this limited-time second chance benefit!",
icon: UIApplicationShortcutIcon(systemImageName: "star.fill"),
userInfo: nil
)
UIApplication.shared.shortcutItems = [shortcutItem]
}
// 处理快捷方式点击
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
// 处理快捷方式点击事件
switch shortcutItem.type {
case "com.app.phonemanager.iap.distance":
// 执行相应操作
let vc = PayDistanceViewController()
vc.modalPresentationStyle = .fullScreen
GETCURRENTNAV()?.present(vc, animated: false)
completionHandler(true)
default:
completionHandler(false)
}
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame_1171276222@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame_1171276222@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
...@@ -48,7 +48,8 @@ class IAPManager: NSObject { ...@@ -48,7 +48,8 @@ class IAPManager: NSObject {
private let alert = PMLoadingHUD.share private let alert = PMLoadingHUD.share
enum PayState { enum PayState {
case subscribe case weekSubscribe
case yearSubscribe
case nonConsumable case nonConsumable
} }
...@@ -56,9 +57,10 @@ class IAPManager: NSObject { ...@@ -56,9 +57,10 @@ class IAPManager: NSObject {
private struct ProductID { private struct ProductID {
static let weekMember = "com.app.phonemanager.week.member" static let weekMember = "com.app.phonemanager.week.member"
static let lifetimeMember = "com.app.phonemanager.lifetime.member" static let lifetimeMember = "com.app.phonemanager.lifetime.member"
static let yearMember = "com.app.phonemanager.year.member"
static var all: [String] { static var all: [String] {
return [weekMember, lifetimeMember] return [weekMember, lifetimeMember,yearMember]
} }
} }
...@@ -66,14 +68,16 @@ class IAPManager: NSObject { ...@@ -66,14 +68,16 @@ class IAPManager: NSObject {
private var weekProduct: SKProduct? private var weekProduct: SKProduct?
// 永久订阅 // 永久订阅
private var lifetimeProduct: SKProduct? private var lifetimeProduct: SKProduct?
// 年费订阅
private var yearProduct: SKProduct?
private var state: PayState = .subscribe private var state: PayState = .weekSubscribe
//购买操作的完成回调 //购买操作的完成回调
private var purchaseCompletion: ((Result<Bool, IAPError>) -> Void)? private var purchaseCompletion: ((Result<Bool, IAPError>) -> Void)?
//恢复购买的完成回调 //恢复购买的完成回调
private var restoreCompletion: ((Result<Bool, IAPError>) -> Void)? private var restoreCompletion: ((Result<Bool, IAPError>) -> Void)?
//获取商品信息的完成回调 //获取商品信息的完成回调
private var productRequestCompletion: (((SKProduct?,SKProduct?)?) -> Void)? private var productRequestCompletion: (((SKProduct?,SKProduct?,SKProduct?)?) -> Void)?
var isSubscribed = false { var isSubscribed = false {
didSet { didSet {
...@@ -98,15 +102,16 @@ class IAPManager: NSObject { ...@@ -98,15 +102,16 @@ class IAPManager: NSObject {
extension IAPManager { extension IAPManager {
// 获取订阅内购商品信息 // 获取订阅内购商品信息
func fetchProducts(completion: @escaping ((SKProduct?,SKProduct?)?) -> Void) { func fetchProducts(completion: @escaping ((SKProduct?,SKProduct?,SKProduct?)?) -> Void) {
// 先检查缓存 // 先检查缓存
if let cachedProducts = getCachedProducts() { if let cachedProducts = getCachedProducts() {
// 后台更新最新数据 // 后台更新最新数据
self.weekProduct = cachedProducts.filter{$0.productIdentifier == ProductID.weekMember}.first self.weekProduct = cachedProducts.filter{$0.productIdentifier == ProductID.weekMember}.first
self.lifetimeProduct = cachedProducts.filter{$0.productIdentifier == ProductID.lifetimeMember}.first self.lifetimeProduct = cachedProducts.filter{$0.productIdentifier == ProductID.lifetimeMember}.first
completion((self.weekProduct,self.lifetimeProduct)) self.yearProduct = cachedProducts.filter{$0.productIdentifier == ProductID.yearMember}.first
completion((self.weekProduct,self.lifetimeProduct,self.yearProduct))
refreshProducts() refreshProducts()
return
} }
productRequestCompletion = completion productRequestCompletion = completion
...@@ -157,9 +162,14 @@ extension IAPManager { ...@@ -157,9 +162,14 @@ extension IAPManager {
} }
} }
func purchase(_ state: PayState = .subscribe, completion: @escaping (Result<Bool, IAPError>) -> Void) { func purchase(_ state: PayState = .weekSubscribe, completion: @escaping (Result<Bool, IAPError>) -> Void) {
if state == .subscribe,weekProduct == nil{ if state == .weekSubscribe,weekProduct == nil{
completion(.failure(.noProductsFound))
return
}
if state == .yearSubscribe,yearProduct == nil{
completion(.failure(.noProductsFound)) completion(.failure(.noProductsFound))
return return
} }
...@@ -175,12 +185,23 @@ extension IAPManager { ...@@ -175,12 +185,23 @@ extension IAPManager {
self.state = state self.state = state
self.purchaseCompletion = completion self.purchaseCompletion = completion
let payment = SKPayment(product: state == .subscribe ? weekProduct! : lifetimeProduct!)
var payment:SKPayment = SKPayment()
switch state{
case .weekSubscribe:
payment = SKPayment(product: weekProduct!)
case .yearSubscribe:
payment = SKPayment(product: yearProduct!)
case .nonConsumable:
payment = SKPayment(product: lifetimeProduct!)
}
SKPaymentQueue.default().add(payment) SKPaymentQueue.default().add(payment)
alert.show("Processing the purchase request...", "") alert.show("Processing the purchase request...", "")
} }
func restore(_ state: PayState = .subscribe, completion: @escaping (Result<Bool, IAPError>) -> Void) { func restore(_ state: PayState = .weekSubscribe, completion: @escaping (Result<Bool, IAPError>) -> Void) {
self.state = state self.state = state
self.restoreCompletion = completion self.restoreCompletion = completion
SKPaymentQueue.default().restoreCompletedTransactions() SKPaymentQueue.default().restoreCompletedTransactions()
...@@ -384,15 +405,15 @@ extension IAPManager: SKProductsRequestDelegate { ...@@ -384,15 +405,15 @@ extension IAPManager: SKProductsRequestDelegate {
// 拿到请求下来的商品信息 // 拿到请求下来的商品信息
self.weekProduct = products.filter{$0.productIdentifier == ProductID.weekMember}.first self.weekProduct = products.filter{$0.productIdentifier == ProductID.weekMember}.first
self.lifetimeProduct = products.filter{$0.productIdentifier == ProductID.lifetimeMember}.first self.lifetimeProduct = products.filter{$0.productIdentifier == ProductID.lifetimeMember}.first
self.yearProduct = products.filter{$0.productIdentifier == ProductID.yearMember}.first
// 缓存 // 缓存
if let week = self.weekProduct,let life = self.lifetimeProduct{ if let week = self.weekProduct,let life = self.lifetimeProduct,let year = self.yearProduct{
cacheProducts([week,life]) cacheProducts([week,life,year])
} }
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
self?.productRequestCompletion?((weakSelf.weekProduct,weakSelf.lifetimeProduct)) self?.productRequestCompletion?((weakSelf.weekProduct,weakSelf.lifetimeProduct,weakSelf.yearProduct))
BackgroundTaskManager.share.endTask() BackgroundTaskManager.share.endTask()
} }
} }
......
...@@ -107,7 +107,7 @@ class HomeViewController:BaseViewController { ...@@ -107,7 +107,7 @@ class HomeViewController:BaseViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
loadLaunchView()
self.setupUI() self.setupUI()
// 调用下追踪权限 // 调用下追踪权限
...@@ -181,6 +181,18 @@ class HomeViewController:BaseViewController { ...@@ -181,6 +181,18 @@ class HomeViewController:BaseViewController {
view.addSubview(homeView!) view.addSubview(homeView!)
} }
func loadLaunchView(){
let luanch = HomeLaunchView(frame: CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH))
luanch.show()
if !UserDef.shard.isShowLanding{
let Ssoryboard = UIStoryboard(name: "PermissionVC", bundle: nil)
if let current = Ssoryboard.instantiateViewController(identifier: "PermissionVCID") as? PermissionVC {
self.navigationController?.pushViewController(current, animated: false)
}
}
}
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
...@@ -259,3 +271,12 @@ extension HomeViewController { ...@@ -259,3 +271,12 @@ extension HomeViewController {
} }
} }
} }
// 遵守转场代理协议
extension HomeViewController: UIViewControllerTransitioningDelegate {
// 返回自定义的消失动画对象
func animationController(forDismissed dismissedController: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return FadeOutTransition()
}
}
...@@ -233,11 +233,4 @@ class HomeVideoDetailCustomHeaderView : UICollectionReusableView { ...@@ -233,11 +233,4 @@ class HomeVideoDetailCustomHeaderView : UICollectionReusableView {
GETCURRENTNAV()?.pushViewController(vc, animated: true) GETCURRENTNAV()?.pushViewController(vc, animated: true)
} }
func GETCURRENTNAV() -> UINavigationController? {
let k = UIApplication.shared.windows.filter({$0.isKeyWindow}).first
let pre = k?.rootViewController?.presentedViewController
let rt = k?.rootViewController
return (pre as? UINavigationController) ?? ((rt as? UITabBarController)?.selectedViewController as? UINavigationController) ?? (rt as? UINavigationController)
}
} }
...@@ -81,21 +81,23 @@ class NewGuideViewController: UIViewController { ...@@ -81,21 +81,23 @@ class NewGuideViewController: UIViewController {
} }
func enterHome(){ func enterHome(){
let vc:HomeViewController = HomeViewController() // let vc:HomeViewController = HomeViewController()
//
let nav = BaseNavViewController(rootViewController: vc) // let nav = BaseNavViewController(rootViewController: vc)
//
cWindow?.rootViewController = nav // cWindow?.rootViewController = nav
//
// let transition = CATransition()
// transition.duration = 0.5
// transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
//
// // 添加动画到 window 的 layer
// cWindow?.layer.add(transition, forKey: kCATransition)
//
// // 显示 window
// cWindow?.makeKeyAndVisible()
let transition = CATransition() self.navigationController?.popToRootViewController(animated: false)
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
// 添加动画到 window 的 layer
cWindow?.layer.add(transition, forKey: kCATransition)
// 显示 window
cWindow?.makeKeyAndVisible()
UserDef.shard.isShowLanding = true UserDef.shard.isShowLanding = true
UserDef.shard.saveUserDefToSandBox() UserDef.shard.saveUserDefToSandBox()
......
//
// HomeLaunchView.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import UIKit
import Lottie
class HomeLaunchView:UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = frame
configUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configUI(){
backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
let logoImage = UIImageView()
logoImage.image = UIImage.init(named: "icon_phone_manager")
addSubview(logoImage)
let nameImage = UIImageView()
nameImage.image = UIImage.init(named: "icon_phone_manager_name")
addSubview(nameImage)
addSubview(LaunchingLoop)
logoImage.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(kSafeAreaInsets.top+150)
}
nameImage.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(logoImage.snp.bottom).offset(12)
}
LaunchingLoop.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.height.equalTo(150)
make.bottom.equalToSuperview().offset(-60 * RScreenH())
}
}
func show(){
KEYWINDOW()?.addSubview(self)
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
self.alpha = 1
UIView.animate(withDuration: 0.3) {
self.alpha = 0
}completion: { _ in
self.removeFromSuperview()
}
})
}
private lazy var LaunchingLoop: LottieAnimationView = {
let animationView = LottieAnimationView(name: "launch_loaing")
animationView.loopMode = .loop
animationView.play()
return animationView
}()
}
...@@ -29,40 +29,33 @@ class LauchVC:UIViewController { ...@@ -29,40 +29,33 @@ class LauchVC:UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// backView.addSubview(LaunchingView) view.addSubview(LaunchingLoop)
backView.addSubview(LaunchingLoop)
// LaunchingView.snp.makeConstraints { make in
// make.centerX.equalToSuperview()
// make.centerY.equalToSuperview().offset(-140 * RScreenH())
// make.width.equalToSuperview()
// make.height.equalTo(LaunchingView.snp.width)
// }
LaunchingLoop.snp.makeConstraints { make in LaunchingLoop.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.width.height.equalTo(150) make.width.height.equalTo(150)
make.bottom.equalToSuperview().offset(-60 * RScreenH()) make.bottom.equalToSuperview().offset(-60 * RScreenH())
} }
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
var vc:UIViewController? self.dismiss(animated: false)
if (UserDef.shard.isShowLanding) { // var vc:UIViewController?
vc = HomeViewController() // if (UserDef.shard.isShowLanding) {
}else { // vc = HomeViewController()
let Ssoryboard = UIStoryboard(name: "PermissionVC", bundle: nil) // }else {
if let current = Ssoryboard.instantiateViewController(identifier: "PermissionVCID") as? PermissionVC { // let Ssoryboard = UIStoryboard(name: "PermissionVC", bundle: nil)
vc = current // if let current = Ssoryboard.instantiateViewController(identifier: "PermissionVCID") as? PermissionVC {
} // vc = current
} // }
guard let vc else {return} // }
let nav = BaseNavViewController(rootViewController: vc) // guard let vc else {return}
cWindow?.rootViewController = nav // let nav = BaseNavViewController(rootViewController: vc)
let transition = CATransition() // cWindow?.rootViewController = nav
transition.duration = 0.5 // let transition = CATransition()
transition.subtype = CATransitionSubtype.fromRight // 从左侧推入 // transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) // transition.subtype = CATransitionSubtype.fromRight // 从左侧推入
cWindow?.layer.add(transition, forKey: kCATransition) // transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
cWindow?.makeKeyAndVisible() // cWindow?.layer.add(transition, forKey: kCATransition)
// cWindow?.makeKeyAndVisible()
}) })
} }
} }
//
// LaunchViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import UIKit
import Lottie
class LaunchViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
let logoImage = UIImageView()
logoImage.image = UIImage.init(named: "icon_phone_manager")
view.addSubview(logoImage)
let nameImage = UIImageView()
nameImage.image = UIImage.init(named: "icon_phone_manager_name")
view.addSubview(nameImage)
view.addSubview(LaunchingLoop)
logoImage.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(kSafeAreaInsets.top+150)
}
nameImage.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(logoImage.snp.bottom).offset(12)
}
LaunchingLoop.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.height.equalTo(150)
make.bottom.equalToSuperview().offset(-60 * RScreenH())
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
self.dismiss(animated: true)
})
}
private lazy var LaunchingLoop: LottieAnimationView = {
let animationView = LottieAnimationView(name: "launch_loaing")
animationView.loopMode = .loop
animationView.play()
return animationView
}()
}
...@@ -631,4 +631,17 @@ class HomePayView:UIView { ...@@ -631,4 +631,17 @@ class HomePayView:UIView {
addSubview(payDueView) addSubview(payDueView)
return payDueView return payDueView
}() }()
func setPayAnime(){
// 添加呼吸动画
let breathAnimation = CABasicAnimation(keyPath: "transform.scale")
breathAnimation.fromValue = 1.0
breathAnimation.toValue = 1.05
breathAnimation.duration = 0.8
breathAnimation.autoreverses = true
breathAnimation.repeatCount = Float.infinity
breathAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
payButton?.layer.add(breathAnimation, forKey: "breathingAnimation")
}
} }
...@@ -10,6 +10,7 @@ import StoreKit ...@@ -10,6 +10,7 @@ import StoreKit
import SVProgressHUD import SVProgressHUD
class HomePayViewController:UIViewController { class HomePayViewController:UIViewController {
private var homePayView:HomePayView? private var homePayView:HomePayView?
...@@ -121,7 +122,7 @@ extension HomePayViewController { ...@@ -121,7 +122,7 @@ extension HomePayViewController {
IAPManager.share.fetchProducts { [weak self] products in IAPManager.share.fetchProducts { [weak self] products in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
if let (weekProduct, lifetimeProduct) = products { if let (weekProduct, lifetimeProduct,_) = products {
weakSelf.homePayView?.reloadSKPorduct(week: weekProduct, life: lifetimeProduct) weakSelf.homePayView?.reloadSKPorduct(week: weekProduct, life: lifetimeProduct)
} }
} }
...@@ -129,7 +130,7 @@ extension HomePayViewController { ...@@ -129,7 +130,7 @@ extension HomePayViewController {
} }
private func payTouch() -> Void { private func payTouch() -> Void {
IAPManager.share.purchase((homePayView?.type == 0) ? .subscribe : .nonConsumable) {[weak self] result in IAPManager.share.purchase((homePayView?.type == 0) ? .weekSubscribe : .nonConsumable) {[weak self] result in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
switch result { switch result {
case .success(let success): case .success(let success):
...@@ -182,4 +183,5 @@ extension HomePayViewController { ...@@ -182,4 +183,5 @@ extension HomePayViewController {
rt.present(nav, animated: true) rt.present(nav, animated: true)
} }
} }
//
// PayDistanceViewController.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import UIKit
import StoreKit
import SVProgressHUD
class PayDistanceViewController: UIViewController {
var scrollView:UIScrollView!
var dropImage:UIImageView!
var yearDistacePrice:UILabel!
var payBtn:UIButton!
var year:SKProduct?
override func viewDidLoad() {
super.viewDidLoad()
reloadSKPorduct()
configUI()
reloadSKPorduct()
}
func configUI(){
scrollView = UIScrollView()
scrollView.frame = CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH)
scrollView.contentSize = CGSize(width: ScreenW, height: ScreenH)
view.addSubview(scrollView)
let closeBtn = UIButton()
closeBtn.setImage(UIImage.init(named: "home_pay_close"), for: .normal)
closeBtn.addTarget(self, action: #selector(payClose), for: .touchUpInside)
scrollView.addSubview(closeBtn)
let offerImage = UIImageView()
offerImage.image = UIImage.init(named: "iap_distance_offer")
scrollView.addSubview(offerImage)
dropImage = UIImageView()
dropImage.image = UIImage.init(named: "iap_distance_bg")
scrollView.addSubview(dropImage)
let distanceL = UILabel()
distanceL.text = "70% Discount"
distanceL.font = UIFont.systemFont(ofSize: 40, weight: .semibold)
distanceL.textColor = UIColor.colorWithHex(hexStr: "#111111")
scrollView.addSubview(distanceL)
let annual = UILabel()
annual.text = "Annual visit"
annual.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
annual.textColor = UIColor.colorWithHex(hexStr: "#111111")
scrollView.addSubview(annual)
yearDistacePrice = UILabel()
yearDistacePrice.font = UIFont.systemFont(ofSize: 14,weight: .semibold)
yearDistacePrice.textColor = UIColor.colorWithHex(hexStr: "#111111")
yearDistacePrice.text = "$39.99 $19.99 / Year"
scrollView.addSubview(yearDistacePrice)
let priceAtt = createRichPriceText(
originalPrice: "$39.99",
discountedPrice: "$19.99 / Year"
)
yearDistacePrice.attributedText = priceAtt
let cancelLabel = UILabel()
cancelLabel.font = UIFont.systemFont(ofSize: 12,weight: .semibold)
cancelLabel.textColor = UIColor.colorWithHex(hexStr: "#0082FF")
cancelLabel.text = "Cancel at any time"
scrollView.addSubview(cancelLabel)
payBtn = UIButton()
payBtn.setTitle("Get discounts", for: .normal)
payBtn.setTitleColor(.white, for: .normal)
payBtn.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
payBtn.layer.cornerRadius = 30
payBtn.addTarget(self, action: #selector(iapPayAction), for: .touchUpInside)
payBtn.backgroundColor = UIColor.colorWithHex(hexStr: "#0082FF")
scrollView.addSubview(payBtn)
let termsBtn = UIButton()
termsBtn.setTitle("Terms", for: .normal)
termsBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium)
termsBtn.setTitleColor(UIColor.colorWithHex(hexStr: "#999999"), for: .normal)
termsBtn.titleLabel?.addUnderline()
scrollView.addSubview(termsBtn)
let recoveryBtn = UIButton()
recoveryBtn.setTitle("Recovery", for: .normal)
recoveryBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium)
recoveryBtn.setTitleColor(UIColor.colorWithHex(hexStr: "#999999"), for: .normal)
recoveryBtn.frame = CGRect(x: scrollView.bounds.width - 66, y: ScreenH - (20+kSafeAreaInsets.bottom), width: 50, height: 20)
recoveryBtn.titleLabel?.addUnderline()
scrollView.addSubview(recoveryBtn)
let appleLabel = UILabel()
appleLabel.font = UIFont.systemFont(ofSize: 10,weight: .medium)
appleLabel.textColor = UIColor.colorWithHex(hexStr: "#111111")
appleLabel.text = " Apple Security Guarantee"
scrollView.addSubview(appleLabel)
closeBtn.snp.makeConstraints { make in
make.left.equalTo(16)
make.top.equalTo(kSafeAreaInsets.top+20)
make.size.equalTo(30)
}
offerImage.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(kSafeAreaInsets.top+70)
make.width.equalTo(111)
make.height.equalTo(37)
}
distanceL.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(dropImage.snp.bottom).offset(46)
}
appleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.height.equalTo(20)
make.top.equalToSuperview().offset(ScreenH - (20+kSafeAreaInsets.bottom))
}
termsBtn.snp.makeConstraints { make in
make.centerY.equalTo(appleLabel)
make.left.equalTo(16)
}
payBtn.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.equalTo(343)
make.height.equalTo(57)
make.bottom.equalTo(appleLabel.snp.top).offset(-24)
}
cancelLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(payBtn.snp.top).offset(-22)
make.height.equalTo(22)
}
yearDistacePrice.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(cancelLabel.snp.top).offset(0)
make.height.equalTo(22)
}
annual.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(yearDistacePrice.snp.top).offset(-8)
make.height.equalTo(22)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setAnim()
}
func setAnim(){
dropImage.frame = CGRect(x: (ScreenW - 300) / 2, y: -224, width: 300, height: 224)
// 添加重力动画效果
UIView.animate(withDuration: 1.5,
delay: 0.2,
usingSpringWithDamping: 0.5, // 弹性系数,越小弹性越大
initialSpringVelocity: 0.8, // 初始速度
options: .curveEaseIn,
animations: {
// 设置最终位置
self.dropImage.frame = CGRect(x: (ScreenW - 300) / 2,
y: kSafeAreaInsets.top + 129, // 根据原来的约束位置计算
width: 300,
height: 224)
}, completion: nil)
// 添加呼吸动画
let breathAnimation = CABasicAnimation(keyPath: "transform.scale")
breathAnimation.fromValue = 1.0
breathAnimation.toValue = 1.05
breathAnimation.duration = 0.8
breathAnimation.autoreverses = true
breathAnimation.repeatCount = Float.infinity
breathAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
payBtn.layer.add(breathAnimation, forKey: "breathingAnimation")
}
func reloadSKPorduct(){
if IAPManager.share.isSubscribed{
}
IAPManager.share.fetchProducts { [weak self] products in
guard let weakSelf = self else { return }
DispatchQueue.main.async {
if let (weekProduct, lifetimeProduct,yearProduct) = products {
weakSelf.year = yearProduct
weakSelf.reloadSKUI()
}
}
}
}
func reloadSKUI(){
guard let year = year else{
return
}
let priceAtt = createRichPriceText(
originalPrice: "$39.99",
discountedPrice: "\(year.localizedPrice) / Year"
)
yearDistacePrice.attributedText = priceAtt
}
}
extension PayDistanceViewController{
@objc func payClose(){
self.dismiss(animated: true)
}
@objc func iapPayAction(){
IAPManager.share.purchase(.yearSubscribe) {[weak self] result in
guard let weakSelf = self else { return }
switch result {
case .success(let success):
DispatchQueue.main.async {
SVProgressHUD.showSuccess(withStatus: "purchase succeeds")
weakSelf.dismiss(animated: true)
}
case .failure(let failure):
DispatchQueue.main.async {
SVProgressHUD.showError(withStatus: failure.localizedDescription)
}
}
}
}
func createRichPriceText(originalPrice: String, discountedPrice: String) -> NSAttributedString {
// 创建可变属性字符串
let attributedString = NSMutableAttributedString()
// 1. 设置原价部分(带删除线)
let originalPriceAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.colorWithHex(hexStr: "#111111", alpha: 0.6),
.strikethroughStyle: NSUnderlineStyle.single.rawValue,
.font: UIFont.systemFont(ofSize: 14,weight: .semibold)
]
let originalPricePart = NSAttributedString(string: originalPrice, attributes: originalPriceAttributes)
attributedString.append(originalPricePart)
// 2. 添加空格分隔
attributedString.append(NSAttributedString(string: " "))
// 3. 设置折扣价部分(无删除线)
let discountedPriceAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.colorWithHex(hexStr: "#111111"),
.font: UIFont.systemFont(ofSize: 14, weight: .semibold)
]
let discountedPricePart = NSAttributedString(string: discountedPrice, attributes: discountedPriceAttributes)
attributedString.append(discountedPricePart)
return attributedString
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PayDistanceViewController" customModuleProvider="target">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
</view>
</objects>
</document>
...@@ -83,3 +83,11 @@ public func Alert( _ title:String? , _ message:String) -> Void { ...@@ -83,3 +83,11 @@ public func Alert( _ title:String? , _ message:String) -> Void {
alert.addAction(UIAlertAction(title: "OK", style: .cancel)) alert.addAction(UIAlertAction(title: "OK", style: .cancel))
cWindow?.rootViewController?.present(alert, animated: true) cWindow?.rootViewController?.present(alert, animated: true)
} }
func GETCURRENTNAV() -> UINavigationController? {
let k = UIApplication.shared.windows.filter({$0.isKeyWindow}).first
let pre = k?.rootViewController?.presentedViewController
let rt = k?.rootViewController
return (pre as? UINavigationController) ?? ((rt as? UITabBarController)?.selectedViewController as? UINavigationController) ?? (rt as? UINavigationController)
}
//
// FadeOutTransition.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import Foundation
import UIKit
// 自定义转场动画(用于消失时的渐隐效果)
class FadeOutTransition: NSObject, UIViewControllerAnimatedTransitioning {
// 动画时长(可自定义)
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3 // 0.3秒渐隐
}
// 执行动画
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// 获取即将消失的控制器(被弹出的控制器)
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) else { return }
// 将目标控制器的视图添加到容器视图(确保转场后界面正确)
transitionContext.containerView.addSubview(toVC.view)
// 渐隐动画:将 fromVC 的视图透明度从 1 降到 0
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
fromVC.view.alpha = 0
}) { (_) in
// 动画完成后,标记转场结束
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment