杨锴
2025-06-04 ac84f81ca2311300b431c1bfb9f71253b59073f2
DolphinEnglishLearnStudent/Moudle/Home/Listen/VC/HomeListenFight_lesson_5_VC.swift
@@ -9,354 +9,352 @@
class HomeListenFight_lesson_5_VC: BaseVC {
            private var viewModel = FightAnswerViewModel()
            private var listenNewModel:ListenNewModel!
            private var page:Int!
            private var answterCount:Int = 0 //回答计数,用于确定角标
            private var playVoiceAt:Int? //播放声音的View
            private var playVoiceRealAt:Int? //播放声音的View -被乱序后,真实Index
            var rootViewModel:HomeListenFightViewModel!
            var teamScheduleModel:TeamScheduleModel?
            private var voicePlayer = VoicePlayer.share()
            private var isListen:Bool = false
    private var viewModel = FightAnswerViewModel()
    private var listenNewModel:ListenNewModel!
    private var page:Int!
    private var answterCount:Int = 0 //回答计数,用于确定角标
    private var playVoiceAt:Int? //播放声音的View
    private var playVoiceRealAt:Int? //播放声音的View -被乱序后,真实Index
    var rootViewModel:HomeListenFightViewModel!
    var teamScheduleModel:TeamScheduleModel?
    private var voicePlayer = VoicePlayer.share()
    private var isListen:Bool = false
    private var playingTags = Set<Int>() //已经播放过的TAG
    private var handleClouse:(()->Void)?
            private lazy var collectionView:UICollectionView = {
                        let flowLayout = UICollectionViewFlowLayout()
                        let w = (JQ_ScreenW - 189 * 2 - 18) / 2.0
                        flowLayout.itemSize = CGSize(width: w, height: w * 0.70)
                        flowLayout.minimumInteritemSpacing = 15
                        flowLayout.minimumLineSpacing = 15
                        flowLayout.scrollDirection = .vertical
                        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
                        collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
                        collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
                        collection.isScrollEnabled = false
                        return collection
            }()
    private lazy var collectionView:UICollectionView = {
        let flowLayout = UICollectionViewFlowLayout()
        let w = (JQ_ScreenW - 189 * 2 - 18) / 2.0
        flowLayout.itemSize = CGSize(width: w, height: w * 0.70)
        flowLayout.minimumInteritemSpacing = 15
        flowLayout.minimumLineSpacing = 15
        flowLayout.scrollDirection = .vertical
        let collection = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collection.register(UINib(nibName: "ListenFight_lesson_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_ListenFight_lesson_1_CCell")
        collection.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
        collection.isScrollEnabled = false
        return collection
    }()
            private lazy var stackView:UIStackView = {
                        let sta = UIStackView()
                        sta.spacing = 89
                        sta.distribution = .equalSpacing
                        sta.axis = .horizontal
                        return sta
            }()
            private lazy var label_hint:UILabel = {
                        let label = UILabel()
                        label.font = .systemFont(ofSize: 18, weight: .medium)
                        label.textColor = .black
                        label.text = "语音对应内容"
                        label.textAlignment = .center
                        return label
            }()
            required init(page:Int,listenNewModel:ListenNewModel){
                        super.init(nibName: nil, bundle: nil)
                        self.page = page
                        self.listenNewModel = listenNewModel
//                        self.listen1Model.subjectList.shuffle()
            }
            required init?(coder: NSCoder) {
                        fatalError("init(coder:) has not been implemented")
            }
    private lazy var stackView:UIStackView = {
        let sta = UIStackView()
        sta.spacing = 49
        sta.distribution = .equalSpacing
        sta.axis = .horizontal
        return sta
    }()
            override func viewDidLoad() {
                        super.viewDidLoad()
    required init(page:Int,listenNewModel:ListenNewModel){
        super.init(nibName: nil, bundle: nil)
        self.page = page
        self.listenNewModel = listenNewModel
        //                        self.listen1Model.subjectList.shuffle()
    }
                        collectionView.reloadData()
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
                        //回传记录,始终保持答题进度
                        if let team = teamScheduleModel{
                                    for teamId in team.teamIds{
                                                for v in listenNewModel.subjectList[page]{
                                                            if team.topicIds.contains(v.id){
                                                                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                                                            }
                                                }
                                    }
                        }
            }
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.reloadData()
            override func viewDidAppear(_ animated: Bool) {
                        super.viewDidAppear(animated)
                        voicePlayer.delegate = self
            }
        //回传记录,始终保持答题进度
        if let team = teamScheduleModel{
            for teamId in team.teamIds{
                for v in listenNewModel.subjectList[page]{
                    if team.topicIds.contains(v.id){
                        rootViewModel.insertCorrectAnswer(teamId: "\(teamId)", answerId: v.id)
                    }
                }
            }
        }
    }
            override func viewDidDisappear(_ animated: Bool) {
                        super.viewDidDisappear(animated)
                        voicePlayer.delegate = nil
                        VoicePlayer.share().playerInterrupt()
            }
            override func setUI() {
                        super.setUI()
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        voicePlayer.delegate = self
    }
                        view.addSubview(stackView)
                        stackView.snp.makeConstraints { make in
                                    make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(19)
                                    make.centerX.equalToSuperview()
                                    make.height.equalTo(52)
                        }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        voicePlayer.delegate = nil
        VoicePlayer.share().playerInterrupt()
    }
                        view.addSubview(label_hint)
                        label_hint.snp.makeConstraints { make in
                                    make.left.right.equalToSuperview()
                                    make.top.equalTo(stackView.snp.bottom).offset(6)
                                    make.height.equalTo(0)
                        }
    override func setUI() {
        super.setUI()
                        collectionView.delegate = self
                        collectionView.dataSource = self
                        collectionView.showsVerticalScrollIndicator = false
                        collectionView.backgroundColor = .clear
                        view.addSubview(collectionView)
                        collectionView.snp.makeConstraints { make in
                                    make.top.equalTo(self.label_hint.snp.bottom).offset(11)
                                    make.left.equalTo(189)
                                    make.width.equalTo(JQ_ScreenW - 189 * 2)
                                    make.bottom.equalToSuperview()
                        }
        view.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(19)
            make.centerX.equalToSuperview()
            make.height.equalTo(52)
        }
                        showHintText(false)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.showsVerticalScrollIndicator = false
        collectionView.backgroundColor = .clear
        view.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(91)
            make.left.equalTo(189)
            make.width.equalTo(JQ_ScreenW - 189 * 2)
            make.bottom.equalToSuperview()
        }
                        setAnswerStackView()
            }
        setAnswerStackView()
    }
            override func viewDidLayoutSubviews() {
                        super.viewDidLayoutSubviews()
                        let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                        let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
                        let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.0
                        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
                                    flowLayout.itemSize = CGSize(width: w, height: h)
                                    collectionView.reloadData()
                        }
            }
        let w = (collectionView.size.width - flowLayout.minimumLineSpacing) / 2
        let h = (collectionView.size.height - flowLayout.minimumInteritemSpacing) / 2.0
            override func setRx() {
        if flowLayout.itemSize.width != w || flowLayout.itemSize.height != h{
            flowLayout.itemSize = CGSize(width: w, height: h)
            collectionView.reloadData()
        }
    }
            }
    override func setRx() {
        NotificationCenter.default.rx.notification(ResetLession_Noti).take(until: self.rx.deallocated).subscribe(onNext: {[weak self]_ in
            self?.restore()
            self?.viewDidLoad()
        }).disposed(by: disposeBag)
    }
            func restore(){
                        viewModel.answerType.accept(.none)
                        answterCount  = 0
                        playVoiceAt = nil
                        playVoiceRealAt = nil
    func restore(){
        viewModel.answerType.accept(.none)
        answterCount  = 0
        playVoiceAt = nil
        playVoiceRealAt = nil
                        for subV in view.subviews{
                                    if subV is VoiceHandleView{
                                                subV.removeFromSuperview()
                                    }
                        }
                        setUI()
                        collectionView.reloadData()
            }
        for subV in view.subviews{
            if subV is VoiceHandleView{
                subV.removeFromSuperview()
            }
        }
        setUI()
        collectionView.reloadData()
    }
            private func setAnswerStackView(){
    private func setAnswerStackView(){
                        for subView in stackView.arrangedSubviews{
                                    subView.removeFromSuperview()
                        }
        for subView in stackView.arrangedSubviews{
            subView.removeFromSuperview()
        }
                        if !view.subviews.contains(stackView){
                                    view.addSubview(stackView)
                                    stackView.snp.makeConstraints { make in
                                                make.right.equalToSuperview().offset(-14)
                                                make.centerY.equalToSuperview()
                                    }
                        }
        if !view.subviews.contains(stackView){
            view.addSubview(stackView)
            stackView.snp.makeConstraints { make in
                make.right.equalToSuperview().offset(-14)
                make.centerY.equalToSuperview()
            }
        }
                        var tempArray = [VoiceHandleView]()
                        for (i,value) in listenNewModel.subjectList[page].enumerated(){
                                    let answerView = VoiceHandleView()
                                    answerView.listenType = .lesson5
                                    answerView.tag = 1000+i
                                    answerView.playAt {[weak self] index in
                                                print("--->\(index)--\(answerView.frame.origin.x / 248)")
                                                self?.playVoiceRealAt = Int(answerView.frame.origin.x) / 248
                                                self?.playVoiceAt = index - 1000
                                                self?.showHintText(true)
                                    }
                                    answerView.playUrl = value.correct
                                    answerView.snp.makeConstraints { make in
                                                make.width.equalTo(159)
                                                make.height.equalTo(52)
                                    }
        var tempArray = [VoiceHandleView]()
        for (i,value) in listenNewModel.subjectList[page].enumerated(){
            let answerView = VoiceHandleView()
            answerView.listenType = .lesson5
            answerView.tag = 1000+i
            answerView.playAt {[weak self] index in
                self?.playVoiceRealAt = Int(answerView.frame.origin.x) / 248
                self?.playVoiceAt = index - 1000
            }
            answerView.playUrl = value.correct
            answerView.snp.makeConstraints { make in
                make.width.equalTo(221)
                make.height.equalTo(40)
            }
                                    UIView.animate(withDuration: 0.05 + Double(i)) {
                                                answerView.alpha = 1
                                    }
                                    tempArray.append(answerView)
                        }
                        tempArray.shuffle()
                        stackView.addArrangedSubviews(tempArray)
            }
            UIView.animate(withDuration: 0.05 + Double(i)) {
                answerView.alpha = 1
            }
            tempArray.append(answerView)
        }
        tempArray.shuffle()
            private func showHintText(_ state:Bool){
                        if let at = playVoiceAt{
                                    label_hint.text = listenNewModel.subjectList[page][at].name
                                    UIView.animate(withDuration: 0.5) {
                                                if state{
                                                            self.label_hint.snp.remakeConstraints { make in
                                                                        make.left.right.equalToSuperview()
                                                                        make.top.equalTo(self.stackView.snp.bottom).offset(6)
                                                                        make.height.equalTo(25)
                                                            }
                                                }else{
                                                            self.label_hint.snp.remakeConstraints { make in
                                                                        make.left.right.equalToSuperview()
                                                                        make.top.equalTo(self.stackView.snp.bottom).offset(6)
                                                                        make.height.equalTo(0)
                                                            }
                                                }
                                                self.view.layoutIfNeeded()
                                    }
                        }
            }
        DispatchQueue.main.asyncAfter(deadline: .now()+1.0) {
            //自动播放第一条
            tempArray.first?.playingAction()
            self.playingTags.insert(tempArray.first?.tag ?? 0)
        }
        stackView.addArrangedSubviews(tempArray)
    }
    func handleClouseAction(clouse:@escaping ()->Void){
        self.handleClouse = clouse
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDelegate{
            func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
                        if isListen == false{
                                    alertError(msg: "请先听题");return
                        }
                        isListen = false
        if isListen == false{
            alertError(msg: "请先听题");return
        }
        isListen = false
                        viewModel.selectIndex.accept(indexPath)
                        guard playVoiceAt != nil else {return}
        viewModel.selectIndex.accept(indexPath)
        guard playVoiceAt != nil else {return}
                        let answer = listenNewModel.subjectList[page][playVoiceAt!]
                        let selectAnswer = listenNewModel.subjectList[page][indexPath.row]
        let answer = listenNewModel.subjectList[page][playVoiceAt!]
        let selectAnswer = listenNewModel.subjectList[page][indexPath.row]
                        var answerType:Fight_lessonType = .none
                        if answer.id == selectAnswer.id{
                                    answerType = .success
                                    voicePlayer.playSuccessVoice()
                                    let teamId = listenNewModel.data?.id.components(separatedBy: ",")[page]
                                    rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: selectAnswer.id)
                        }else{
                                    answerType = .fail
                                    voicePlayer.playFailVoice()
                        }
        var answerType:Fight_lessonType = .none
        if answer.id == selectAnswer.id{
            answerType = .success
            voicePlayer.playSuccessVoice()
            let teamId = listenNewModel.data?.id.components(separatedBy: ",")[page]
            rootViewModel.insertCorrectAnswer(teamId: teamId, answerId: selectAnswer.id)
                        let tempSubV = stackView.arrangedSubviews[self.playVoiceRealAt!] as! VoiceHandleView
                        label_hint.text = ""
                        switch answerType {
                                    case .success:
                                                answterCount += 1
                                                rootViewModel.correctNum += 1
                                                viewModel.answerType.accept(.success)
            let data = listenNewModel.list[page]
            data.status = 2
            Services.answerQuestion(id: data.id, status: 2).subscribe(onNext: {[weak self]_ in
                self?.handleClouse?()
            }).disposed(by: disposeBag)
        }else{
            answerType = .fail
            voicePlayer.playFailVoice()
                                                let copyView = tempSubV.copyView()
                                                copyView.listenType = .lesson5
                                                copyView.jq_cornerRadius = 0
                                                copyView.playUrl = selectAnswer.correct
                                                let newRect = tempSubV.convert(tempSubV.bounds, to: self.view)
                                                copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 159, height: 52))
                                                self.view.addSubview(copyView)
                                                tempSubV.alpha = 0
            let data = listenNewModel.list[page]
            data.status = 3
            Services.answerQuestion(id: data.id, status: 3).subscribe(onNext: {[weak self]_ in
                self?.handleClouse?()
            }).disposed(by: disposeBag)
        }
                                                DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
        let tempSubV = stackView.arrangedSubviews[self.playVoiceRealAt!] as! VoiceHandleView
                                                            //获取Cell的顶部试图
                                                            let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        switch answerType {
        case .success:
            answterCount += 1
            rootViewModel.correctNum += 1
            viewModel.answerType.accept(.success)
                                                            if let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as? ListenFight_lesson_1_CCell{
                                                                        var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                                                                        newRect1.origin.x += (collectionView.frame.origin.x + 5)
                                                                        newRect1.origin.y += 94 + 24
            let copyView = tempSubV.copyView()
            copyView.isPlayed = true
            copyView.listenType = .lesson5
            copyView.jq_cornerRadius = 8
            copyView.playUrl = selectAnswer.correct
            let newRect = tempSubV.convert(tempSubV.bounds, to: self.view)
            copyView.frame = CGRect(origin: newRect.origin, size: CGSize(width: 221, height: 52))
            self.view.addSubview(copyView)
            tempSubV.alpha = 0
                                                                        UIView.animateKeyframes(withDuration: 0.4, delay: 0,options: .calculationModeLinear) {
                                                                                    copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: flowLayout.itemSize.width - 10 , height: 40))
            DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
                                                                        }completion: { _ in
                                                                                    copyView.playingAction()
                                                                                    self.playVoiceRealAt = nil
                                                                                    self.playVoiceAt = nil
                                                                                    self.collectionView.reloadData()
                                                                        }
                                                            }
                                                }
                //获取Cell的顶部试图
                let flowLayout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
                if let cell = self.collectionView.cellForItem(at: indexPath) as? ListenFight_lesson_1_CCell{
                    var newRect1 = cell.convert(cell.bounds, to: self.collectionView)
                    newRect1.origin.x += (collectionView.frame.origin.x)
                    newRect1.origin.y += 94
                                    case .fail:
                                                viewModel.answerType.accept(.fail)
                                                rootViewModel.errorNum += 1
                                                collectionView.reloadData()
                                    case .none:
                                                break
                        }
            }
                    UIView.animateKeyframes(withDuration: 0.4, delay: 0,options: .calculationModeLinear) {
                        copyView.frame = CGRect(origin: newRect1.origin, size: CGSize(width: flowLayout.itemSize.width , height: 40))
                    }completion: { _ in
                        //                        copyView.playingAction()
                        self.playVoiceRealAt = nil
                        self.playVoiceAt = nil
                        self.collectionView.reloadData()
                        //播放下一个
                        DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                            for v in self.stackView.subviews as! [VoiceHandleView]{
                                if !self.playingTags.contains(v.tag){
                                    v.playingAction()
                                    self.playingTags.insert(v.tag)
                                    break
                                }
                            }
                        }
                    }
                }
            }
        case .fail:
            viewModel.answerType.accept(.fail)
            rootViewModel.errorNum += 1
            collectionView.reloadData()
        case .none:
            break
        }
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDelegateFlowLayout{
            func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
                        return CGSize.zero
            }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize.zero
    }
}
extension HomeListenFight_lesson_5_VC:UICollectionViewDataSource{
            func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
                        let model = listenNewModel.subjectList[page][indexPath.row]
                        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
                        cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
                        if viewModel.selectIndex.value == indexPath{
                                    cell.setState(state: viewModel.answerType.value)
                        }else{
                                    cell.setState(state: .none)
                        }
                        cell.setListen1SubModel(model)
                        return cell
            }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let model = listenNewModel.subjectList[page][indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_1_CCell", for: indexPath) as! ListenFight_lesson_1_CCell
        cell.jq_addShadows(shadowColor: .black.withAlphaComponent(0.31), corner: 8, radius: 3, offset: CGSize(width: 0, height: 1), opacity: 1)
        if viewModel.selectIndex.value == indexPath{
            cell.setState(state: viewModel.answerType.value)
        }else{
            cell.setState(state: .none)
        }
        cell.setListen1SubModel(model)
        return cell
    }
            func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
                        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
                        return reusableView
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath)
        return reusableView
            }
    }
            func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                        return listenNewModel.subjectList[page].count
            }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return listenNewModel.subjectList[page].count
    }
            func numberOfSections(in collectionView: UICollectionView) -> Int {
                        return 1
            }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
}
extension HomeListenFight_lesson_5_VC:VoicePlayerDelegate{
            func playComplete() {
                        view.isUserInteractionEnabled = true
                        isListen = true
                        for subV in stackView.arrangedSubviews as! [VoiceHandleView]{
                                    subV.resetView()
                        }
    func playComplete() {
        view.isUserInteractionEnabled = true
        isListen = true
        for subV in stackView.arrangedSubviews as! [VoiceHandleView]{
            subV.resetView()
        }
                        if viewModel.answerType.value == .success{
                                    let v = rootViewModel.answerCount.value + 1
                                    rootViewModel.answerCount.accept(v)
                                    viewModel.answerType.accept(.none)
                        }
        if viewModel.answerType.value == .success{
            let v = rootViewModel.answerCount.value + 1
            rootViewModel.answerCount.accept(v)
            viewModel.answerType.accept(.none)
        }
                        if self.answterCount >= 4{
//                                    DispatchQueue.main.asyncAfter(delay: 3.0) {
                                                self.voicePlayer.playerEnd()
                                                NotificationCenter.default.post(name: NextLession_Noti, object: nil)
//                                    }
                        }
            }
            func playing() {
                        view.isUserInteractionEnabled = false
            }
        if self.answterCount >= 4{
            //                                    DispatchQueue.main.asyncAfter(delay: 3.0) {
            self.voicePlayer.playerEnd()
            NotificationCenter.default.post(name: NextLession_Noti, object: nil)
            //                                    }
        }
    }
    func playing() {
        view.isUserInteractionEnabled = false
    }
}