// // MPPositive_HomeViewController.swift // MusicPlayer // // Created by Mr.Zhou on 2024/4/19. // import UIKit import MJRefresh class MPPositive_HomeViewController: MPPositive_BaseViewController, UIViewControllerTransitioningDelegate{ //背景图片 private lazy var bgImageView:UIImageView = { let imageView:UIImageView = .init(image: .init(named: "B_Home_BG'bg")) imageView.contentMode = .scaleAspectFill return imageView }() //菜单按钮 private lazy var memuBtn:UIButton = { let btn = UIButton() btn.setBackgroundImage(UIImage(named: "Home_Menu'logo"), for: .normal) btn.addTarget(self, action: #selector(menuRightClick(_ :)), for: .touchUpInside) return btn }() //ViP按钮 private lazy var vipImageView:UIImageView = { let imageView:UIImageView = UIImageView(image: .init(named: "VIP'logo")) imageView.isUserInteractionEnabled = true imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(vipIAPClick(_ :)))) return imageView }() //斜向标题Label private lazy var musicLaxLabel:UILabel = { let label = UILabel() label.text = "MusicLax" // Create a bold italic font if let boldItalicFontDescriptor = UIFont.systemFont(ofSize: 20*width, weight: .bold).fontDescriptor.withSymbolicTraits([.traitBold, .traitItalic]) { let boldItalicFont = UIFont(descriptor: boldItalicFontDescriptor, size: 20*width) label.font = boldItalicFont } else { label.font = UIFont.boldSystemFont(ofSize: 20*width) } label.textColor = .white label.isUserInteractionEnabled = true label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(scrollTopAction(_ :)))) return label }() //搜索栏 private lazy var searchView = createSearchView() //导航栏内购是否展示 private var isBuyShow:Bool = true //最后一次偏移量 private var lastContentOffset: CGFloat = 0 //是否用户滚动 private var isUserDragging:Bool = false //tableView private lazy var tableView:UITableView = { let tableView = UITableView(frame: .init(x: 0, y: 0, width: screen_Width, height: screen_Height), style: .plain) tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.estimatedRowHeight = 200 tableView.rowHeight = UITableView.automaticDimension tableView.dataSource = self tableView.delegate = self tableView.register(MPPositive_HomeLibraryListstableViewCell.self, forCellReuseIdentifier: MPPositive_HomeLibraryListstableViewCellID) tableView.register(MPPositive_HomeSinglesTableViewCell.self, forCellReuseIdentifier: MPPositive_HomeSinglesTableViewCellID) tableView.register(MPPositive_HomeShowTableViewCell.self, forCellReuseIdentifier: MPPositive_HomeShowTableViewCellID) tableView.contentInset = .init(top: 0, left: 0, bottom: 70*width, right: 0) //添加一个下拉刷新 let header = MJRefreshGifHeader { [weak self] in self?.pullDownRefreshAction() } header.setTitle("Pull down", for: .idle) header.setTitle("Release Refresh", for: .pulling) header.setTitle("Loading...", for: .refreshing) tableView.mj_header = header return tableView }() private let MPPositive_HomeLibraryListstableViewCellID = "MPPositive_HomeLibraryListstableViewCell" private let MPPositive_HomeSinglesTableViewCellID = "MPPositive_HomeSinglesTableViewCell" private let MPPositive_HomeShowTableViewCellID = "MPPositive_HomeShowTableViewCell" private var isFirstAppearance:Bool = true //是否允许下拉刷新 private var isCountdownActive = false //下拉刷新间隔倒计时器 private var countdownTimer: Timer? //下拉刷新间隔时间 private var countdownValue = 15 // private var loadViewModel:MPPositive_BrowseLoadViewModel! override func viewDidLoad() { super.viewDidLoad() DispatchQueue.main.asyncAfter(deadline: .now() + 3) { // 请求跟踪授权 requestTrackingAuthorization { idfa in } } MP_IAPManager.shared.systemRestorePurchases() setTitle("") confirgue() if MPPositive_BrowseLoadViewModel.shared.browseModuleLists.count == 0 { MP_HUD.loading() } MP_AdMobManager.shared.configureLibraryNativeAd(rootController: self) MP_AdMobManager.shared.loadLibraryNativeAd() //加载搜索原生广告 MP_AdMobManager.shared.configureSreachNativeAd(rootController: self) MP_AdMobManager.shared.loadSearchNativeAd() errorBlock = { [weak self] in guard let self = self else { return } guard MPPositive_BrowseLoadViewModel.shared.browseModuleLists.count == 0 else { return } //移除所有(除了navView)的子控件,并告知页面处理错误,提示用户重试 view.subviews.forEach { item in if item != self.navView { //移除 if item.superview != nil { item.removeFromSuperview() } } } //添加报错View setErrorView() MP_HUD.hideNow() } retryBlock = { [weak self] in guard let self = self else {return} MP_HUD.loading() DispatchQueue.main.async { //获取首页 MPPositive_BrowseLoadViewModel.shared.reloadBrowseLists() } } //当home展示后尝试获取最后一次播放歌曲内容 guard let last = UserDefaults.standard.object(forKey: "Last_Play_Songs") as? [String:Any] else {return} guard let currentVideoId = last["currentVideoId"] as? String, let data = last["Songs"] as? Data, let array = jsonforCoreSongs(data) else {return} //数据获取完成,使用这些数据合成一个Load let lodaViewModel = MPPositive_PlayerLoadViewModel(array, currentVideoId: currentVideoId) lodaViewModel.improveData(currentVideoId) //更改播放器播放类型 MP_PlayerManager.shared.setPlayType(.normal) MP_PlayerManager.shared.setLastStatus(bool: true) MP_PlayerManager.shared.loadPlayer = lodaViewModel } deinit { NotificationCenter.default.removeObserver(self) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.notificationKey.add(observer: self, selector: #selector(reloadAction(_ :)), notificationName: .positive_browses_reload) MPPositive_BrowseLoadViewModel.shared.libraryList.reloadLibrarys{ [weak self] in guard let self = self else {return} reloadAction(nil) } if isFirstAppearance == false { // 第二次展示的逻辑,可触发相应的方法 presentGuide() isFirstAppearance = true } MP_AnalyticsManager.shared.home_b_pvAction() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } //生成一个顶部搜索框 private func createSearchView() -> UIView{ let searchView:UIView = UIView() searchView.backgroundColor = .init(hex: "#212121") searchView.isUserInteractionEnabled = true searchView.layer.masksToBounds = true searchView.layer.cornerRadius = 16*width //添加一个icon let iconImageView = UIImageView(image: .init(named: "Search_ICON'logo")) searchView.addSubview(iconImageView) iconImageView.snp.makeConstraints { make in make.height.width.equalTo(16*width) make.left.equalToSuperview().offset(16*width) make.centerY.equalToSuperview() } let label = createLabel("Search songs,artists,playlists", font: .systemFont(ofSize: 14*width, weight: .regular), textColor: .init(hex: "#666666"), textAlignment: .left) searchView.addSubview(label) label.snp.makeConstraints { make in make.left.equalTo(iconImageView.snp.right).offset(8*width) make.centerY.equalToSuperview() } searchView.isUserInteractionEnabled = true searchView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(searchClick(_:)))) return searchView } //前往搜索结果页 @objc fileprivate func searchClick(_ sender:UITapGestureRecognizer) { isFirstAppearance = false MP_AnalyticsManager.shared.search_from_actionAction("home page") let resultVC = MPPositive_SearchResultShowViewController() navigationController?.pushViewController(resultVC, animated: false) } ///弹出好评引导框 func presentGuide() { guard UserDefaults.standard.bool(forKey: "isGuide") != true else { //已经评论过了 return } //检索活跃天数是否达到3天 guard var actives = UserDefaults.standard.object(forKey: "ActiveDays") as? [Date], actives.count >= 3 else { return } //达到3天,可以弹出好评引导框 MPPositive_ModalType = .Guide let guideVC = MP_GuideViewController() guideVC.disMissBlock = { DispatchQueue.main.async { //移除所有的日期值,填充一个新的日期值 actives.removeAll() let now = Date().timeZone() UserDefaults.standard.setValue([now], forKey: "ActiveDays") } } guideVC.feedBackBlock = { DispatchQueue.main.async { let alert = UIAlertController(title: "Feedback", message: "If you have any comments or suggestions, please contact us at the following e-mail address", preferredStyle: .actionSheet) let email = UIAlertAction(title: "marketing@lux-ad.com", style: .default) { (_) in //将邮箱复制到剪切板中 UIPasteboard.general.string = "marketing@lux-ad.com" MP_HUD.text("Successfully copied the e-mail address to the clipboard", delay: 1.0, completion: nil) } alert.addAction(email) let cancel = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(cancel) self.present(alert, animated: true) } } guideVC.storeBlock = { DispatchQueue.main.async { //跳转AppStore if let url = URL(string: "https://apps.apple.com/app/6502973957") { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } } guideVC.transitioningDelegate = self guideVC.modalPresentationStyle = .custom self.present(guideVC, animated: true) } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return MPPositive_PresentationController(presentedViewController: presented, presenting: presenting) } //下拉刷新事件 @objc private func pullDownRefreshAction() { // 如果倒计时活动中,直接返回 if isCountdownActive { tableView.mj_header?.endRefreshing() return } //禁用刷新 tableView.mj_header?.isUserInteractionEnabled = false //设置一个计时器 DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [weak self] in guard let self = self else {return} //正在下来刷新 if MPPositive_BrowseLoadViewModel.shared.isRefresh == true { MPPositive_BrowseLoadViewModel.shared.isRefresh = false // 重新启用下拉刷新 self.tableView.mj_header?.isUserInteractionEnabled = true self.tableView.mj_header?.endRefreshing() pullDownRefreshCountdown() print("下拉刷新超时.") } } MPPositive_BrowseLoadViewModel.shared.pullDownRefresh{ [weak self] in guard let self = self else {return} reloadAction(nil) pullDownRefreshCountdown() } } //刷新间隔 func pullDownRefreshCountdown() { isCountdownActive = true countdownValue = 15 countdownTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in guard let self = self else { return } self.countdownValue -= 1 if self.countdownValue <= 0 { self.isCountdownActive = false timer.invalidate() } } } //MARK: - UI生成与配置 //配置 private func confirgue() { navView.frame = .init(x: 0, y: statusBarHeight, width: screen_Width, height: 90*width) if memuBtn.superview == nil { navView.addSubview(memuBtn) memuBtn.snp.makeConstraints { make in make.right.equalToSuperview().offset(-16*width) make.top.equalToSuperview().offset(16*width) make.width.height.equalTo(24*width) } } if vipImageView.superview == nil { navView.addSubview(vipImageView) vipImageView.snp.makeConstraints { make in make.centerY.equalTo(memuBtn.snp.centerY) make.right.equalTo(memuBtn.snp.left).offset(-10*width) make.width.equalTo(51*width) make.height.equalTo(32*width) } } if musicLaxLabel.superview == nil { navView.addSubview(musicLaxLabel) musicLaxLabel.snp.makeConstraints { make in make.left.equalToSuperview().offset(16*width) make.centerY.equalTo(memuBtn) } musicLaxLabel.setGradientTextColor(gradientTextColors) } if searchView.superview == nil { navView.addSubview(searchView) searchView.snp.makeConstraints { make in make.width.equalTo(340*width) make.height.equalTo(32*width) make.centerX.equalToSuperview() make.bottom.equalToSuperview().offset(-9*width) } } if bgImageView.superview == nil { view.addSubview(bgImageView) bgImageView.snp.makeConstraints { make in make.top.right.left.equalToSuperview() make.height.equalTo(981*width) } } if tableView.superview == nil { view.addSubview(tableView) tableView.snp.makeConstraints { make in make.top.equalTo(navView.snp.bottom).offset(15*width) make.left.right.bottom.equalToSuperview() } } } //MARK: - 页面刷新 //页面刷新 @objc private func reloadAction(_ sender:Notification?) { DispatchQueue.main.async { [weak self] in guard let self = self else {return} if tableView.superview == nil { confirgue() } removeErrorView() MP_HUD.hideNow() tableView.showMessage(MPPositive_BrowseLoadViewModel.shared.browseModuleLists.count) // 重新启用下拉刷新 tableView.reloadData() tableView.mj_header?.isUserInteractionEnabled = true tableView.mj_header?.endRefreshing() } } //MARK: - 点击 //点击顶部右侧弹出菜单 @objc private func menuRightClick(_ sender:UIButton) { isFirstAppearance = false let setVC = MPSideA_SettingViewController() setVC.cleanSide = true navigationController?.pushViewController(setVC, animated: true) } //弹出VIP页面 @objc private func vipIAPClick(_ sender:UITapGestureRecognizer) { view.endEditing(true) let iapVC = MP_IAPViewController() iapVC.isType = true iapVC.privacyBlock = { [weak self] in guard let self = self else {return} MP_NetWorkManager.shared.requestNetworkPermission(oberve: self) { [weak self] in let privacyVC = MPSideA_PrivacyViewController() self?.navigationController?.pushViewController(privacyVC, animated: true) } } iapVC.termsBlock = { [weak self] in guard let self = self else {return} MP_NetWorkManager.shared.requestNetworkPermission(oberve: self) { [weak self] in let serviceVC = MPSideA_ServiceViewController() self?.navigationController?.pushViewController(serviceVC, animated: true) } } iapVC.modalPresentationStyle = .fullScreen present(iapVC, animated: true) } @objc private func scrollTopAction(_ sender:UITapGestureRecognizer) { let topOffset = CGPoint(x: 0, y: 0) tableView.setContentOffset(topOffset, animated: true) } } //MARK: - tableView extension MPPositive_HomeViewController: UITableViewDataSource, UITableViewDelegate { // 用户开始拖动scrollView时调用 func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { isUserDragging = true lastContentOffset = scrollView.contentOffset.y // 重置 lastContentOffset } // 用户停止拖动scrollView时调用 func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if !decelerate { isUserDragging = false } } // scrollView停止减速时调用 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { isUserDragging = false } func scrollViewDidScroll(_ scrollView: UIScrollView) { guard isUserDragging, !MPPositive_BrowseLoadViewModel.shared.isRefresh, lastContentOffset >= -(self.tableView.mj_header?.frame.height ?? 0) else { return } let currentOffset = scrollView.contentOffset.y let scrollDiff = currentOffset - lastContentOffset if scrollDiff >= 30 && isBuyShow == true { //收起来 adjustHeight(to: 50*width) isBuyShow = false }else if scrollDiff <= -30 && isBuyShow == false { //展示 adjustHeight(to: 90*width) isBuyShow = true } // 更新最后的偏移量 lastContentOffset = currentOffset } // 调整高度方法 func adjustHeight(to height: CGFloat) { UIView.animate(withDuration: 0.3) {[weak self] in guard let self = self else {return} musicLaxLabel.isHidden = !(height > (50*width)) vipImageView.isHidden = !(height > (50*width)) memuBtn.isHidden = !(height > (50*width)) var frame = navView.frame frame.size.height = height self.navView.frame = frame } } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return MPPositive_BrowseLoadViewModel.shared.browseModuleLists.count + 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch indexPath.row { case 1: let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_HomeLibraryListstableViewCellID, for: indexPath) as! MPPositive_HomeLibraryListstableViewCell cell.libraryViewModels = MPPositive_BrowseLoadViewModel.shared.libraryList.libraryViewModels return cell default: let row:Int = indexPath.row == 0 ? 0:(indexPath.row-1) if row < MPPositive_BrowseLoadViewModel.shared.browseModuleLists.count { if let first = MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row].items.first, first.browseItem.itemType == .single, (first.browseItem.pageType == "MUSIC_VIDEO_TYPE_ATV" || first.browseItem.pageType == "MUSIC_VIDEO_TYPE_UGC") { //是单曲 let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_HomeSinglesTableViewCellID, for: indexPath) as! MPPositive_HomeSinglesTableViewCell cell.browseViewModel = MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row] return cell }else { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_HomeShowTableViewCellID, for: indexPath) as! MPPositive_HomeShowTableViewCell cell.browseViewModel = MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row] if let first = MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row].items.first, first.browseItem.itemType == .single, first.browseItem.pageType == "MUSIC_VIDEO_TYPE_OMV" { cell.showType = .Fifth }else { //判断是列表还是艺术家 if MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row].items.first?.browseItem.pageType == "MUSIC_PAGE_TYPE_ARTIST" { cell.showType = .Fourth }else { cell.showType = .Third } } cell.requestNextBlock = { [weak self] (item) in guard let self = self else {return} MP_AnalyticsManager.shared.home_b_module_clickAction(item.browseItem.pageType ?? "") switch item.browseItem.itemType { case .single: //单曲/视频跳转 MPPositive_Debouncer.shared.call { [weak self] in guard let self = self else {return} guard MP_NetWorkManager.shared.netWorkStatu == .reachable else { MP_HUD.text("Weak connection.", delay: 2.0, completion: nil) return } MP_AnalyticsManager.shared.song_clickAction("Home") //优先清除数据 MP_PlayerManager.shared.loadPlayer = nil //弹出播放器 NotificationCenter.notificationKey.post(notificationName: .pup_player_vc) MP_AnalyticsManager.shared.player_b_impAction() //触发next请求,优先获取列表全部单曲基础数据(不完善) MP_NetWorkManager.shared.requestNextList(item.browseItem.playListId ?? "", videoId: item.browseItem.videoId ?? "", clickTrackingParams: item.browseItem.clickTrackingParams){ [weak self] listSongs in guard let self = self else {return} //回掉的数据并不完善,生成一个playerloadViewModel let lodaViewModel = MPPositive_PlayerLoadViewModel(listSongs, currentVideoId: item.browseItem.videoId ?? "") lodaViewModel.improveData(item.browseItem.videoId ?? "") //更改播放器播放类型 MP_PlayerManager.shared.setPlayType(.normal) MP_PlayerManager.shared.loadPlayer = lodaViewModel MP_AnalyticsManager.shared.player_b_listAction() } } case .list: isFirstAppearance = false //列表专辑 let listVC = MPPositive_ListShowViewController(item.browseItem.browseId ?? "", params: "", title: item.title ?? "", subtitle: item.subtitle ?? "", clickTrackingParams: item.browseItem.clickTrackingParams ?? "") navigationController?.pushViewController(listVC, animated: true) case .artist: isFirstAppearance = false //前往艺术家页面 let artistVC = MPPositive_ArtistShowViewController(item.browseItem.artistId ?? "", clickTrackingParams: item.browseItem.clickTrackingParams ?? "") navigationController?.pushViewController(artistVC, animated: true) default: break } } cell.findMoreBlock = { [weak self] in guard let self = self else {return} isFirstAppearance = false let moreVC = MPPositive_MoreContentViewController(MPPositive_BrowseLoadViewModel.shared.browseModuleLists[row]) navigationController?.pushViewController(moreVC, animated: true) } return cell } }else { let cell = tableView.dequeueReusableCell(withIdentifier: MPPositive_HomeLibraryListstableViewCellID, for: indexPath) as! MPPositive_HomeLibraryListstableViewCell cell.libraryViewModels = MPPositive_BrowseLoadViewModel.shared.libraryList.libraryViewModels return cell } } } }