//
|
// TreeTeskVC.swift
|
// XQMuse
|
//
|
// Created by 无故事王国 on 2024/8/23.
|
//
|
|
import UIKit
|
import AVKit
|
import JQTools
|
import APNGKit
|
import SDWebImage
|
import UserDefaultsStore
|
|
let TreeTaskUpdate_Noti = Notification.Name.init(rawValue: "TreeTaskUpdate_Noti")
|
|
class TreeTeskVC: BaseVC {
|
|
private let cacheTreeInfoModel = UserDefaultsStore<TreeInfoModel>(uniqueIdentifier: "cacheTreeInfoModel")!
|
|
@IBOutlet weak var view_energy: UIView!
|
@IBOutlet weak var icon_energy: UILabel!
|
@IBOutlet weak var btn_exchange: TapBtn!
|
@IBOutlet weak var btn_watering: UIButton!
|
@IBOutlet weak var view_group: UIView!
|
@IBOutlet weak var view_group_inner: UIView!
|
@IBOutlet weak var btn_seedingAgain: UIButton!
|
@IBOutlet weak var view_progressTotal: UIView!
|
@IBOutlet weak var view_progress: UIView!
|
@IBOutlet weak var cons_progressHei: NSLayoutConstraint!
|
@IBOutlet weak var label_progress: UILabel!
|
|
private lazy var player:AVPlayer = {
|
let bgPath = Bundle.main.url(forResource: "bg", withExtension: "mov")
|
let p = AVPlayer(url: bgPath!)
|
p.isMuted = true
|
return p
|
}()
|
|
private lazy var playerLayer:AVPlayerLayer = {
|
let pLayer = AVPlayerLayer()
|
pLayer.videoGravity = .resize
|
return pLayer
|
}()
|
|
private lazy var taskListView:TreeTeskListView = {
|
let view = TreeTeskListView.jq_loadNibView()
|
return view
|
}()
|
|
private var treeTeskListSwipe:TreeTeskListSwipe = .down
|
private var settingModel = UserDefaultSettingViewModel.getSetting()
|
|
private var treeImage = UIImageView()
|
private var aPNGTreeImageView:APNGImageView?
|
private var aPNGSunImageView:APNGImageView?
|
private var aPNGActionImageView:APNGImageView?
|
|
private var treeInfoModel:TreeInfoModel?
|
|
private var ruleView:TreeTeskFirstRuleView?
|
|
private var maxLevel:TreeLevel?
|
private var currentLevel:TreeLevel?
|
|
private lazy var voicePlayer:AVPlayer = {
|
let p = AVPlayer()
|
return p
|
}()
|
|
override func viewDidDisappear(_ animated: Bool) {
|
super.viewDidDisappear(animated)
|
player.pause()
|
aPNGSunImageView?.stopAnimating()
|
aPNGTreeImageView?.stopAnimating()
|
}
|
|
override func viewDidAppear(_ animated: Bool) {
|
player.play()
|
aPNGSunImageView?.startAnimating()
|
aPNGTreeImageView?.startAnimating()
|
|
Services.energyExchangeGift(page: 1).subscribe(onNext: {[weak self]data in
|
self?.btn_exchange.isHidden = (data.data?.count ?? 0) == 0
|
}).disposed(by: disposeBag)
|
|
getTreeData()
|
if !(UserViewModel.getLoginInfo()?.accessToken.isEmpty ?? true){
|
Services.isFirst().subscribe(onNext: {[weak self]data in
|
|
var viewModel = UserDefaultSettingViewModel.getSetting()
|
|
if viewModel?.userFirstOpenTreeTask == true || data.data == true{
|
DispatchQueue.main.asyncAfter(delay: 0.5) {
|
let h = (JQ_ScreenW - 90) * 0.8766
|
self?.ruleView = TreeTeskFirstRuleView.show(title: "生命之树", content:"亲爱的家人,生命之树的种子已植入这片沃土,请以农夫的心态用心浇灌,为你加油哦。",textAlignment: .left,height: h,textTopOffset: 22)
|
self?.voicePlayer.replaceCurrentItem(with: AVPlayerItem(url: URL(string: TreeLevel.level_1.sound)!))
|
self?.voicePlayer.play()
|
self?.ruleView?.setEnableBtn(state: false)
|
}
|
viewModel?.userFirstOpenTreeTask = true
|
UserDefaultSettingViewModel.saveSetting(viewModel!)
|
}
|
}).disposed(by: disposeBag)
|
}
|
}
|
|
override func viewDidLayoutSubviews() {
|
super.viewDidLayoutSubviews()
|
playerLayer.frame = view.frame
|
|
view.bringSubviewToFront(self.view_energy)
|
view.bringSubviewToFront(self.btn_exchange)
|
view.bringSubviewToFront(self.btn_watering)
|
view.bringSubviewToFront(self.view_group)
|
view.bringSubviewToFront(self.btn_seedingAgain)
|
view.bringSubviewToFront(self.taskListView)
|
|
btn_seedingAgain.jq_borderWidth = 1
|
btn_seedingAgain.jq_borderColor = UIColor(hexString: "#8AAE65")
|
btn_seedingAgain.jq_addShadows(shadowColor: .white, corner: 20, radius: 5, offset: CGSize(width: 0, height: 0), opacity: 1)
|
|
taskListView.jq_addCorners(corner: [.topLeft,.topRight], radius: 20)
|
}
|
|
override func viewDidLoad() {
|
super.viewDidLoad()
|
title = "树苗打卡站"
|
btn_seedingAgain.isHidden = true
|
}
|
|
override func setUI() {
|
|
let btn = UIButton(type: .custom)
|
btn.setTitle("规则说明", for: .normal)
|
btn.setTitleColor(.black.withAlphaComponent(0.55), for: .normal)
|
btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
|
btn.addTarget(self, action: #selector(ruleAction), for: .touchUpInside)
|
|
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: btn)
|
|
btn_exchange.jq_cornerPartWithNib(byRoundingCorners: [.topLeft,.bottomLeft], radii: 17.5, size: CGSizeMake(98, 35))
|
|
playerLayer.player = player
|
view.layer.addSublayer(playerLayer)
|
player.play()
|
|
view_group_inner.jq_addCorners(corner: [.topLeft,.bottomRight], radius: 8)
|
|
let path = JQ_LibaryPath + "/tree_img/"
|
SDImageCacheHealper.cache(namespace:"treeCache", path: path, url: TreeLevel.sunAni) { [unowned self] data in
|
guard let originData = data else {return}
|
if let sunApngImage = try? APNGImage(data: originData){
|
sunApngImage.numberOfPlays = 2
|
aPNGSunImageView = APNGImageView(image: sunApngImage)
|
view.addSubview(aPNGSunImageView!)
|
aPNGSunImageView?.layer.zPosition = -998
|
aPNGSunImageView!.snp.makeConstraints { make in
|
make.edges.equalToSuperview()
|
}
|
aPNGSunImageView!.startAnimating()
|
}
|
}
|
|
updateAni()
|
|
view.addSubview(taskListView)
|
taskListView.snp.makeConstraints { make in
|
make.left.right.equalToSuperview()
|
make.bottom.equalToSuperview().offset((JQ_ScreenW * 0.655 - 50))
|
make.height.equalTo(JQ_ScreenW * 0.655)
|
}
|
}
|
|
private func getTreeData(){
|
|
//缓存的树苗信息
|
if let cacheModel = cacheTreeInfoModel.allObjects().first{
|
treeInfoModel = cacheModel
|
updateTreeInfo()
|
}
|
|
Services.treeInfo().subscribe(onNext: {data in
|
if let m = data.data{
|
self.taskListView.setTreeInfoModel(m)
|
self.treeInfoModel = m
|
if self.currentLevel == nil{
|
self.currentLevel = m.treeLevelType
|
}
|
self.updateTreeInfo()
|
self.updateAni(false)
|
try? self.cacheTreeInfoModel.save(m)
|
}
|
}).disposed(by: disposeBag)
|
}
|
|
private func updateTreeInfo(){
|
btn_seedingAgain.isHidden = currentLevel != .level_10
|
|
icon_energy.text = String(format: "当前能量值:%ld", treeInfoModel!.energyValue)
|
setProgress(current: treeInfoModel!.growthValue, total: treeInfoModel!.nextLevel)
|
}
|
|
@IBAction func sowAgainAction(_ sender: UIButton) {
|
Services.sowAgain().subscribe(onNext: {[weak self] _ in
|
self?.currentLevel = nil
|
self?.getTreeData()
|
|
let h = (JQ_ScreenW - 90) * 0.7729
|
self?.ruleView = TreeTeskFirstRuleView.show(title: "生命之树", content:TreeLevel.level_1.titleContent,textAlignment: .center,height: h,textTopOffset: 57,clickClouse: {
|
|
})
|
|
if let soundURL = URL(string:TreeLevel.level_1.sound){
|
self?.voicePlayer.replaceCurrentItem(with: AVPlayerItem(url: soundURL))
|
self?.voicePlayer.play()
|
}
|
}).disposed(by: disposeBag)
|
}
|
|
override func setRx() {
|
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) { [weak self] data in
|
|
let notifiUrl = ((data.object as? AVPlayerItem)?.asset as? AVURLAsset)
|
let currentItem = (self?.player.currentItem?.asset as? AVURLAsset)
|
|
if currentItem?.url == notifiUrl?.url{
|
self?.player.seek(to: CMTime.zero)
|
self?.player.play()
|
}
|
|
//播放音频完成
|
if let voiceUrl = (self?.voicePlayer.currentItem?.asset as? AVURLAsset){
|
if voiceUrl.url == notifiUrl?.url{
|
self?.ruleView?.setEnableBtn(state: true)
|
}
|
}
|
}
|
|
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
|
self?.player.pause()
|
self?.aPNGSunImageView?.stopAnimating()
|
self?.aPNGTreeImageView?.stopAnimating()
|
}
|
|
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] _ in
|
if self?.navigationController?.tabBarController?.selectedIndex == 2{
|
self?.player.play()
|
self?.aPNGSunImageView?.startAnimating()
|
self?.aPNGTreeImageView?.startAnimating()
|
}
|
}
|
|
NotificationCenter.default.rx.notification(TreeTaskUpdate_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self] _ in
|
self?.getTreeData()
|
}).disposed(by: disposeBag)
|
|
taskListView.swipeClouse {[unowned self] dirition in
|
guard self.treeTeskListSwipe != dirition else {return}
|
self.treeTeskListSwipe = dirition
|
switch dirition{
|
case .down:
|
UIView.animate(withDuration: 0.3) {
|
self.taskListView.snp.updateConstraints { make in
|
make.left.right.equalToSuperview()
|
make.bottom.equalToSuperview().offset((JQ_ScreenW * 0.655 - 50))
|
make.height.equalTo(JQ_ScreenW * 0.655)
|
}
|
self.view.layoutIfNeeded()
|
}
|
case .up:
|
UIView.animate(withDuration: 0.3) {
|
self.taskListView.snp.updateConstraints { make in
|
make.left.right.equalToSuperview()
|
make.bottom.equalToSuperview()
|
make.height.equalTo(JQ_ScreenW * 0.655)
|
}
|
self.view.layoutIfNeeded()
|
}
|
}
|
}
|
|
taskListView.signSuccess {[weak self] in
|
self?.getTreeData()
|
}
|
}
|
|
// 0 - 100
|
private func setProgress(current:Int,total:Int){
|
guard total != 0 else {
|
self.label_progress.attributedText = AttributedStringbuilder.build().add(string: "0", withFont: .systemFont(ofSize: 12, weight: .bold), withColor: .white, indent: 0, lineSpace: 0).add(string: "\n/NAN", withFont: .systemFont(ofSize: 11,weight: .thin), withColor: .white).mutableAttributedString
|
return
|
}
|
|
let realH = view_progressTotal.height - 4 //上下boder宽度
|
|
let pix = realH / Double(total)
|
let px = Double(current) * pix
|
let max = realH
|
cons_progressHei.constant = min(px,max)
|
|
self.label_progress.attributedText = AttributedStringbuilder.build().add(string: "\(current)", withFont: .systemFont(ofSize: 12, weight: .bold), withColor: .white, indent: 0, lineSpace: 0).add(string: "\n/\(total)", withFont: .systemFont(ofSize: 11,weight: .thin), withColor: .white).mutableAttributedString
|
self.label_progress.textAlignment = .center
|
|
UIView.animate(withDuration: 1.5) {
|
self.view_progressTotal.layoutIfNeeded()
|
self.view_progress.jq_gradientColor(colorArr: [UIColor(hexString: "#FCFECA")!.cgColor,UIColor(hexString: "#3CB56A")!.cgColor], cornerRadius: 2.5,startPoint: CGPoint(x: 0, y: 0),endPoint: CGPoint(x: 1, y: 1),clear: true)
|
}
|
}
|
|
private func next(){
|
currentLevel?.updateLevel()
|
guard let currentL = currentLevel else {return}
|
guard let maxL = maxLevel else {return}
|
if currentL.rawValue > maxL.rawValue{return}
|
|
|
let h = (JQ_ScreenW - 90) * 0.7729
|
ruleView = TreeTeskFirstRuleView.show(title: "生命之树", content:currentL.titleContent,textAlignment: .center,height: h,textTopOffset: 57,clickClouse: {[weak self] () in
|
if currentL.rawValue != maxL.rawValue || currentL != TreeLevel.level_10{
|
self?.next()
|
}
|
})
|
|
if let soundURL = URL(string:currentL.sound){
|
voicePlayer.replaceCurrentItem(with: AVPlayerItem(url: soundURL))
|
voicePlayer.play()
|
}
|
|
ruleView?.setEnableBtn(state: false)
|
|
updateAni()
|
getTreeData()
|
updateTreeInfo()
|
}
|
|
//能量明细
|
@IBAction func energyDetailAction(_ sender: TapBtn) {
|
guard sceneDelegate!.checkisLoginState() else {return}
|
let vc = TreeTeskDetailVC()
|
push(vc: vc)
|
}
|
|
//兑换
|
@IBAction func exchangeAction(_ sender: TapBtn) {
|
guard sceneDelegate!.checkisLoginState() else {return}
|
let vc = TreeTeskEnergyExchangeVC()
|
push(vc: vc)
|
}
|
|
@IBAction func wateringAction(_ sender: UIButton) {
|
guard sceneDelegate!.checkisLoginState() else {return}
|
|
sender.isUserInteractionEnabled = false
|
|
if treeInfoModel == nil {return}
|
|
guard treeInfoModel!.energyValue != 0 else{
|
alert(msg: "已经没有能量值了,快去做任务吧");
|
sender.isUserInteractionEnabled = true
|
return
|
}
|
|
Services.watering().subscribe(onNext: {[weak self] data in
|
guard let weakSelf = self else { return }
|
if let m = data.data{
|
sender.isUserInteractionEnabled = true
|
if m.isNext{
|
weakSelf.maxLevel = m.treeLevelType
|
weakSelf.treeInfoModel!.treeLevelType = m.treeLevelType
|
weakSelf.next()
|
}
|
|
weakSelf.treeInfoModel?.growthValue = m.growthValue
|
weakSelf.treeInfoModel?.energyValue = 0
|
weakSelf.treeInfoModel?.nextLevel = m.nextLevel
|
weakSelf.icon_energy.text = String(format: "当前能量值:%ld", 0)
|
|
NotificationCenter.default.post(name: UpdateUserProfile_Noti, object: nil)
|
weakSelf.getTreeData()
|
}
|
}).disposed(by: disposeBag)
|
|
if let waterApngImage = try? APNGImage(fileURL: Bundle.main.url(forResource: "apngb-animated-flow", withExtension:"png")!){
|
waterApngImage.numberOfPlays = 2
|
aPNGActionImageView = APNGImageView(image: waterApngImage)
|
aPNGActionImageView!.alpha = 0
|
aPNGSunImageView?.contentMode = .scaleToFill
|
view.addSubview(aPNGActionImageView!)
|
aPNGActionImageView!.snp.makeConstraints { make in
|
make.center.equalToSuperview().inset(UIEdgeInsets(top: 90, left: 140, bottom: 0, right: 0))
|
make.width.equalTo(200)
|
make.height.equalTo(150)
|
}
|
UIView.animate(withDuration: 0.5) {
|
self.aPNGActionImageView!.alpha = 1.0
|
}
|
aPNGActionImageView!.startAnimating()
|
|
aPNGActionImageView!.onOnePlayDone.delegate(on: self) {[unowned self] a,b in
|
if b == 2{
|
sender.isUserInteractionEnabled = true
|
UIView.animate(withDuration: 0.5) {
|
self.aPNGActionImageView!.alpha = 0
|
}completion: { _ in
|
self.aPNGActionImageView!.removeFromSuperview()
|
}
|
}
|
}
|
}
|
}
|
|
@objc func jumpAction(){
|
// treeLevel = TreeLevel(rawValue: treeLevel.rawValue + 1) ?? TreeLevel.level_1
|
}
|
|
@objc func ruleAction(){
|
let vc = WebVC(type: .treeRule)
|
vc.title = "规则说明"
|
push(vc: vc)
|
}
|
|
private func updateAni(_ needAni:Bool = true){
|
guard let treeLevel = currentLevel else {return}
|
guard let staticDroopUrl = Bundle.main.url(forResource: treeLevel.staticDroopResource, withExtension: "png") else {return}
|
|
let path = JQ_LibaryPath + "/tree_img/"
|
SDImageCacheHealper.cache(namespace:"treeCache", path: path, url: treeLevel.aniResourceUrl) { [unowned self] data in
|
guard let dataOrigin = data else {return}
|
if let treeApngImage = try? APNGImage(data: dataOrigin){
|
|
if treeLevel == .level_1{
|
treeApngImage.numberOfPlays = 1
|
}else{
|
treeApngImage.numberOfPlays = 0
|
}
|
|
if self.aPNGTreeImageView == nil{
|
self.aPNGTreeImageView = APNGImageView(image: treeApngImage)
|
self.aPNGTreeImageView?.tag = 10101
|
self.aPNGTreeImageView!.contentMode = .redraw
|
let tap = UITapGestureRecognizer(target: self, action: #selector(jumpAction))
|
self.aPNGTreeImageView!.addGestureRecognizer(tap)
|
}
|
|
if view.viewWithTag(10101) == nil{
|
view.addSubview(self.aPNGTreeImageView!)
|
}
|
|
let scale:Double = 0.55 //放大倍数
|
|
self.aPNGTreeImageView!.snp.remakeConstraints { make in
|
make.centerX.equalToSuperview().offset(-10)
|
make.bottom.equalToSuperview().offset(-220)
|
make.width.equalTo(642 * scale)
|
make.height.equalTo(731 * scale)
|
}
|
|
let duration = needAni ? 0.6:0
|
UIView.animate(withDuration: duration) {
|
self.aPNGTreeImageView?.alpha = 0
|
|
}completion: { state in
|
if state{
|
self.aPNGTreeImageView!.image = treeApngImage
|
UIView.animate(withDuration: 0.5, delay: 0.2) {
|
self.aPNGTreeImageView?.alpha = 1
|
}completion: { _ in
|
self.aPNGTreeImageView!.startAnimating()
|
|
if self.treeInfoModel?.status == .yes{
|
self.aPNGTreeImageView?.stopAnimating()
|
self.aPNGTreeImageView?.image = nil
|
self.aPNGTreeImageView?.staticImage = UIImage(contentsOfFile: staticDroopUrl.droppedScheme()!.absoluteString)
|
}else{
|
self.aPNGTreeImageView?.staticImage = nil
|
self.aPNGTreeImageView?.image = treeApngImage
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
extension TreeTeskVC:CAAnimationDelegate{
|
func animationDidStart(_ anim: CAAnimation) {
|
treeImage.isUserInteractionEnabled = false
|
}
|
|
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
// print("结束动画")
|
// if anim.value(forKey: "customType") as! String == "ani_flower"{
|
//
|
//
|
// }
|
}
|
}
|
|
class SDImageCacheHealper{
|
static func cache(namespace:String,path:String,url:String,clouse:@escaping (Data?)->Void){
|
let image = SDImageCache(namespace: namespace.jq_md5String(), diskCacheDirectory: path)
|
let loader = SDImageLoadersManager.shared
|
|
let manager = SDWebImageManager(cache: image, loader: loader)
|
|
//过滤链接,去除参数,保证缓存的KEY被MD5后不变
|
manager.cacheKeyFilter = SDWebImageCacheKeyFilter(block: { url in
|
let img = url.absoluteString.components(separatedBy: "?").first
|
return img
|
})
|
|
if let localImgData = image.diskImageData(forKey: url){
|
clouse(localImgData)
|
}else{
|
let originUrl = URL(string: url)
|
manager.loadImage(with: originUrl, progress: nil) { imageData, data,error, cacheType, finished, imageUrl in
|
if error != nil{
|
clouse(nil)
|
}
|
if data != nil{
|
clouse(data)
|
}
|
}
|
}
|
}
|
}
|