Commit 83f33d9f authored by shenyong's avatar shenyong

订阅增加类型获取,新增年费会员

parent 855a80da
...@@ -135,20 +135,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -135,20 +135,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private func setupDefault() { private func setupDefault() {
IAPManager.share.checkSubscriptionState { isSubscribed, expiresDate in
Print("是否内购---",isSubscribed)
}
NetStatusManager.manager.startNet { status in NetStatusManager.manager.startNet { status in
switch status { switch status {
case .NoNet: case .NoNet:
break break
case .WIFI,.WWAN: case .WIFI,.WWAN:
break
if (PhotoAndVideoMananger.mananger.allAssets.count == 0) {
PhotoAndVideoMananger.mananger.setAssets()
}
} }
} }
......
...@@ -28,9 +28,23 @@ extension AppDelegate{ ...@@ -28,9 +28,23 @@ extension AppDelegate{
switch shortcutItem.type { switch shortcutItem.type {
case "com.app.phonemanager.iap.distance": case "com.app.phonemanager.iap.distance":
// 执行相应操作 // 执行相应操作
let vc = PayDistanceViewController() IAPManager.share.showYearPage = true
if IAPManager.share.isHotLaunch{
// 热启动走这里
switch IAPManager.share.subscriptionType {
case .none,.week:
let vc:PayDistanceViewController = PayDistanceViewController()
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
guard let rt = UIViewController.topMostViewController() else { return }
rt.present(nav, animated: true)
default:
let vc : PayCompletedViewController = PayCompletedViewController()
guard let rt = UIViewController.topMostViewController() else { return }
vc.modalPresentationStyle = .fullScreen vc.modalPresentationStyle = .fullScreen
GETCURRENTNAV()?.present(vc, animated: false) rt.present(vc, animated: true)
}
}
completionHandler(true) completionHandler(true)
default: default:
completionHandler(false) completionHandler(false)
......
...@@ -37,6 +37,15 @@ enum IAPError: Error { ...@@ -37,6 +37,15 @@ enum IAPError: Error {
} }
} }
// 添加一个枚举来表示订阅类型
enum SubscriptionType {
case none
case week
case year
case lifetime
}
// MARK: - IAPManager // MARK: - IAPManager
class IAPManager: NSObject { class IAPManager: NSObject {
...@@ -87,10 +96,23 @@ class IAPManager: NSObject { ...@@ -87,10 +96,23 @@ class IAPManager: NSObject {
} }
} }
// 是否展示优惠订阅
var showYearPage:Bool = false
// 是否热启动
var isHotLaunch:Bool = false
// 订阅状态
var subscriptionType:SubscriptionType = .none{
didSet{
isSubscribed = subscriptionType != .none
}
}
// MARK: - Initialization // MARK: - Initialization
private override init() { private override init() {
super.init() super.init()
SKPaymentQueue.default().add(self) SKPaymentQueue.default().add(self)
} }
deinit { deinit {
...@@ -133,31 +155,56 @@ extension IAPManager { ...@@ -133,31 +155,56 @@ extension IAPManager {
// 检查订阅信息 // 检查订阅信息
/// - Parameter completion: 回调闭包,返回订阅状态和到期时间 /// - Parameter completion: 回调闭包,返回订阅状态和到期时间
func checkSubscriptionState(completion: @escaping (_ isSubscribed: Bool, _ expiresDate: Date?) -> Void) { // func checkSubscriptionState(completion: @escaping (_ isSubscribed: Bool, _ expiresDate: Date?) -> Void) {
// verifyReceiptWithApple { result in
// switch result {
// case .success(let receipt):
// // 打印完整收据信息,方便调试
// //print("收据信息:\(receipt)")
// let status = self.checkSubscriptionStatus(receiptInfo: receipt)
// DispatchQueue.main.async {
// completion(status.isActive, status.expiresDate)
// // 更新本地订阅状态
// self.isSubscribed = status.isActive
// #if DEBUG
// if let expDate = status.expiresDate {
// let formatter = DateFormatter()
// formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// Print("订阅状态:\(status.isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))")
// }
// #endif
// }
//
// case .failure(_):
// // 处理错误情况
// self.refreshReceipt()
// completion(false, nil)
// }
// }
// }
// 修改回调方法,增加订阅类型参数
func checkSubscriptionState(completion: @escaping (_ isSubscribed: Bool, _ subscriptionType: SubscriptionType, _ expiresDate: Date?) -> Void) {
verifyReceiptWithApple { result in verifyReceiptWithApple { result in
switch result { switch result {
case .success(let receipt): case .success(let receipt):
// 打印完整收据信息,方便调试
//print("收据信息:\(receipt)")
let status = self.checkSubscriptionStatus(receiptInfo: receipt) let status = self.checkSubscriptionStatus(receiptInfo: receipt)
DispatchQueue.main.async { DispatchQueue.main.async {
completion(status.isActive, status.expiresDate) completion(status.isActive, status.subscriptionType, status.expiresDate)
// 更新本地订阅状态
self.isSubscribed = status.isActive self.isSubscribed = status.isActive
self.subscriptionType = status.subscriptionType
#if DEBUG #if DEBUG
if let expDate = status.expiresDate { if let expDate = status.expiresDate {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
Print("订阅状态:\(status.isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))") Print("订阅状态:\(status.isActive ? "已订阅" : "未订阅")订阅类型:\(status.subscriptionType)到期时间:\(formatter.string(from: expDate))")
} }
#endif #endif
} }
case .failure(_): case .failure(_):
// 处理错误情况
self.refreshReceipt() self.refreshReceipt()
completion(false, nil) completion(false, .none, nil)
} }
} }
} }
...@@ -440,7 +487,7 @@ extension IAPManager: SKPaymentTransactionObserver { ...@@ -440,7 +487,7 @@ extension IAPManager: SKPaymentTransactionObserver {
/** 恢复购买 */ /** 恢复购买 */
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
// 刷新订阅状态 // 刷新订阅状态
checkSubscriptionState { [weak self] isSubscribed, expiresDate in checkSubscriptionState { [weak self] isSubscribed,type,expiresDate in
guard let self = self else { return } guard let self = self else { return }
// 更新恢复购买的结果 // 更新恢复购买的结果
if isSubscribed { if isSubscribed {
...@@ -468,7 +515,57 @@ extension IAPManager: SKPaymentTransactionObserver { ...@@ -468,7 +515,57 @@ extension IAPManager: SKPaymentTransactionObserver {
extension IAPManager{ extension IAPManager{
func checkSubscriptionStatus(receiptInfo: [String: Any]) -> (isActive: Bool, isTrial: Bool, expiresDate: Date?) { // func checkSubscriptionStatus(receiptInfo: [String: Any]) -> (isActive: Bool, isTrial: Bool, expiresDate: Date?) {
// // 首先检查 in_app 购买记录
// if let receipt = receiptInfo["receipt"] as? [String: Any],
// let inAppPurchases = receipt["in_app"] as? [[String: Any]] {
// // 检查是否存在永久商品购买记录
// let hasLifetimePurchase = inAppPurchases.contains { purchase in
// return purchase["product_id"] as? String == ProductID.lifetimeMember
// }
//
// if hasLifetimePurchase {
// print("检测到永久商品购买记录")
// return (true, false, nil)
// }
// }
//
// // 如果没有永久商品,继续检查订阅
// guard let latestReceiptInfo = receiptInfo["latest_receipt_info"] as? [[String: Any]] else {
// print("没有找到任何购买记录")
// return (false, false, nil)
// }
//
// // 按过期时间排序,确保获取最新的订阅记录
// let sortedReceipts = latestReceiptInfo.sorted { receipt1, receipt2 in
// let date1 = getExpiryDate(from: receipt1) ?? Date.distantPast
// let date2 = getExpiryDate(from: receipt2) ?? Date.distantPast
// return date1 > date2
// }
//
// guard let lastReceipt = sortedReceipts.first else {
// return (false, false, nil)
// }
//
// // 解析试用期状态
// let isTrial = (lastReceipt["is_trial_period"] as? String) == "true"
//
// // 获取过期时间
// let expiresDate = getExpiryDate(from: lastReceipt)
// let isActive = expiresDate?.compare(Date()) == .orderedDescending
//
// #if DEBUG
// if let expDate = expiresDate {
// let formatter = DateFormatter()
// formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// print("订阅状态:\(isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))")
// }
// #endif
//
// return (isActive, isTrial, expiresDate)
// }
func checkSubscriptionStatus(receiptInfo: [String: Any]) -> (isActive: Bool, isTrial: Bool, subscriptionType: SubscriptionType, expiresDate: Date?) {
// 首先检查 in_app 购买记录 // 首先检查 in_app 购买记录
if let receipt = receiptInfo["receipt"] as? [String: Any], if let receipt = receiptInfo["receipt"] as? [String: Any],
let inAppPurchases = receipt["in_app"] as? [[String: Any]] { let inAppPurchases = receipt["in_app"] as? [[String: Any]] {
...@@ -479,14 +576,14 @@ extension IAPManager{ ...@@ -479,14 +576,14 @@ extension IAPManager{
if hasLifetimePurchase { if hasLifetimePurchase {
print("检测到永久商品购买记录") print("检测到永久商品购买记录")
return (true, false, nil) return (true, false, .lifetime, nil)
} }
} }
// 如果没有永久商品,继续检查订阅 // 如果没有永久商品,继续检查订阅
guard let latestReceiptInfo = receiptInfo["latest_receipt_info"] as? [[String: Any]] else { guard let latestReceiptInfo = receiptInfo["latest_receipt_info"] as? [[String: Any]] else {
print("没有找到任何购买记录") print("没有找到任何购买记录")
return (false, false, nil) return (false, false, .none, nil)
} }
// 按过期时间排序,确保获取最新的订阅记录 // 按过期时间排序,确保获取最新的订阅记录
...@@ -497,7 +594,7 @@ extension IAPManager{ ...@@ -497,7 +594,7 @@ extension IAPManager{
} }
guard let lastReceipt = sortedReceipts.first else { guard let lastReceipt = sortedReceipts.first else {
return (false, false, nil) return (false, false, .none, nil)
} }
// 解析试用期状态 // 解析试用期状态
...@@ -507,15 +604,21 @@ extension IAPManager{ ...@@ -507,15 +604,21 @@ extension IAPManager{
let expiresDate = getExpiryDate(from: lastReceipt) let expiresDate = getExpiryDate(from: lastReceipt)
let isActive = expiresDate?.compare(Date()) == .orderedDescending let isActive = expiresDate?.compare(Date()) == .orderedDescending
#if DEBUG // 确定订阅类型
if let expDate = expiresDate { let productId = lastReceipt["product_id"] as? String
let formatter = DateFormatter() let subscriptionType: SubscriptionType
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" switch productId {
print("订阅状态:\(isActive ? "已订阅" : "未订阅"),到期时间:\(formatter.string(from: expDate))") case ProductID.weekMember:
subscriptionType = .week
case ProductID.yearMember:
subscriptionType = .year
case ProductID.lifetimeMember:
subscriptionType = .lifetime
default:
subscriptionType = .none
} }
#endif
return (isActive, isTrial, expiresDate) return (isActive, isTrial, subscriptionType, expiresDate)
} }
// 辅助方法:解析过期时间 // 辅助方法:解析过期时间
......
//
// IAPPageManager.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import Foundation
struct IAPPageManager{
func showIAPPage(){
}
}
...@@ -137,9 +137,14 @@ class PhotoManager{ ...@@ -137,9 +137,14 @@ class PhotoManager{
private func requestAuthorization(){ private func requestAuthorization(){
// 获取基础数据 // 获取基础数据
requestAuthorization {[weak self] _ in requestAuthorization {[weak self] permission in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
if permission{
weakSelf.getBaseAssetGroup() weakSelf.getBaseAssetGroup()
}else{
weakSelf.clearLocalData()
}
} }
} }
...@@ -748,6 +753,10 @@ extension PhotoManager{ ...@@ -748,6 +753,10 @@ extension PhotoManager{
} }
} }
func clearLocalData(){
}
func removeAssets(withIdentifiers identifiers: [String], from assets: [AssetModel]) -> [AssetModel] { func removeAssets(withIdentifiers identifiers: [String], from assets: [AssetModel]) -> [AssetModel] {
let identifierSet = Set(identifiers) let identifierSet = Set(identifiers)
......
...@@ -14,8 +14,12 @@ class HomeViewController:BaseViewController { ...@@ -14,8 +14,12 @@ class HomeViewController:BaseViewController {
private var isShowCharge:Bool = false private var isShowCharge:Bool = false
private var canShowIAP:Bool = false
var homeView:HomeView? var homeView:HomeView?
fileprivate func junmToModule(_ cIndex: Int, _ self: HomeViewController) { fileprivate func junmToModule(_ cIndex: Int, _ self: HomeViewController) {
switch cIndex { switch cIndex {
...@@ -185,6 +189,15 @@ class HomeViewController:BaseViewController { ...@@ -185,6 +189,15 @@ class HomeViewController:BaseViewController {
func loadLaunchView(){ func loadLaunchView(){
let luanch = HomeLaunchView(frame: CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH)) let luanch = HomeLaunchView(frame: CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH))
luanch.show() luanch.show()
luanch.disMissBlock = {[weak self] in
guard let weakSelf = self else { return }
guard UserDef.shard.isShowLanding else{
return
}
weakSelf.showIAPVC()
}
if !UserDef.shard.isShowLanding{ if !UserDef.shard.isShowLanding{
let Ssoryboard = UIStoryboard(name: "PermissionVC", bundle: nil) let Ssoryboard = UIStoryboard(name: "PermissionVC", bundle: nil)
...@@ -192,9 +205,47 @@ class HomeViewController:BaseViewController { ...@@ -192,9 +205,47 @@ class HomeViewController:BaseViewController {
self.navigationController?.pushViewController(current, animated: false) self.navigationController?.pushViewController(current, animated: false)
} }
} }
NotificationCenter.default.addObserver(forName: .guidePageClose, object: nil, queue: nil) {[weak self] _ in
guard let weakSelf = self else { return }
weakSelf.showIAPVC()
}
}
func showIAPVC(){
IAPManager.share.isHotLaunch = true
IAPManager.share.checkSubscriptionState { isSubscribed,type,expiresDate in
Print("是否内购---",isSubscribed)
if IAPManager.share.showYearPage{
//按钮启动
switch type {
case .none,.week:
let vc:PayDistanceViewController = PayDistanceViewController()
let nav:BaseNavViewController = BaseNavViewController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
self.present(nav, animated: true)
case .lifetime,.year:
let vc : PayCompletedViewController = PayCompletedViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true)
}
}else{
// 正常启动
if !isSubscribed{
DispatchQueue.main.async {
if !IAPManager.share.showYearPage{
HomePayViewController.show {
NotificationManager().configNotifications()
}
}
}
}
}
}
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
...@@ -215,6 +266,21 @@ class HomeViewController:BaseViewController { ...@@ -215,6 +266,21 @@ class HomeViewController:BaseViewController {
homeView?.viewModel.reloadTrashAndKeep() homeView?.viewModel.reloadTrashAndKeep()
} }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.barHidden = false
// 开启定时器
Singleton.shared.startCountdown {}
if !isShowCharge {
NotificationManager().configNotifications()
return
}
}
} }
extension HomeViewController { extension HomeViewController {
...@@ -272,11 +338,3 @@ extension HomeViewController { ...@@ -272,11 +338,3 @@ extension HomeViewController {
} }
} }
// 遵守转场代理协议
extension HomeViewController: UIViewControllerTransitioningDelegate {
// 返回自定义的消失动画对象
func animationController(forDismissed dismissedController: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return FadeOutTransition()
}
}
...@@ -60,10 +60,10 @@ class HomeCollectionViewHeader : UICollectionReusableView { ...@@ -60,10 +60,10 @@ class HomeCollectionViewHeader : UICollectionReusableView {
self.addSubview(self.permissionView) self.addSubview(self.permissionView)
self.permissionView.snp.makeConstraints { make in self.permissionView.snp.makeConstraints { make in
make.top.equalTo(self.progressBar.snp.bottom).offset(-92) make.top.equalTo(progressBar.snp.bottom).offset(10)
make.left.equalToSuperview().offset(-22) make.left.equalToSuperview().offset(-22)
make.right.equalToSuperview().offset(22) make.right.equalToSuperview().offset(22)
make.height.equalTo(449) make.height.equalTo(340)
} }
...@@ -206,6 +206,7 @@ class CustomProgressBar: UIView { ...@@ -206,6 +206,7 @@ class CustomProgressBar: UIView {
progressLayer.frame = CGRect(x: 0, y: 0, width: ScreenW-48, height: 10) progressLayer.frame = CGRect(x: 0, y: 0, width: ScreenW-48, height: 10)
progressLayer.cornerRadius = 5 progressLayer.cornerRadius = 5
progressLayer.masksToBounds = true progressLayer.masksToBounds = true
updateProgress()
} }
......
...@@ -404,7 +404,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -404,7 +404,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
if PhotoManager.shared.permissionStatus == .authorized{ if PhotoManager.shared.permissionStatus == .authorized{
return CGSize(width: self.collectionView.width, height: 76 + (self.homeNavView?.height ?? 0)) return CGSize(width: self.collectionView.width, height: 76 + (self.homeNavView?.height ?? 0))
}else{ }else{
return CGSize(width: self.collectionView.width, height: 404) return CGSize(width: self.collectionView.width, height: 450)
} }
} }
return CGSize.zero return CGSize.zero
......
...@@ -24,6 +24,7 @@ class NewGuideViewController: UIViewController { ...@@ -24,6 +24,7 @@ class NewGuideViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.transitioningDelegate = self
configUI() configUI()
startTimer() startTimer()
} }
...@@ -97,10 +98,11 @@ class NewGuideViewController: UIViewController { ...@@ -97,10 +98,11 @@ class NewGuideViewController: UIViewController {
// // 显示 window // // 显示 window
// cWindow?.makeKeyAndVisible() // cWindow?.makeKeyAndVisible()
self.navigationController?.popToRootViewController(animated: false)
self.navigationController?.popToRootViewController(animated: false)
UserDef.shard.isShowLanding = true UserDef.shard.isShowLanding = true
UserDef.shard.saveUserDefToSandBox() UserDef.shard.saveUserDefToSandBox()
NotificationCenter.default.post(name: .guidePageClose, object: nil)
} }
deinit{ deinit{
...@@ -147,3 +149,10 @@ extension NewGuideViewController:UICollectionViewDelegate,UICollectionViewDataSo ...@@ -147,3 +149,10 @@ extension NewGuideViewController:UICollectionViewDelegate,UICollectionViewDataSo
} }
} }
extension NewGuideViewController: UIViewControllerTransitioningDelegate {
func animationController(forDismissed dismissedController: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// pop 时触发消失动画(等同于 popToRootViewController)
return FadeOutPopTransition()
}
}
...@@ -10,6 +10,8 @@ import Lottie ...@@ -10,6 +10,8 @@ import Lottie
class HomeLaunchView:UIView { class HomeLaunchView:UIView {
var disMissBlock:(() ->Void)?
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
self.frame = frame self.frame = frame
...@@ -62,6 +64,7 @@ class HomeLaunchView:UIView { ...@@ -62,6 +64,7 @@ class HomeLaunchView:UIView {
}completion: { _ in }completion: { _ in
self.removeFromSuperview() self.removeFromSuperview()
} }
self.disMissBlock?()
}) })
} }
......
...@@ -102,10 +102,6 @@ class HomePayView:UIView { ...@@ -102,10 +102,6 @@ class HomePayView:UIView {
let animationView = LottieAnimationView(name: "iapAnimation") let animationView = LottieAnimationView(name: "iapAnimation")
animationView.loopMode = .loop animationView.loopMode = .loop
// animationView.animationSpeed = -1.0
// animationView.layer.cornerRadius = 8
// animationView.contentMode = .scaleAspectFill
// animationView.clipsToBounds = true
return animationView return animationView
}() }()
...@@ -115,6 +111,7 @@ class HomePayView:UIView { ...@@ -115,6 +111,7 @@ class HomePayView:UIView {
super.init(frame: frame) super.init(frame: frame)
setupUI() setupUI()
setPayAnime()
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
...@@ -162,63 +159,6 @@ class HomePayView:UIView { ...@@ -162,63 +159,6 @@ class HomePayView:UIView {
titleLabel2?.sizeToFit() titleLabel2?.sizeToFit()
// photoImage = UIImageView(image: UIImage(named: "img_photos_start"))
// self.addSubview(photoImage)
//
// photoImage.snp.makeConstraints { make in
// make.top.equalTo(titleLabel2!.snp.bottom).offset(38.RH())
// make.left.equalTo(48.RW())
// make.size.equalTo(64.RW())
// }
//
// icloudImage = UIImageView(image: UIImage(named: "img_file_guide"))
// self.addSubview(icloudImage)
//
// icloudImage.snp.makeConstraints { make in
// make.left.equalTo(photoImage.snp.right).offset(24.RW())
// make.centerY.equalTo(photoImage)
// make.size.equalTo(64.RW())
// }
//
// photoLabel = UILabel()
// photoLabel?.text = "403"
// photoLabel?.backgroundColor = UIColor.colorWithHex(hexStr: "#EB4545")
// photoLabel?.font = UIFont.scaledSystemFont(ofSize: 10, weight: .bold)
// photoLabel?.textAlignment = .center
// photoLabel?.textColor = .white
// photoLabel?.layer.cornerRadius = 12.RW()
// photoLabel?.layer.masksToBounds = true
// self.addSubview(photoLabel!)
//
// photoLabel?.snp.makeConstraints { make in
// make.top.equalTo(photoImage.snp.top).offset(-12)
// make.right.equalTo(photoImage.snp.right).offset(12)
// make.size.equalTo(24.RW())
// }
//
// photoLabel?.sizeToFit()
//
// icloudLabel = UILabel()
// icloudLabel?.text = "217"
// icloudLabel?.textAlignment = .center
// icloudLabel?.font = UIFont.scaledSystemFont(ofSize: 10, weight: .bold)
// icloudLabel?.backgroundColor = UIColor.colorWithHex(hexStr: "#EB4545")
// icloudLabel?.textColor = .white
// icloudLabel?.layer.cornerRadius = 12.RW()
// icloudLabel?.layer.masksToBounds = true
// self.addSubview(icloudLabel!)
//
// icloudLabel?.snp.makeConstraints { make in
// make.top.equalTo(icloudImage.snp.top).offset(-12)
// make.right.equalTo(icloudImage.snp.right).offset(12)
// make.size.equalTo(24.RW())
// }
//
// icloudLabel?.sizeToFit()
//
self.addSubview(animationView) self.addSubview(animationView)
playAnimationWithDelay() playAnimationWithDelay()
...@@ -228,27 +168,6 @@ class HomePayView:UIView { ...@@ -228,27 +168,6 @@ class HomePayView:UIView {
make.width.equalTo(285.RW()) make.width.equalTo(285.RW())
make.height.equalTo(142.RW()) make.height.equalTo(142.RW())
} }
//
// let analysis = UILabel()
// analysis.text = "Analysis completed"
// analysis.textColor = UIColor.colorWithHex(hexStr: "#B3B3B3")
// analysis.font = UIFont.scaledSystemFont(ofSize: 14, weight: .bold)
// addSubview(analysis)
// analysis.snp.makeConstraints { make in
// make.left.equalTo(animationView.snp.left)
// make.top.equalTo(animationView.snp.bottom).offset(8)
// }
//
// memSize = UILabel()
// memSize.text = "100%"
// memSize.textColor = UIColor.colorWithHex(hexStr: "#B3B3B3")
// memSize.font = UIFont.scaledSystemFont(ofSize: 14, weight: .bold)
// addSubview(memSize)
// memSize.snp.makeConstraints { make in
// make.right.equalTo(animationView.snp.right)
// make.top.equalTo(animationView.snp.bottom).offset(8)
// }
appleLabel = UILabel() appleLabel = UILabel()
appleLabel?.text = " Secured with Apple" appleLabel?.text = " Secured with Apple"
...@@ -341,49 +260,6 @@ class HomePayView:UIView { ...@@ -341,49 +260,6 @@ class HomePayView:UIView {
} }
// if let url = Bundle.main.url(forResource: "iap", withExtension: "gif"),
// let data = try? Data(contentsOf: url) {
// let animatedImage = FLAnimatedImage(animatedGIFData: data)
// let imageView = FLAnimatedImageView()
// imageView.animatedImage = animatedImage
// imageView.startAnimating()
// addSubview(imageView)
//
// imageView.snp.makeConstraints { make in
// make.centerX.equalToSuperview()
// make.top.equalTo(titleLabel2!.snp.bottom).offset(40)
// make.width.equalTo(285.RW())
// make.height.equalTo(142.RW())
// }
// }
// let gifImageView = UIImageView()
// gifImageView.contentMode = .scaleAspectFit
// addSubview(gifImageView)
// gifImageView.snp.makeConstraints { make in
// make.centerX.equalToSuperview()
// make.top.equalTo(titleLabel2!.snp.bottom).offset(40)
// make.width.equalTo(285.RW())
// make.height.equalTo(142.RW())
// }
//
// // 从 Bundle 加载 GIF 文件
// guard let gifImage = try? UIImage(gifName: "iap.gif") else {
// print("Failed to load GIF")
// return
// }
//
// gifImageView.setGifImage(gifImage, loopCount: -1)
// gifImageView.startAnimatingGif()
// gifImageView.startAnimatingGif()
// // 设置 GIF 到 ImageView 并开始播放
// do {
// try gifImageView.setGifImage(gifImage, loopCount: 0) // 0 表示无限循环
// } catch {
// print("Error setting GIF: \(error)")
// }
} }
func playAnimationWithDelay() { func playAnimationWithDelay() {
...@@ -553,45 +429,6 @@ class HomePayView:UIView { ...@@ -553,45 +429,6 @@ class HomePayView:UIView {
make.height.equalTo(110.RH()) make.height.equalTo(110.RH())
} }
// let freespace = UILabel()
// freespace.font = UIFont.scaledSystemFont(ofSize: 12, weight: .bold)
// freespace.text = "FREE SPACE"
// freespace.textColor = UIColor.colorWithHex(hexStr: "#B3B3B3")
// addSubview(freespace)
//
// sizeLabel = UILabel()
// sizeLabel.text = "54.2 GB"
// sizeLabel.textColor = UIColor.colorWithHex(hexStr: black3Color)
// sizeLabel.font = UIFont.scaledSystemFont(ofSize: 12, weight: .bold)
// addSubview(sizeLabel)
//
// freespace.snp.makeConstraints { make in
// make.left.equalTo(icloudImage!.snp.right).offset(24.RW())
// make.top.equalTo(titleLabel2!.snp.bottom).offset(42)
// make.height.equalTo(17.RW())
// }
//
// sizeLabel.snp.makeConstraints { make in
// make.top.equalTo(freespace.snp.bottom).offset(0)
// make.left.equalTo(icloudImage!.snp.right).offset(24)
// make.height.equalTo(39.RW())
// }
//
// let fullText = FileTool().formatBytes(FileTool().getStorageInfo(for: .free) ?? 0 )
// // 创建一个可变的富文本字符串
// let attributedString = NSMutableAttributedString(string: fullText)
// // 设置前半部分(动态数值)的范围
// let regularFont = UIFont.scaledSystemFont(ofSize: 28, weight: .bold) // 设置常规字体大小
// attributedString.addAttribute(.font, value: regularFont, range: NSRange.init(location: 0,length:fullText.count-2))
//
// // 设置“GB”部分的范围并将字体大小设置为12
// let smallFont = UIFont.scaledSystemFont(ofSize: 12, weight: .bold) // 设置字体大小为12
// attributedString.addAttribute(.font, value: smallFont, range:NSRange.init(location: fullText.count-2, length: 2))
//
// // 将富文本赋值给UILabel
// sizeLabel.attributedText = attributedString
payDueView.snp.makeConstraints { make in payDueView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalTo(tipsView.snp.bottom).offset(90.RH()) make.top.equalTo(tipsView.snp.bottom).offset(90.RH())
......
//
// PayDistanceAnimaeView.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import UIKit
class PayDistanceAnimaeView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
import UIKit import UIKit
import StoreKit import StoreKit
import SVProgressHUD import SVProgressHUD
import Lottie
class PayDistanceViewController: UIViewController { class PayDistanceViewController: UIViewController {
...@@ -23,9 +24,9 @@ class PayDistanceViewController: UIViewController { ...@@ -23,9 +24,9 @@ class PayDistanceViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
reloadSKPorduct()
configUI() configUI()
reloadSKPorduct() reloadSKPorduct()
IAPManager.share.showYearPage = false
} }
...@@ -34,6 +35,9 @@ class PayDistanceViewController: UIViewController { ...@@ -34,6 +35,9 @@ class PayDistanceViewController: UIViewController {
scrollView = UIScrollView() scrollView = UIScrollView()
scrollView.frame = CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH) scrollView.frame = CGRect(x: 0, y: 0, width: ScreenW, height: ScreenH)
scrollView.contentSize = CGSize(width: ScreenW, height: ScreenH) scrollView.contentSize = CGSize(width: ScreenW, height: ScreenH)
scrollView.contentInsetAdjustmentBehavior = .never
scrollView.delaysContentTouches = false // 添加这一行
scrollView.canCancelContentTouches = true // 添加这一行
view.addSubview(scrollView) view.addSubview(scrollView)
let closeBtn = UIButton() let closeBtn = UIButton()
...@@ -95,14 +99,18 @@ class PayDistanceViewController: UIViewController { ...@@ -95,14 +99,18 @@ class PayDistanceViewController: UIViewController {
termsBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium) termsBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium)
termsBtn.setTitleColor(UIColor.colorWithHex(hexStr: "#999999"), for: .normal) termsBtn.setTitleColor(UIColor.colorWithHex(hexStr: "#999999"), for: .normal)
termsBtn.titleLabel?.addUnderline() termsBtn.titleLabel?.addUnderline()
termsBtn.addTarget(self, action: #selector(termsAction(btn:)), for: .touchUpInside)
scrollView.addSubview(termsBtn) scrollView.addSubview(termsBtn)
let recoveryBtn = UIButton() let recoveryBtn = UIButton()
recoveryBtn.setTitle("Recovery", for: .normal) recoveryBtn.setTitle("Restore", for: .normal)
recoveryBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium) recoveryBtn.titleLabel?.font = UIFont.systemFont(ofSize: 10,weight: .medium)
recoveryBtn.setTitleColor(UIColor.colorWithHex(hexStr: "#999999"), for: .normal) 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.frame = CGRect(x: scrollView.bounds.width - 66, y: ScreenH - (20+kSafeAreaInsets.bottom), width: 50, height: 20)
recoveryBtn.titleLabel?.addUnderline() recoveryBtn.titleLabel?.addUnderline()
recoveryBtn.addTarget(self, action: #selector(recoveryBtnAction), for: .touchUpInside)
scrollView.addSubview(recoveryBtn) scrollView.addSubview(recoveryBtn)
...@@ -128,7 +136,7 @@ class PayDistanceViewController: UIViewController { ...@@ -128,7 +136,7 @@ class PayDistanceViewController: UIViewController {
distanceL.snp.makeConstraints { make in distanceL.snp.makeConstraints { make in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalTo(dropImage.snp.bottom).offset(46) make.top.equalTo(annual.snp.top).offset(-115.RH())
} }
...@@ -169,30 +177,65 @@ class PayDistanceViewController: UIViewController { ...@@ -169,30 +177,65 @@ class PayDistanceViewController: UIViewController {
make.height.equalTo(22) make.height.equalTo(22)
} }
scrollView.addSubview(termsView)
termsView.addSubview(privavye_Label)
termsView.snp.makeConstraints { make in
make.left.equalTo(scrollView).offset(16)
make.right.equalTo(scrollView).offset(-16)
make.top.equalTo(termsBtn.snp.bottom).offset(15.RH())
make.height.equalTo(500.RW())
// 添加这一行确保 termsView 的宽度正确
make.width.equalTo(scrollView).offset(-32)
}
privavye_Label.snp.makeConstraints { make in
make.edges.equalToSuperview()
// 添加宽度约束确保 label 能正确计算其尺寸
make.width.equalToSuperview()
}
view.addSubview(animationView)
animationView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
animationView.play { completed in
if completed{
self.animationView.removeFromSuperview()
self.setAnim()
}
}
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
setAnim()
} }
func setAnim(){ func setAnim(){
dropImage.frame = CGRect(x: (ScreenW - 300) / 2, y: -224, width: 300, height: 224) let dropW = CGFloat(300.RW())
let dropH = CGFloat(224.RW())
dropImage.frame = CGRect(x: (ScreenW - dropW) / 2, y: -dropH, width: dropW, height: dropH)
// 添加重力动画效果 // 添加重力动画效果
UIView.animate(withDuration: 1.5, UIView.animate(withDuration: 1.1,
delay: 0.2, delay: 0.2,
usingSpringWithDamping: 0.5, // 弹性系数,越小弹性越大 usingSpringWithDamping: 0.5, // 弹性系数,越小弹性越大
initialSpringVelocity: 0.8, // 初始速度 initialSpringVelocity: 0.8, // 初始速度
options: .curveEaseIn, options: .curveEaseIn,
animations: { animations: {
// 设置最终位置 // 设置最终位置
self.dropImage.frame = CGRect(x: (ScreenW - 300) / 2, self.dropImage.frame = CGRect(x: (ScreenW - dropW)/2.0,
y: kSafeAreaInsets.top + 129, // 根据原来的约束位置计算 y: kSafeAreaInsets.top + 129.RH(), // 根据原来的约束位置计算
width: 300, width: dropW,
height: 224) height: dropH)
}, completion: nil) }, completion: nil)
...@@ -210,14 +253,10 @@ class PayDistanceViewController: UIViewController { ...@@ -210,14 +253,10 @@ class PayDistanceViewController: UIViewController {
func reloadSKPorduct(){ func reloadSKPorduct(){
if IAPManager.share.isSubscribed{
}
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,yearProduct) = products { if let (_, _,yearProduct) = products {
weakSelf.year = yearProduct weakSelf.year = yearProduct
weakSelf.reloadSKUI() weakSelf.reloadSKUI()
} }
...@@ -239,6 +278,34 @@ class PayDistanceViewController: UIViewController { ...@@ -239,6 +278,34 @@ class PayDistanceViewController: UIViewController {
} }
lazy var termsView:UIView = {
let termsView = UIView()
termsView.backgroundColor = .clear
return termsView
}()
lazy var privavye_Label: UILabel = {
let priva = UILabel()
priva.font = UIFont.scaledSystemFont(ofSize: 14, weight: .regular)
priva.textColor = .gray
priva.text = ""
priva.numberOfLines = 0
priva.clipsToBounds = true
priva.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
tap.delegate = self
tap.cancelsTouchesInView = false // 添加这一行
priva.addGestureRecognizer(tap)
return priva
}()
lazy var animationView:LottieAnimationView = {
let animationView = LottieAnimationView(name: "discount")
animationView.loopMode = .playOnce
animationView.contentMode = .scaleAspectFill
return animationView
}()
} }
...@@ -256,7 +323,7 @@ extension PayDistanceViewController{ ...@@ -256,7 +323,7 @@ extension PayDistanceViewController{
IAPManager.share.purchase(.yearSubscribe) {[weak self] result in IAPManager.share.purchase(.yearSubscribe) {[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(_):
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.showSuccess(withStatus: "purchase succeeds") SVProgressHUD.showSuccess(withStatus: "purchase succeeds")
weakSelf.dismiss(animated: true) weakSelf.dismiss(animated: true)
...@@ -297,4 +364,88 @@ extension PayDistanceViewController{ ...@@ -297,4 +364,88 @@ extension PayDistanceViewController{
return attributedString return attributedString
} }
@objc func termsAction(btn:UIButton){
btn.isSelected = !btn.isSelected
let strs:NSString = btn.isSelected ? "We offer 1-year and lifetime subscriptions. The price is clearly displayed within the application.\n\n·After the purchase confirmation, your iTunes account will be charged.\n\n·Unless the automatic update is turned off at least 24 hours before the end of the current cycle, the subscription will be automatically updated.\n\n·Your account will be renewed within 24 hours before the end of the current cycle cost.\n\n·You can go to the \"Account Settings\" in the iTunes store to manage your subscriptions and turn off auto renewal.\n\n·If provided, if you choose to use our free trial version, any unused portion during the free trial period will become invalid when you purchase a publication subscription, if applicable.\n\n·If you choose not to purchase the AI PhoneManager Pro version, you can continue to use it for free and enjoy PhoneManager.\n\nYour personal data is securely stored in PhoneManager, please make sure to read our \nprivacy policy and terms of use." : ""
let attribtit = NSMutableAttributedString(string:strs as String , attributes: [:])
if strs.length > 3 {
let rang = strs.range(of: "privacy policy")
attribtit.addAttributes([NSAttributedString.Key.font : UIFont.scaledSystemFont(ofSize: 14, weight: .bold),NSAttributedString.Key.underlineStyle:NSUnderlineStyle.single.rawValue,.link:"appscheme://private"], range: rang)
let rang1 = strs.range(of: "terms of use")
attribtit.addAttributes([NSAttributedString.Key.font : UIFont.scaledSystemFont(ofSize: 14, weight: .bold),NSAttributedString.Key.underlineStyle:NSUnderlineStyle.single.rawValue,.link:"appscheme://terms"], range: rang1)
}
let contentH = attribtit.length > 0 ? 500.RW() : 0
// self.scrollView.contentSize = CGSizeMake(ScreenW, contentH + ScreenH)
self.scrollView.contentSize = CGSize(width: self.scrollView.frame.width, height: contentH + ScreenH)
privavye_Label.attributedText = attribtit
// privavye_Label.frame = CGRect(x: 16, y: ScreenH+20, width: ScreenW-32, height: contentH)
Print("scrollView frame:", scrollView.frame)
Print("scrollView contentSize:", scrollView.contentSize)
Print("termsView frame:", termsView.frame)
Print("privavye_Label frame:", privavye_Label.frame)
// view.layoutIfNeeded()
// if privavye_Label.attributedText?.length ?? 0 > 10 {
// UIView.animate(withDuration: 0.1) {
// self.scrollView.contentOffset = CGPoint(x: 0, y: self.scrollView.contentSize.height - self.scrollView.height)
// }
// }
}
@objc private func labelTapped(_ gesture: UITapGestureRecognizer) {
let location = gesture.location(in: privavye_Label)
let strs:NSString = "privacy policy"
let frssss = strs.boundingRect(with: CGSizeMake(.infinity, .infinity), options: .truncatesLastVisibleLine, attributes: [NSAttributedString.Key.font : privavye_Label.font ?? UIFont.scaledSystemFont(ofSize: 14, weight: .regular)], context: nil)
let frame1 = CGRectMake(0, privavye_Label.height-25, CGRectGetWidth(frssss), 25)
let frame2 = CGRectMake(CGRectGetWidth(frssss)+20, privavye_Label.height-25, 90, 25)
if frame1.contains(location) {
Print("点击了隐私")
ppClick()
}else if frame2.contains(location){
Print("点击了terms")
touClick()
}
}
private func ppClick() {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
let vc:PrivacyPolicyWebViewController = PrivacyPolicyWebViewController()
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
}
private func touClick() {
DispatchQueue.main.async {[weak self] in
guard let self else {return}
let vc:TermOfUseWebViewController = TermOfUseWebViewController()
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
}
@objc func recoveryBtnAction(){
IAPManager.share.restore { result in
}
}
}
// 在文件末尾添加
extension PayDistanceViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// 允许手势同时识别
return true
}
} }
...@@ -11,20 +11,20 @@ ...@@ -11,20 +11,20 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="EwM-B0-fXo" customClass="PMPermissionView" customModule="PhoneManager" customModuleProvider="target"> <view contentMode="scaleToFill" id="EwM-B0-fXo" customClass="PMPermissionView" customModule="PhoneManager" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="543" height="449"/> <rect key="frame" x="0.0" y="0.0" width="470" height="337"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_queshengtu" translatesAutoresizingMaskIntoConstraints="NO" id="qhV-IF-vsx"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_queshengtu" translatesAutoresizingMaskIntoConstraints="NO" id="qhV-IF-vsx">
<rect key="frame" x="177" y="100" width="189" height="189"/> <rect key="frame" x="140.66666666666666" y="0.0" width="188.99999999999997" height="189"/>
</imageView> </imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Access permission is required to start scan" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ci8-h6-fiE"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Access permission is required to start scan" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ci8-h6-fiE">
<rect key="frame" x="5" y="297" width="533" height="20"/> <rect key="frame" x="5" y="197" width="460" height="20"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Phone Manager We need access to all the photos in your photo library" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e8v-Xy-wvP"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Phone Manager We need access to all the photos in your photo library" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e8v-Xy-wvP">
<rect key="frame" x="109" y="320" width="325" height="38.333333333333314"/> <rect key="frame" x="72.666666666666686" y="220" width="325" height="38.333333333333314"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="325" id="7Q3-fW-f7G"/> <constraint firstAttribute="width" constant="325" id="7Q3-fW-f7G"/>
</constraints> </constraints>
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fRi-wi-J8I"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fRi-wi-J8I">
<rect key="frame" x="177" y="371.33333333333331" width="189" height="46"/> <rect key="frame" x="140.66666666666666" y="271.33333333333331" width="188.99999999999997" height="46"/>
<color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.50980392159999999" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="46" id="2ml-bk-VZ7"/> <constraint firstAttribute="height" constant="46" id="2ml-bk-VZ7"/>
...@@ -59,14 +59,14 @@ ...@@ -59,14 +59,14 @@
<constraint firstItem="ci8-h6-fiE" firstAttribute="leading" secondItem="Z86-W8-437" secondAttribute="leading" constant="5" id="2pS-Hl-sgb"/> <constraint firstItem="ci8-h6-fiE" firstAttribute="leading" secondItem="Z86-W8-437" secondAttribute="leading" constant="5" id="2pS-Hl-sgb"/>
<constraint firstItem="ci8-h6-fiE" firstAttribute="centerX" secondItem="qhV-IF-vsx" secondAttribute="centerX" id="Fv3-iO-5BI"/> <constraint firstItem="ci8-h6-fiE" firstAttribute="centerX" secondItem="qhV-IF-vsx" secondAttribute="centerX" id="Fv3-iO-5BI"/>
<constraint firstItem="e8v-Xy-wvP" firstAttribute="centerX" secondItem="ci8-h6-fiE" secondAttribute="centerX" id="OZD-Bq-U2s"/> <constraint firstItem="e8v-Xy-wvP" firstAttribute="centerX" secondItem="ci8-h6-fiE" secondAttribute="centerX" id="OZD-Bq-U2s"/>
<constraint firstItem="qhV-IF-vsx" firstAttribute="top" secondItem="EwM-B0-fXo" secondAttribute="top" constant="100" id="fx5-wC-f0q"/> <constraint firstItem="qhV-IF-vsx" firstAttribute="top" secondItem="EwM-B0-fXo" secondAttribute="top" id="fx5-wC-f0q"/>
<constraint firstItem="fRi-wi-J8I" firstAttribute="top" secondItem="e8v-Xy-wvP" secondAttribute="bottom" constant="13" id="jys-rP-uUI"/> <constraint firstItem="fRi-wi-J8I" firstAttribute="top" secondItem="e8v-Xy-wvP" secondAttribute="bottom" constant="13" id="jys-rP-uUI"/>
<constraint firstItem="e8v-Xy-wvP" firstAttribute="top" secondItem="ci8-h6-fiE" secondAttribute="bottom" constant="3" id="tgi-kq-3Ap"/> <constraint firstItem="e8v-Xy-wvP" firstAttribute="top" secondItem="ci8-h6-fiE" secondAttribute="bottom" constant="3" id="tgi-kq-3Ap"/>
<constraint firstItem="ci8-h6-fiE" firstAttribute="top" secondItem="qhV-IF-vsx" secondAttribute="bottom" constant="8" id="uKW-kw-KCJ"/> <constraint firstItem="ci8-h6-fiE" firstAttribute="top" secondItem="qhV-IF-vsx" secondAttribute="bottom" constant="8" id="uKW-kw-KCJ"/>
<constraint firstItem="Z86-W8-437" firstAttribute="trailing" secondItem="ci8-h6-fiE" secondAttribute="trailing" constant="5" id="wMs-cZ-eGL"/> <constraint firstItem="Z86-W8-437" firstAttribute="trailing" secondItem="ci8-h6-fiE" secondAttribute="trailing" constant="5" id="wMs-cZ-eGL"/>
</constraints> </constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="295.41984732824426" y="-329.22535211267609"/> <point key="canvasLocation" x="239.69465648854961" y="-368.66197183098592"/>
</view> </view>
</objects> </objects>
<resources> <resources>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -34,3 +34,31 @@ class FadeOutTransition: NSObject, UIViewControllerAnimatedTransitioning { ...@@ -34,3 +34,31 @@ class FadeOutTransition: NSObject, UIViewControllerAnimatedTransitioning {
} }
} }
} }
// 自定义转场动画(用于 pop 时的渐隐效果)
class FadeOutPopTransition: 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 }
// 将目标控制器(RootViewController)的视图添加到容器视图
transitionContext.containerView.addSubview(toVC.view)
// 渐隐动画:当前控制器视图透明度从 1 降到 0
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
fromVC.view.alpha = 0
}) { (_) in
// 动画完成后,标记转场结束
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
...@@ -15,4 +15,8 @@ extension NSNotification.Name { ...@@ -15,4 +15,8 @@ extension NSNotification.Name {
//监听拿到基本相册资源 //监听拿到基本相册资源
static let getBaseAssetsSuccess: NSNotification.Name = NSNotification.Name(rawValue: "getBaseAssetsSuccess") static let getBaseAssetsSuccess: NSNotification.Name = NSNotification.Name(rawValue: "getBaseAssetsSuccess")
//首次打开引导页关闭
static let guidePageClose: NSNotification.Name = NSNotification.Name(rawValue: "guidePageClose")
} }
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