Commit 9febb62f authored by CZ1004's avatar CZ1004

Merge branch 'dev_main' into dev_zhaoqian

* dev_main:
  处理退款,修复保留bug,首页视频播放
  优化图片加载器
  订阅增加类型获取,新增年费会员
  年费会员界面,启动引导页结构更改
parents 78402c67 10caad0a
...@@ -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))
...@@ -52,6 +58,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -52,6 +58,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// 相册基本资源加载 // 相册基本资源加载
PhotoManager.shared.config() PhotoManager.shared.config()
// 设置app动态按钮
setupDynamicShortcuts()
return true return true
} }
...@@ -128,20 +136,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -128,20 +136,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()
}
} }
} }
......
//
// 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":
// 执行相应操作
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
rt.present(vc, animated: true)
}
}
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
}
}
...@@ -2,7 +2,12 @@ import UIKit ...@@ -2,7 +2,12 @@ import UIKit
import Photos import Photos
// 图片缓存管理(单例) // 图片缓存管理(单例)
private let imageCache = NSCache<NSString, UIImage>() private let imageCache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.totalCostLimit = 50 * 1024 * 1024 // 50MB
cache.countLimit = 100 // 最多缓存100张图片
return cache
}()
extension UIImageView { extension UIImageView {
// 关联对象键,用于存储当前请求的 localIdentifier // 关联对象键,用于存储当前请求的 localIdentifier
...@@ -34,7 +39,7 @@ extension UIImageView { ...@@ -34,7 +39,7 @@ extension UIImageView {
// 加载图片的主方法 // 加载图片的主方法
func loadImage( func loadImage(
withLocalIdentifier identifier: String, withLocalIdentifier identifier: String,
targetSize: CGSize = PHImageManagerMaximumSize, targetSize: CGSize = CGSize(width: 300, height: 300),
placeholder: UIImage? = nil, placeholder: UIImage? = nil,
completion: ((UIImage?) -> Void)? = nil completion: ((UIImage?) -> Void)? = nil
) { ) {
...@@ -63,13 +68,23 @@ extension UIImageView { ...@@ -63,13 +68,23 @@ extension UIImageView {
completion?(nil) completion?(nil)
return return
} }
// 6. 配置图片请求选项 // 6. 配置图片请求选项
let options = PHImageRequestOptions() let options = PHImageRequestOptions()
options.version = .current options.version = .current
options.deliveryMode = .highQualityFormat options.deliveryMode = .highQualityFormat // 改为渐进式加载
options.isNetworkAccessAllowed = true options.isNetworkAccessAllowed = true
options.resizeMode = .exact options.resizeMode = .fast // 改为快速加载模式
options.isSynchronous = false
options.progressHandler = { progress, error, stop, info in
DispatchQueue.main.async {
// 可以在这里添加加载进度的处理
// print("Loading progress: \(progress)")
if let error = error {
print("Loading error: \(error.localizedDescription)")
}
}
}
// 7. 请求图片 // 7. 请求图片
currentRequestID = PHImageManager.default().requestImage( currentRequestID = PHImageManager.default().requestImage(
...@@ -116,9 +131,18 @@ extension UIImageView { ...@@ -116,9 +131,18 @@ extension UIImageView {
} }
} }
// 清理缓存
@objc func clearCacheOnMemoryWarning() {
imageCache.removeAllObjects()
}
// 命名空间 // 命名空间
struct AssetLoader { struct AssetLoader {
fileprivate weak var imageView: UIImageView? fileprivate weak var imageView: UIImageView?
func clearImageCache() {
imageCache.removeAllObjects()
}
} }
var asset: AssetLoader { var asset: AssetLoader {
......
...@@ -34,6 +34,12 @@ enum BaseDataLoadingState { ...@@ -34,6 +34,12 @@ enum BaseDataLoadingState {
case failed(Error) case failed(Error)
} }
enum FileType {
case json
case plist
case all
}
class PhotoManager{ class PhotoManager{
...@@ -137,9 +143,14 @@ class PhotoManager{ ...@@ -137,9 +143,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 }
weakSelf.getBaseAssetGroup() if permission{
weakSelf.getBaseAssetGroup()
}else{
//weakSelf.clearLocalData()
}
} }
} }
...@@ -204,6 +215,7 @@ class PhotoManager{ ...@@ -204,6 +215,7 @@ class PhotoManager{
let start = CFAbsoluteTimeGetCurrent() let start = CFAbsoluteTimeGetCurrent()
self.videoModels = await convertAssetsToModel(for: self.videoAssets, mediaType: 2) self.videoModels = await convertAssetsToModel(for: self.videoAssets, mediaType: 2)
self.videoModels.sort { $0.createDate > $1.createDate }
let duration = CFAbsoluteTimeGetCurrent() - start let duration = CFAbsoluteTimeGetCurrent() - start
print("视频转换总耗时: \(duration)秒") print("视频转换总耗时: \(duration)秒")
let videoTotalSize = Int64(self.videoModels.reduce(0){$0+$1.assetSize}) let videoTotalSize = Int64(self.videoModels.reduce(0){$0+$1.assetSize})
...@@ -233,6 +245,7 @@ class PhotoManager{ ...@@ -233,6 +245,7 @@ class PhotoManager{
let start = CFAbsoluteTimeGetCurrent() let start = CFAbsoluteTimeGetCurrent()
self.otherModels = await convertAssetsToModel(for: self.otherAssets, mediaType: 1) self.otherModels = await convertAssetsToModel(for: self.otherAssets, mediaType: 1)
self.otherModels.sort { $0.createDate > $1.createDate }
let duration = CFAbsoluteTimeGetCurrent() - start let duration = CFAbsoluteTimeGetCurrent() - start
print("其他图片转换总耗时: \(duration)秒") print("其他图片转换总耗时: \(duration)秒")
let otherTotalSize = Int64(self.otherModels.reduce(0){$0+$1.assetSize}) let otherTotalSize = Int64(self.otherModels.reduce(0){$0+$1.assetSize})
...@@ -258,11 +271,11 @@ class PhotoManager{ ...@@ -258,11 +271,11 @@ class PhotoManager{
self.screenShotTotalSize = screenShotTotalSize self.screenShotTotalSize = screenShotTotalSize
complectionHandler?(localModels, screenShotTotalSize) complectionHandler?(localModels, screenShotTotalSize)
} }
return
} }
let start = CFAbsoluteTimeGetCurrent() let start = CFAbsoluteTimeGetCurrent()
self.screenShotModels = await convertAssetsToModel(for: self.screenShotAssets, mediaType: 1) self.screenShotModels = await convertAssetsToModel(for: self.screenShotAssets, mediaType: 1)
self.screenShotModels.sort { $0.createDate > $1.createDate }
let duration = CFAbsoluteTimeGetCurrent() - start let duration = CFAbsoluteTimeGetCurrent() - start
print("截图转换总耗时: \(duration)秒") print("截图转换总耗时: \(duration)秒")
let screenShotTotalSize = Int64(self.screenShotModels.reduce(0){$0+$1.assetSize}) let screenShotTotalSize = Int64(self.screenShotModels.reduce(0){$0+$1.assetSize})
...@@ -513,7 +526,7 @@ extension PhotoManager{ ...@@ -513,7 +526,7 @@ extension PhotoManager{
options.version = .original options.version = .original
options.deliveryMode = .highQualityFormat options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true options.isNetworkAccessAllowed = true
// 4. 请求视频资源 // 4. 请求视频资源
PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { (avAsset, _, _) in PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { (avAsset, _, _) in
DispatchQueue.main.async { DispatchQueue.main.async {
...@@ -748,6 +761,11 @@ extension PhotoManager{ ...@@ -748,6 +761,11 @@ 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 {
...@@ -107,7 +111,7 @@ class HomeViewController:BaseViewController { ...@@ -107,7 +111,7 @@ class HomeViewController:BaseViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
loadLaunchView()
self.setupUI() self.setupUI()
// 调用下追踪权限 // 调用下追踪权限
...@@ -181,7 +185,66 @@ class HomeViewController:BaseViewController { ...@@ -181,7 +185,66 @@ 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()
luanch.disMissBlock = {[weak self] in
guard let weakSelf = self else { return }
guard UserDef.shard.isShowLanding else{
return
}
weakSelf.showIAPVC()
}
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)
}
}
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)
...@@ -203,6 +266,21 @@ class HomeViewController:BaseViewController { ...@@ -203,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 {
...@@ -259,3 +337,4 @@ extension HomeViewController { ...@@ -259,3 +337,4 @@ extension HomeViewController {
} }
} }
} }
...@@ -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()
} }
......
...@@ -135,11 +135,4 @@ class HomeVideoDetailCustomHeaderView : UICollectionReusableView { ...@@ -135,11 +135,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)
}
} }
...@@ -51,7 +51,7 @@ class HomeView:UIView { ...@@ -51,7 +51,7 @@ class HomeView:UIView {
sview.register(HomeTitleCollectionCell.self, forCellWithReuseIdentifier: HomeTitleCollectionCell.identifiers) sview.register(HomeTitleCollectionCell.self, forCellWithReuseIdentifier: HomeTitleCollectionCell.identifiers)
sview.register(HomeOtherCollectionCell.self, forCellWithReuseIdentifier: HomeOtherCollectionCell.identifier) sview.register(HomeOtherCollectionCell.self, forCellWithReuseIdentifier: HomeOtherCollectionCell.identifier)
sview.register(HomeVideoCoverCell.classForCoder(), forCellWithReuseIdentifier: "HomeVideoCoverCell")
sview.register(HomeCollectionViewHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HomeCollectionViewHeader") sview.register(HomeCollectionViewHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HomeCollectionViewHeader")
sview.register(UICollectionReusableView.self,forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,withReuseIdentifier: "HomeCollectionViewFooter") sview.register(UICollectionReusableView.self,forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,withReuseIdentifier: "HomeCollectionViewFooter")
...@@ -278,11 +278,19 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -278,11 +278,19 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
return cell return cell
case 1: case 1:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeOtherCollectionCell.identifier, for: indexPath) as! HomeOtherCollectionCell if viewModel.cardGroup[indexPath.row].folderName == HomeUIEnum.SimilarVideos.title || viewModel.cardGroup[indexPath.row].folderName == HomeUIEnum.Videos.title{
cell.dealMediaType(indexPath.row) let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeVideoCoverCell", for: indexPath) as! HomeVideoCoverCell
let model = viewModel.cardGroup[indexPath.row] let model = viewModel.cardGroup[indexPath.row]
cell.reloadUIWithModel(model: model) cell.reloadUIWithModel(model: model)
return cell return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeOtherCollectionCell.identifier, for: indexPath) as! HomeOtherCollectionCell
cell.dealMediaType(indexPath.row)
let model = viewModel.cardGroup[indexPath.row]
cell.reloadUIWithModel(model: model)
return cell
}
default: default:
return UICollectionViewCell() return UICollectionViewCell()
} }
...@@ -402,9 +410,9 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -402,9 +410,9 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize { func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallMutiSectionFlowLayout, section: Int) -> CGSize {
if section == 0 { if section == 0 {
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:ScreenW-(marginLR*2), height: 76 + (self.homeNavView?.height ?? 0))
}else{ }else{
return CGSize(width: self.collectionView.width, height: 404) return CGSize(width:ScreenW-(marginLR*2), height: 450)
} }
} }
return CGSize.zero return CGSize.zero
......
...@@ -12,7 +12,7 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -12,7 +12,7 @@ class HomeTitleCollectionCell:UICollectionViewCell {
static let identifiers = "HomeTitleCollectionCellID" static let identifiers = "HomeTitleCollectionCellID"
private var titleLabel: UILabel? private var titleLabel: UILabel!
public var fileLabel:UILabel? public var fileLabel:UILabel?
...@@ -48,8 +48,8 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -48,8 +48,8 @@ class HomeTitleCollectionCell:UICollectionViewCell {
titleLabel = UILabel() titleLabel = UILabel()
titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold) titleLabel.font = UIFont.systemFont(ofSize: 16, weight: .bold)
titleLabel?.textColor = UIColor.colorWithHex(hexStr: black3Color) titleLabel.textColor = UIColor.colorWithHex(hexStr: black3Color)
fileLabel = UILabel() fileLabel = UILabel()
fileLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold) fileLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
...@@ -118,8 +118,8 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -118,8 +118,8 @@ class HomeTitleCollectionCell:UICollectionViewCell {
guard let model = model else { return } guard let model = model else { return }
self.model = model self.model = model
titleLabel?.text = model.folderName titleLabel.text = model.folderName
titleLabel?.sizeToFit() titleLabel.sizeToFit()
var count = 0 var count = 0
...@@ -137,8 +137,8 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -137,8 +137,8 @@ class HomeTitleCollectionCell:UICollectionViewCell {
assetsModels = assets.compactMap({ model in assetsModels = assets.compactMap({ model in
return ImageCollectionModel.init(asset: model) return ImageCollectionModel.init(asset: model)
}) })
collectionView?.reloadData()
} }
collectionView?.reloadData()
} }
...@@ -155,12 +155,12 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -155,12 +155,12 @@ class HomeTitleCollectionCell:UICollectionViewCell {
super.layoutSubviews() super.layoutSubviews()
titleLabel?.snp.makeConstraints({ make in titleLabel.snp.makeConstraints({ make in
make.top.left.equalTo(16) make.top.left.equalTo(16)
}) })
titleLabel?.sizeToFit() titleLabel.sizeToFit()
fileLabel?.snp.makeConstraints({ make in fileLabel?.snp.makeConstraints({ make in
...@@ -172,9 +172,9 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -172,9 +172,9 @@ class HomeTitleCollectionCell:UICollectionViewCell {
collectionView?.snp.makeConstraints({ make in collectionView?.snp.makeConstraints({ make in
make.left.equalTo(16) make.left.equalTo(16)
make.bottom.equalTo(-16) make.bottom.equalTo(0)
make.right.equalTo(-16) make.right.equalTo(-16)
make.top.equalTo(48) make.top.equalTo(titleLabel.snp.bottom).offset(13)
}) })
nextImage?.snp.makeConstraints({ make in nextImage?.snp.makeConstraints({ make in
...@@ -205,7 +205,7 @@ extension HomeTitleCollectionCell:UICollectionViewDelegate,UICollectionViewDataS ...@@ -205,7 +205,7 @@ extension HomeTitleCollectionCell:UICollectionViewDelegate,UICollectionViewDataS
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 计算 cell 宽度 // 计算 cell 宽度
return CGSize(width:collectionView.height - 2.5, height: collectionView.height - 2.5) // 宽高相等,形成网格 return CGSize(width:collectionView.height - 18, height: collectionView.height - 18) // 宽高相等,形成网格
} }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
......
//
// HomeVideoCoverCell.swift
// PhoneManager
//
// Created by edy on 2025/5/20.
//
import UIKit
import AVKit
class HomeVideoCoverCell: UICollectionViewCell {
var videoUrl:URL?
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
backgroundColor = .white
self.contentView.addSubview(self.titleLabel)
self.contentView.addSubview(self.imageView)
contentView.layer.addSublayer(playerLayer)
self.contentView.addSubview(self.infoBackView)
self.infoBackView.addSubview(self.countLabel)
self.infoBackView.addSubview(self.sizeLabel)
self.infoBackView.addSubview(self.moreImageView)
}
func reloadUIWithModel(model:HomePhotosModel?){
// 1. 先重置状态
guard let model else {
playerLayer.isHidden = true
videoPlayer.pause()
return
}
// 设置标题
self.titleLabel.text = model.folderName
// 获取数量
var count = 0
for item in model.assets {
count = count + item.count
}
// 设置数量文字
self.countLabel.text = "\(count) Videos"
// 设置文件大小文字
let sizeKB : Double = model.allFileSize/1000
if sizeKB < 1000{
self.sizeLabel.text = String(format: "(%.2lf) KB" ,sizeKB)
}else if sizeKB < (1000 * 1000) && sizeKB > 1000{
self.sizeLabel.text = String(format: "(%.2lf) MB" ,sizeKB/1000)
}else{
self.sizeLabel.text = String(format: "(%.2lf) GB" ,sizeKB/(1000 * 1000))
}
// 2. 设置新数据
if let id = model.assets.first?.first?.localIdentifier{
// 保存当前的 identifier 用于验证
let currentID = id
imageView.asset.load(withLocalIdentifier:id,placeholder: UIImage(named: "videosmoren"))
PhotoManager.shared.getVideoURL(localIdentifier:id, completion: {[weak self] url in
guard let weakSelf = self else { return }
// 验证 ID 是否匹配,避免 cell 重用导致的错误
if currentID == id {
weakSelf.configure(with: url)
}
})
}else{
self.imageView.image = UIImage.init(named: "videosmoren")
playerLayer.isHidden = true
videoPlayer.pause()
}
}
public let imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 8
iv.backgroundColor = .clear
return iv
}()
private let infoBackView: UIView = {
let iv = UIView()
iv.clipsToBounds = true
iv.layer.cornerRadius = 8
iv.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
return iv
}()
public var countLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 14, weight: .medium)
label.textAlignment = .left
return label
}()
let sizeLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 10, weight: .medium)
label.textAlignment = .left
return label
}()
private let moreImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.image = UIImage(named: "icon_left_setting_grey")
iv.backgroundColor = .clear
return iv
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 16, weight: .bold)
label.textColor = UIColor.colorWithHex(hexStr: black3Color)
label.textAlignment = .left
label.numberOfLines = 0
return label
}()
override func layoutSubviews() {
self.layer.cornerRadius = 12
self.layer.masksToBounds = true
imageView.snp.makeConstraints { make in
make.top.equalTo(self.titleLabel.snp.bottom).offset(8)
make.left.equalToSuperview().offset(16)
make.width.equalToSuperview().offset(-32)
make.height.equalTo(self.width - 32)
}
self.infoBackView.snp.makeConstraints { make in
make.left.right.bottom.equalTo(imageView)
make.height.equalTo(40)
}
titleLabel.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(16)
make.width.equalToSuperview().offset(-32)
}
self.countLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.top.equalToSuperview().offset(3)
make.height.equalTo(20)
}
self.sizeLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.top.equalTo(countLabel.snp.bottom).offset(-2)
make.height.equalTo(14)
}
self.moreImageView.snp.makeConstraints { make in
make.width.height.equalTo(20)
make.right.equalToSuperview().offset(-12)
make.centerY.equalToSuperview()
}
playerLayer.frame = imageView.frame
playerLayer.cornerRadius = 8
playerLayer.masksToBounds = true
}
lazy var videoPlayer:AVPlayer = {
let palyer = AVPlayer.init()
palyer.volume = 0
// 设置音频会话,允许混音
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)
try? AVAudioSession.sharedInstance().setActive(true)
return palyer
}()
lazy var playerLayer:AVPlayerLayer = {
let playerLayer = AVPlayerLayer.init(player: videoPlayer)
playerLayer.backgroundColor = UIColor.black.cgColor
playerLayer.isHidden = true
return playerLayer
}()
deinit{
NotificationCenter.default.removeObserver(self)
}
}
extension HomeVideoCoverCell{
func configure(with videoURL: URL?) {
guard let videoURL = videoURL else{
playerLayer.isHidden = true
videoPlayer.pause()
return
}
playerLayer.isHidden = false
if videoURL == videoUrl{
Print("地址相同,无需刷新")
return
}
videoUrl = videoURL
Print("地址不同,需刷新")
let item = AVPlayerItem.init(url: videoURL)
videoPlayer.replaceCurrentItem(with: item)
videoPlayer.play()
NotificationCenter.default.addObserver(self,
selector: #selector(playerDidFinishPlaying),
name: .AVPlayerItemDidPlayToEndTime,
object: item)
}
@objc private func playerDidFinishPlaying() {
videoPlayer.seek(to: .zero)
videoPlayer.play()
}
}
...@@ -121,6 +121,10 @@ class HomeViewModel { ...@@ -121,6 +121,10 @@ class HomeViewModel {
return return
} }
guard photoManager.permissionStatus == .authorized else{
return
}
totalFilesCount = photoManager.allAssets.count totalFilesCount = photoManager.allAssets.count
hadLoad = true hadLoad = true
......
...@@ -80,6 +80,7 @@ class MaintaiDetailViewController: BaseViewController { ...@@ -80,6 +80,7 @@ class MaintaiDetailViewController: BaseViewController {
if isSelectAll{ if isSelectAll{
let ids = viewModel.souces.flatMap{$0}.compactMap{$0.localIdentifier} let ids = viewModel.souces.flatMap{$0}.compactMap{$0.localIdentifier}
if GroupDatabase.shared.batchDelete(localIdentifiers: ids){ if GroupDatabase.shared.batchDelete(localIdentifiers: ids){
selectAsset.removeAll()
self.navigationController?.popViewController(animated: true) self.navigationController?.popViewController(animated: true)
} }
isSelectAll = false isSelectAll = false
...@@ -175,7 +176,7 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{ ...@@ -175,7 +176,7 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{
} }
// 拿到当前在选中数据的下标 // 拿到当前在选中数据的下标
let selectIndex = selectAsset.firstIndex(where: { var selectIndex = selectAsset.firstIndex(where: {
$0 == cell.selectAsset $0 == cell.selectAsset
}) })
...@@ -191,11 +192,15 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{ ...@@ -191,11 +192,15 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{
weakSelf.selectAsset[sIndex] = selects weakSelf.selectAsset[sIndex] = selects
}else{ }else{
weakSelf.selectAsset.remove(at: sIndex) weakSelf.selectAsset.remove(at: sIndex)
selectIndex = nil
} }
}else{ }else{
Print("没有任何选中数据") Print("没有任何选中数据")
weakSelf.selectAsset.append(selects) weakSelf.selectAsset.append(selects)
selectIndex = weakSelf.selectAsset.firstIndex(where: {
$0 == selects
})
} }
weakSelf.dealMutilImagePick() weakSelf.dealMutilImagePick()
} }
...@@ -238,8 +243,10 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{ ...@@ -238,8 +243,10 @@ extension MaintaiDetailViewController:UITableViewDelegate,UITableViewDataSource{
}else{ }else{
maintaiBottomView.disMiss() maintaiBottomView.disMiss()
} }
// tableView.reloadData()
tableView.reloadData() // UIView.performWithoutAnimation {
// self.tableView.reloadData()
// }
} }
func dealPickAssetModel(index:Int){ func dealPickAssetModel(index:Int){
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
<rect key="frame" x="0.0" y="0.0" width="269" height="253"/> <rect key="frame" x="0.0" y="0.0" width="269" height="253"/>
</imageView> </imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oJ5-js-pjw"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oJ5-js-pjw">
<rect key="frame" x="234" y="213" width="30" height="30"/> <rect key="frame" x="235" y="219" width="34" height="34"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="30" id="gGs-ha-T24"/> <constraint firstAttribute="width" constant="34" id="gGs-ha-T24"/>
<constraint firstAttribute="height" constant="30" id="mvi-bY-CFY"/> <constraint firstAttribute="height" constant="34" id="mvi-bY-CFY"/>
</constraints> </constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="icon_maintai_unselect_big"/> <state key="normal" image="icon_maintai_unselect_big"/>
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/> <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
<constraints> <constraints>
<constraint firstAttribute="bottom" secondItem="T4a-zL-UKQ" secondAttribute="bottom" id="B7R-M8-dpU"/> <constraint firstAttribute="bottom" secondItem="T4a-zL-UKQ" secondAttribute="bottom" id="B7R-M8-dpU"/>
<constraint firstAttribute="bottom" secondItem="oJ5-js-pjw" secondAttribute="bottom" constant="10" id="BfF-Td-Tcc"/> <constraint firstAttribute="bottom" secondItem="oJ5-js-pjw" secondAttribute="bottom" id="BfF-Td-Tcc"/>
<constraint firstAttribute="trailing" secondItem="oJ5-js-pjw" secondAttribute="trailing" constant="5" id="Iw9-UA-zNe"/> <constraint firstAttribute="trailing" secondItem="oJ5-js-pjw" secondAttribute="trailing" id="Iw9-UA-zNe"/>
<constraint firstItem="T4a-zL-UKQ" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="MoV-lR-Hqz"/> <constraint firstItem="T4a-zL-UKQ" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="MoV-lR-Hqz"/>
<constraint firstItem="T4a-zL-UKQ" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="dJs-Pa-MhO"/> <constraint firstItem="T4a-zL-UKQ" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="dJs-Pa-MhO"/>
<constraint firstAttribute="trailing" secondItem="T4a-zL-UKQ" secondAttribute="trailing" id="gyj-E3-guF"/> <constraint firstAttribute="trailing" secondItem="T4a-zL-UKQ" secondAttribute="trailing" id="gyj-E3-guF"/>
......
...@@ -43,7 +43,7 @@ class MaintaiDetailImageSmallCell: UICollectionViewCell { ...@@ -43,7 +43,7 @@ class MaintaiDetailImageSmallCell: UICollectionViewCell {
selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside) selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside)
selectBtn.snp.makeConstraints { make in selectBtn.snp.makeConstraints { make in
make.right.bottom.equalTo(0) make.right.bottom.equalTo(0)
make.size.equalTo(20) make.size.equalTo(30)
} }
} }
......
...@@ -58,8 +58,8 @@ class MaintaiDetailTableViewCell: UITableViewCell { ...@@ -58,8 +58,8 @@ class MaintaiDetailTableViewCell: UITableViewCell {
func configChild(){ func configChild(){
let layout = UICollectionViewFlowLayout() let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: smallW, height: smallW) layout.itemSize = CGSize(width: smallW, height: smallW)
layout.minimumLineSpacing = 8 layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 8 layout.minimumInteritemSpacing = 12
layout.sectionInset = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) layout.sectionInset = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)
layout.scrollDirection = .horizontal layout.scrollDirection = .horizontal
...@@ -135,8 +135,9 @@ extension MaintaiDetailTableViewCell:UIScrollViewDelegate{ ...@@ -135,8 +135,9 @@ extension MaintaiDetailTableViewCell:UIScrollViewDelegate{
Print("主图滑动距离",scrollView.contentOffset.x) Print("主图滑动距离",scrollView.contentOffset.x)
let index = Int(scrollView.contentOffset.x/bigW) let index = Int(scrollView.contentOffset.x/bigW)
Print("小图滑动到下标\(index)") Print("小图滑动到下标\(index)")
let space:CGFloat = (self.smallW + 12) * CGFloat(index)
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
self.pageCollectionView.contentOffset = CGPoint(x: index * Int(self.smallW+8)+4, y: 0) self.pageCollectionView.contentOffset = CGPoint(x:space, y: 0)
} }
} }
} }
...@@ -183,8 +184,9 @@ extension MaintaiDetailTableViewCell:UIScrollViewDelegate{ ...@@ -183,8 +184,9 @@ extension MaintaiDetailTableViewCell:UIScrollViewDelegate{
Print("小图滑动距离",offsetX) Print("小图滑动距离",offsetX)
let index = Int((offsetX/(smallW+12)).rounded()) //Int(offsetX/(smallW+12).rounded()) //rounded(offsetX/smallW) let index = Int((offsetX/(smallW+12)).rounded()) //Int(offsetX/(smallW+12).rounded()) //rounded(offsetX/smallW)
Print("大图滑动到下标\(index)") Print("大图滑动到下标\(index)")
let space:CGFloat = (self.smallW + 12) * CGFloat(index)
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
self.pageCollectionView.contentOffset = CGPoint(x: index * Int(self.smallW+8)+4, y: 0) self.pageCollectionView.contentOffset = CGPoint(x: space, y: 0)
} }
self.mainCollectionView.contentOffset = CGPoint(x: index * Int(self.bigW), y: 0) self.mainCollectionView.contentOffset = CGPoint(x: index * Int(self.bigW), y: 0)
draggView = .null draggView = .null
......
...@@ -37,23 +37,20 @@ class MaintaiDetialVideoCell: UITableViewCell { ...@@ -37,23 +37,20 @@ class MaintaiDetialVideoCell: UITableViewCell {
contentView.bringSubviewToFront(selectBtn) contentView.bringSubviewToFront(selectBtn)
selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside) selectBtn.addTarget(self, action: #selector(selectChange), for: .touchUpInside)
selectBtn.snp.makeConstraints { make in selectBtn.snp.makeConstraints { make in
make.right.equalTo(-15) make.right.equalTo(-22)
make.bottom.equalTo(-10) make.bottom.equalTo(0)
make.size.equalTo(30) make.size.equalTo(34)
} }
} }
private func setupPlayer() { private func setupPlayer() {
// 创建播放器层
// playerLayer = AVPlayerLayer()
// playerLayer?.videoGravity = .resizeAspect
contentView.layer.addSublayer(playerLayer) contentView.layer.addSublayer(playerLayer)
} }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
// 设置播放器层的frame // 设置播放器层的frame
playerLayer.frame = CGRect(x: 15, y: 10, width: contentView.width-30, height: contentView.height-20) playerLayer.frame = CGRect(x: 22, y: 10, width: contentView.width-44, height: contentView.height-10)
playerLayer.cornerRadius = 15 playerLayer.cornerRadius = 15
playerLayer.masksToBounds = true playerLayer.masksToBounds = true
} }
...@@ -92,53 +89,34 @@ class MaintaiDetialVideoCell: UITableViewCell { ...@@ -92,53 +89,34 @@ class MaintaiDetialVideoCell: UITableViewCell {
return return
} }
if videoPlayer.rate != 0{
return
}
let item = AVPlayerItem.init(url: videoURL) let item = AVPlayerItem.init(url: videoURL)
videoPlayer.replaceCurrentItem(with: item) videoPlayer.replaceCurrentItem(with: item)
videoPlayer.play() videoPlayer.play()
// // 创建播放器项
// let playerItem = AVPlayerItem(url: videoURL)
//
// // 创建播放器
// player = AVPlayer(playerItem: playerItem)
// playerLayer?.player = player
//
// // 设置静音
// player?.volume = 0
//
// // 播放视频
// player?.play()
//
// 添加播放完成的观察者
NotificationCenter.default.addObserver(self, NotificationCenter.default.addObserver(self,
selector: #selector(playerDidFinishPlaying), selector: #selector(playerDidFinishPlaying),
name: .AVPlayerItemDidPlayToEndTime, name: .AVPlayerItemDidPlayToEndTime,
object: item) object: item)
} }
//
@objc private func playerDidFinishPlaying() { @objc private func playerDidFinishPlaying() {
// // 播放结束后不做任何操作,因为只需要播放一次
// player?.pause()
// player?.seek(to: .zero)
//playerLayer.player?.seek(to: .zero)
videoPlayer.seek(to: .zero) videoPlayer.seek(to: .zero)
videoPlayer.play() videoPlayer.play()
} }
//
// override func prepareForReuse() {
// super.prepareForReuse()
// // 清理播放器
// player?.pause()
// player = nil
// NotificationCenter.default.removeObserver(self)
// }
//
@objc func selectChange(){ @objc func selectChange(){
selectChangeBlock?() selectChangeBlock?()
} }
deinit{
NotificationCenter.default.removeObserver(self)
}
} }
...@@ -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()
} }
...@@ -81,24 +82,27 @@ class NewGuideViewController: UIViewController { ...@@ -81,24 +82,27 @@ 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
//
// let transition = CATransition()
// transition.duration = 0.5
// transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
//
// // 添加动画到 window 的 layer
// cWindow?.layer.add(transition, forKey: kCATransition)
//
// // 显示 window
// cWindow?.makeKeyAndVisible()
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()
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{
...@@ -145,3 +149,10 @@ extension NewGuideViewController:UICollectionViewDelegate,UICollectionViewDataSo ...@@ -145,3 +149,10 @@ extension NewGuideViewController:UICollectionViewDelegate,UICollectionViewDataSo
} }
} }
extension NewGuideViewController: UIViewControllerTransitioningDelegate {
func animationController(forDismissed dismissedController: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// pop 时触发消失动画(等同于 popToRootViewController)
return FadeOutPopTransition()
}
}
//
// HomeLaunchView.swift
// PhoneManager
//
// Created by edy on 2025/5/19.
//
import UIKit
import Lottie
class HomeLaunchView:UIView {
var disMissBlock:(() ->Void)?
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()
}
self.disMissBlock?()
})
}
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
}()
}
...@@ -46,8 +46,11 @@ class HomePayDueView: UIView { ...@@ -46,8 +46,11 @@ class HomePayDueView: UIView {
func reloadUI(_ type:Int,week:SKProduct?,life:SKProduct?){ func reloadUI(_ type:Int,week:SKProduct?,life:SKProduct?){
guard let pord = week,let pord1 = life else { return } guard let pord = week,let pord1 = life else {
self.isHidden = true
return
}
self.isHidden = false
if type == 0{ if type == 0{
// 免费试用订阅 // 免费试用订阅
dueDay.text = "Due today" dueDay.text = "Due today"
......
//
// 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
}
*/
}
...@@ -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)
} }
} }
<?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>
...@@ -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>
......
...@@ -70,4 +70,8 @@ struct FileTool{ ...@@ -70,4 +70,8 @@ struct FileTool{
return totalSize return totalSize
} }
} }
...@@ -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)
}
}
}
// 自定义转场动画(用于 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