| | |
| | | 13CDF44F2C05756C00E8D4FD /* Lesson_4_AnswerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CDF44E2C05756C00E8D4FD /* Lesson_4_AnswerView.swift */; }; |
| | | 13CDF4512C05757400E8D4FD /* Lesson_4_AnswerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13CDF4502C05757400E8D4FD /* Lesson_4_AnswerView.xib */; }; |
| | | 13E07DF12BFDF1E600B7F5FB /* ExchangeRecordHistoryVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E07DF02BFDF1E600B7F5FB /* ExchangeRecordHistoryVC.swift */; }; |
| | | 13E99EFA2C1FD1A9004F52D4 /* LaunchImageHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E99EF92C1FD1A9004F52D4 /* LaunchImageHelper.swift */; }; |
| | | 13EEB8912BFED3F3002996FC /* AwardListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EEB8902BFED3F3002996FC /* AwardListView.swift */; }; |
| | | 13EEB8932BFED3FA002996FC /* AwardListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13EEB8922BFED3FA002996FC /* AwardListView.xib */; }; |
| | | 13EEB8972BFF1531002996FC /* AwardListCCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EEB8952BFF1531002996FC /* AwardListCCell.swift */; }; |
| | |
| | | 13CDF44E2C05756C00E8D4FD /* Lesson_4_AnswerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lesson_4_AnswerView.swift; sourceTree = "<group>"; }; |
| | | 13CDF4502C05757400E8D4FD /* Lesson_4_AnswerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Lesson_4_AnswerView.xib; sourceTree = "<group>"; }; |
| | | 13E07DF02BFDF1E600B7F5FB /* ExchangeRecordHistoryVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExchangeRecordHistoryVC.swift; sourceTree = "<group>"; }; |
| | | 13E99EF92C1FD1A9004F52D4 /* LaunchImageHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LaunchImageHelper.swift; sourceTree = "<group>"; }; |
| | | 13EEB8902BFED3F3002996FC /* AwardListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwardListView.swift; sourceTree = "<group>"; }; |
| | | 13EEB8922BFED3FA002996FC /* AwardListView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AwardListView.xib; sourceTree = "<group>"; }; |
| | | 13EEB8952BFF1531002996FC /* AwardListCCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwardListCCell.swift; sourceTree = "<group>"; }; |
| | |
| | | 130278402BFD978900DDCE81 /* Config */ = { |
| | | isa = PBXGroup; |
| | | children = ( |
| | | 13E99EF92C1FD1A9004F52D4 /* LaunchImageHelper.swift */, |
| | | 1302783E2BFD978900DDCE81 /* Config.swift */, |
| | | 1302783F2BFD978900DDCE81 /* Enums.swift */, |
| | | 13812B9B2C0F02B600905CCE /* VoicePlayer.swift */, |
| | |
| | | 133386382C007E91002EE788 /* HomeListenFight_lesson_2_VC.swift in Sources */, |
| | | 13812B9C2C0F02B700905CCE /* VoicePlayer.swift in Sources */, |
| | | 1302787B2BFD9ED600DDCE81 /* MarketContentVC.swift in Sources */, |
| | | 13E99EFA2C1FD1A9004F52D4 /* LaunchImageHelper.swift in Sources */, |
| | | 13649E9C2C00304C001B04E2 /* ListenFight_lesson_1_CCell.swift in Sources */, |
| | | 13EEB8A02BFF28A7002996FC /* HomeListenVC.swift in Sources */, |
| | | 130278492BFD979200DDCE81 /* BaseTabBarVC.swift in Sources */, |
| | |
| | | class BaseNav: UINavigationController,UINavigationControllerDelegate { |
| | | |
| | | /// 需要透明Nav的VC |
| | | private var lucencyVCs = [HomeVC.self] |
| | | private var lucencyVCs = [HomeVC.self,LoginVC.self,CommonWebVC.self] |
| | | |
| | | private let img = UIImage.jq_gradient(["#B6E0FF","#FFFFFF"],size: CGSize(width: JQ_ScreenW, height: 90),direction: GradientDirection.vertical) |
| | | |
| | |
| | | navigationBar.setBackgroundImage(UIImage(), for: .default) |
| | | navigationBar.shadowImage = UIImage() |
| | | } |
| | | |
| | | // let titleV = UIView() |
| | | // titleV.sizeToFit() |
| | | // let imgV = UIImageView(image: UIImage(named: "bg_logo")) |
| | | // imgV.contentMode = .scaleAspectFit |
| | | // titleV.addSubview(imgV) |
| | | // imgV.snp.makeConstraints { make in |
| | | // make.edges.equalToSuperview() |
| | | // } |
| | | // |
| | | // navigationItem.titleView = titleV |
| | | } |
| | | |
| | | open func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { |
| | |
| | | override var preferredStatusBarStyle: UIStatusBarStyle{ |
| | | return .lightContent |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | class LoginNav: UINavigationController,UINavigationControllerDelegate { |
| | | |
| | | private var popDelegate: UIGestureRecognizerDelegate? |
| | | |
| | | open override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | self.navigationBar.barTintColor = .white |
| | | self.navigationBar.titleTextAttributes = [.font:UIFont.systemFont(ofSize: 18, weight: .medium), .foregroundColor:UIColor.black] |
| | | self.navigationBar.tintColor = UIColor.black |
| | | self.navigationBar.shadowImage = UIImage() |
| | | self.navigationBar.isTranslucent = true |
| | | self.delegate = self |
| | | self.popDelegate = self.interactivePopGestureRecognizer?.delegate |
| | | |
| | | |
| | | if #available(iOS 15.0, *) { |
| | | |
| | | let scrollBar = UINavigationBarAppearance() |
| | | scrollBar.configureWithOpaqueBackground() |
| | | scrollBar.backgroundEffect = nil |
| | | scrollBar.shadowColor = nil |
| | | scrollBar.titleTextAttributes = [.foregroundColor:Config.ThemeColor,.font:Config.NavFont] |
| | | scrollBar.backgroundColor = UIColor.clear |
| | | // scrollBar.backgroundImage = img |
| | | |
| | | |
| | | let standardBar = UINavigationBarAppearance() |
| | | standardBar.configureWithOpaqueBackground() |
| | | standardBar.backgroundEffect = nil |
| | | standardBar.shadowColor = nil |
| | | standardBar.shadowImage = nil |
| | | standardBar.titleTextAttributes = [.foregroundColor:Config.ThemeColor,.font:Config.NavFont] |
| | | standardBar.backgroundColor = UIColor.clear |
| | | // standardBar.backgroundImage = img |
| | | |
| | | navigationBar.scrollEdgeAppearance = scrollBar //顶部透明 |
| | | navigationBar.standardAppearance = standardBar |
| | | |
| | | |
| | | }else { |
| | | navigationBar.titleTextAttributes = [.foregroundColor:Config.ThemeColor,.font:Config.NavFont] |
| | | } |
| | | } |
| | | |
| | | open func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { |
| | | |
| | | |
| | | } |
| | | |
| | | //侧滑 |
| | | public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { |
| | | if viewController == self.viewControllers[0] { |
| | | self.interactivePopGestureRecognizer!.delegate = self.popDelegate |
| | | }else{ |
| | | self.interactivePopGestureRecognizer!.delegate = nil |
| | | } |
| | | } |
| | | |
| | | open override var childForStatusBarHidden: UIViewController? { |
| | | return self.topViewController |
| | | } |
| | | |
| | | open override var childForStatusBarStyle: UIViewController? { |
| | | return self.topViewController |
| | | } |
| | | |
| | | } |
| | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | |
| | | // let bgGradientImg = UIImageView(image: UIImage.jq_gradient(["#B6E0FF","#FFFFFF"],size: CGSize(width: JQ_ScreenW, height: JQ_ScreenH), direction: .vertical)) |
| | | // |
| | | // view.addSubview(bgGradientImg) |
| | | // bgGradientImg.snp.makeConstraints { make in |
| | | // make.top.left.right.equalToSuperview() |
| | | // make.height.equalTo(JQ_ScreenW * 0.46) |
| | | // } |
| | | // view.sendSubviewToBack(bgGradientImg) |
| | | // view.backgroundColor = .white |
| | | |
| | | disposeBag = DisposeBag() |
| | | setUI() |
| | |
| | | navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton) |
| | | } |
| | | |
| | | if !self.isKind(of: HomeVC.self) && !self.isKind(of: HomeListenSubVC.self) && !self.isKind(of: HomeListenFight_lesson_1_VC.self) && !self.isKind(of: HomeListenFight_lesson_2_VC.self) && !self.isKind(of: HomeListenFight_lesson_3_VC.self) && !self.isKind(of: HomeListenFight_lesson_4_VC.self) && !self.isKind(of: HomeListenFight_lesson_5_VC.self) && !self.isKind(of: HomeListenGame_1_VC.self) && !self.isKind(of: HomeListenGame_2_VC.self) && !self.isKind(of: HomeListenStory_1_VC.self) && !self.isKind(of: HomeListenStory_2_VC.self){ |
| | | if !self.isKind(of: HomeVC.self) && !self.isKind(of: HomeListenSubVC.self) && !self.isKind(of: HomeListenFight_lesson_1_VC.self) && !self.isKind(of: HomeListenFight_lesson_2_VC.self) && !self.isKind(of: HomeListenFight_lesson_3_VC.self) && !self.isKind(of: HomeListenFight_lesson_4_VC.self) && !self.isKind(of: HomeListenFight_lesson_5_VC.self) && !self.isKind(of: HomeListenGame_1_VC.self) && !self.isKind(of: HomeListenGame_2_VC.self) && !self.isKind(of: HomeListenStory_1_VC.self) && !self.isKind(of: HomeListenStory_2_VC.self) && !self.isKind(of: LoginVC.self){ |
| | | let titleV = UIView() |
| | | titleV.bounds = CGRect(x: 0, y: 0, width: 156, height: 63) |
| | | // titleV.bounds = CGRect(x: 0, y: 0, width: 156, height: 24) |
| | | titleV.sizeToFit() |
| | | let imgV = UIImageView(image: UIImage(named: "bg_logo")) |
| | | imgV.contentMode = .scaleAspectFit |
| | |
| | | |
| | | view.addSubview(titleV) |
| | | titleV.snp.makeConstraints { make in |
| | | make.top.equalToSuperview().offset(18) |
| | | make.centerX.equalToSuperview() |
| | | make.center.equalToSuperview() |
| | | } |
| | | |
| | | navigationItem.titleView = titleV |
| | | |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | func setRx(){ |
| | |
| | | self.isUserInteractionEnabled = true |
| | | }); |
| | | }else { |
| | | DispatchQueue.main.async(execute: { |
| | | self.setTitle("\(time)s", for: .normal) |
| | | self.setTitleColor(unenableColor, for: .normal) |
| | | self.isUserInteractionEnabled = false |
| | | }); |
| | | self.setTitle("\(time)s", for: .normal) |
| | | self.setTitleColor(unenableColor, for: .normal) |
| | | self.isUserInteractionEnabled = false |
| | | } |
| | | time -= 1 |
| | | }); |
New file |
| | |
| | | // |
| | | // LaunchImageHelper.swift |
| | | // WanPai |
| | | // |
| | | // Created by 无故事王国 on 2023/10/19. |
| | | // |
| | | |
| | | import Foundation |
| | | |
| | | class LaunchImageHelper { |
| | | static func snapshotStoryboard(sbName: String, isPortrait: Bool) -> UIImage? { |
| | | if sbName.isEmpty { |
| | | return nil |
| | | } |
| | | |
| | | let storyboard = UIStoryboard(name: sbName, bundle: nil) |
| | | guard let vc = storyboard.instantiateInitialViewController() else { |
| | | return nil |
| | | } |
| | | |
| | | vc.view.frame = UIScreen.main.bounds |
| | | if isPortrait { |
| | | if vc.view.frame.size.width > vc.view.frame.size.height { |
| | | vc.view.frame = CGRect(x: 0, y: 0, width: vc.view.frame.size.height, height: vc.view.frame.size.width) |
| | | } |
| | | } else { |
| | | if vc.view.frame.size.width < vc.view.frame.size.height { |
| | | vc.view.frame = CGRect(x: 0, y: 0, width: vc.view.frame.size.height, height: vc.view.frame.size.width) |
| | | } |
| | | } |
| | | |
| | | vc.view.setNeedsLayout() |
| | | vc.view.layoutIfNeeded() |
| | | |
| | | UIGraphicsBeginImageContextWithOptions(vc.view.frame.size, false, UIScreen.main.scale) |
| | | vc.view.layer.render(in: UIGraphicsGetCurrentContext()!) |
| | | let image = UIGraphicsGetImageFromCurrentImageContext() |
| | | UIGraphicsEndImageContext() |
| | | return image |
| | | } |
| | | |
| | | static func snapshotStoryboardForPortrait(sbName: String) -> UIImage? { |
| | | return snapshotStoryboard(sbName: sbName, isPortrait: true) |
| | | } |
| | | |
| | | static func snapshotStoryboardForLandscape(sbName: String) -> UIImage? { |
| | | return snapshotStoryboard(sbName: sbName, isPortrait: false) |
| | | } |
| | | |
| | | static func changeAllLaunchImageToPortrait(_ image: UIImage?) { |
| | | guard let image = image else { |
| | | return |
| | | } |
| | | // 全部替换为竖屏启动图 |
| | | let resizedImage = resizeImage(image, toPortraitScreenSize: true) |
| | | BBADynamicLaunchImage.replaceLaunchImage(resizedImage) |
| | | } |
| | | |
| | | static func changeAllLaunchImageToLandscape(_ image: UIImage?) { |
| | | guard let image = image else { |
| | | return |
| | | } |
| | | // 全部替换为横屏启动图 |
| | | let resizedImage = resizeImage(image, toPortraitScreenSize: false) |
| | | BBADynamicLaunchImage.replaceLaunchImage(resizedImage) |
| | | } |
| | | |
| | | static func changePortraitLaunchImage(_ p: UIImage) { |
| | | // Implementation for this function is missing |
| | | } |
| | | |
| | | static func resizeImage(_ image: UIImage, toPortraitScreenSize: Bool) -> UIImage { |
| | | // Implementation for this function is missing |
| | | return image |
| | | } |
| | | } |
| | | |
| | | |
| | | private class BBADynamicLaunchImage { |
| | | static func launchImageCacheDirectory() -> String? { |
| | | let bundleID = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String |
| | | let fm = FileManager.default |
| | | |
| | | // iOS13之前 |
| | | if var cachesDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first { |
| | | cachesDirectory.append(contentsOf: "Snapshots") |
| | | cachesDirectory.append(contentsOf: bundleID!) |
| | | if fm.fileExists(atPath: cachesDirectory) { |
| | | return cachesDirectory |
| | | } |
| | | } |
| | | |
| | | // iOS13 |
| | | if let libraryDirectory = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first { |
| | | let snapshotsPath = String(format: "%@/SplashBoard/Snapshots/%@ - {DEFAULT GROUP}", libraryDirectory, bundleID ?? "") |
| | | if fm.fileExists(atPath: snapshotsPath) { |
| | | return snapshotsPath |
| | | } |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | static func isSnapShotName(_ name: String) -> Bool { |
| | | // 新系统后缀 |
| | | let snapshotSuffixs = ".ktx" |
| | | if name.hasSuffix(snapshotSuffixs) { |
| | | return true |
| | | } |
| | | |
| | | // 老系统后缀 |
| | | let snapshotSuffixs2 = ".png" |
| | | if name.hasSuffix(snapshotSuffixs2) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | |
| | | @discardableResult |
| | | static func replaceLaunchImage(_ replacementImage: UIImage?) -> Bool { |
| | | guard let image = replacementImage else {return false} |
| | | return self.replaceLaunchImage(replacementImage: image, compressionQuality: 0.8, customValidation: nil) |
| | | } |
| | | |
| | | @discardableResult |
| | | static func replaceLaunchImage(_ replacementImage: UIImage?, compressionQuality: CGFloat) -> Bool { |
| | | guard let image = replacementImage else {return false} |
| | | return self.replaceLaunchImage(replacementImage: image, compressionQuality: compressionQuality, customValidation: nil) |
| | | } |
| | | |
| | | static func replaceLaunchImage(replacementImage: UIImage, compressionQuality: CGFloat, customValidation: ((UIImage,UIImage) -> Bool)?) -> Bool { |
| | | |
| | | let data = replacementImage.jpegData(compressionQuality: compressionQuality) |
| | | if data == nil { |
| | | return false |
| | | } |
| | | |
| | | // if !checkImageMatchScreenSize(image: replacementImage) { |
| | | // return false |
| | | // } |
| | | |
| | | guard let cacheDir = launchImageCacheDirectory() else { |
| | | return false |
| | | } |
| | | |
| | | let cachesParentDir = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0] |
| | | let tmpDir = (cachesParentDir as NSString).appendingPathComponent("_tmpLaunchImageCaches") |
| | | |
| | | let fm = FileManager.default |
| | | if fm.fileExists(atPath: tmpDir) { |
| | | do { |
| | | try fm.removeItem(atPath: tmpDir) |
| | | } catch { |
| | | return false |
| | | } |
| | | } |
| | | |
| | | do { |
| | | try fm.moveItem(atPath: cacheDir, toPath: tmpDir) |
| | | } catch { |
| | | return false |
| | | } |
| | | |
| | | var cacheImageNames = [String]() |
| | | if let contents = try? fm.contentsOfDirectory(atPath: tmpDir) { |
| | | for name in contents { |
| | | if isSnapShotName(name) { |
| | | cacheImageNames.append(name) |
| | | } |
| | | } |
| | | } |
| | | |
| | | for name in cacheImageNames { |
| | | let filePath = (tmpDir as NSString).appendingPathComponent(name) |
| | | var result = true |
| | | |
| | | if customValidation != nil{ |
| | | if let cachedImageData = try? Data(contentsOf: URL(string: filePath)!),let cachedImage = imageFromData(data: cachedImageData as NSData){ |
| | | result = customValidation!(cachedImage,replacementImage) |
| | | } |
| | | } |
| | | |
| | | if result { |
| | | do { |
| | | try data?.write(to: URL(fileURLWithPath: filePath), options: .atomic) |
| | | } catch { |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | try? fm.moveItem(atPath: tmpDir, toPath: cacheDir) |
| | | if fm.fileExists(atPath: tmpDir) { |
| | | do { |
| | | try fm.removeItem(atPath: tmpDir) |
| | | } catch { |
| | | return false |
| | | } |
| | | } |
| | | |
| | | return true |
| | | } |
| | | |
| | | |
| | | |
| | | static func imageFromData(data: NSData) -> UIImage? { |
| | | guard let source = CGImageSourceCreateWithData(data, nil) else { |
| | | return nil |
| | | } |
| | | |
| | | if let imageRef = CGImageSourceCreateImageAtIndex(source, 0, nil) { |
| | | let originImage = UIImage(cgImage: imageRef) |
| | | return originImage |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func getImageSize(imageData: NSData) -> CGSize { |
| | | guard let source = CGImageSourceCreateWithData(imageData, nil) else { |
| | | return CGSize.zero |
| | | } |
| | | |
| | | if let imageRef = CGImageSourceCreateImageAtIndex(source, 0, nil) { |
| | | let width = CGFloat(imageRef.width) |
| | | let height = CGFloat(imageRef.height) |
| | | return CGSize(width: width, height: height) |
| | | } |
| | | |
| | | return CGSize.zero |
| | | } |
| | | |
| | | |
| | | /// 检查图片大小 |
| | | static func checkImageMatchScreenSize(image: UIImage) -> Bool { |
| | | let screenSize = CGSize(width: UIScreen.main.bounds.size.width * UIScreen.main.scale, |
| | | height: UIScreen.main.bounds.size.height * UIScreen.main.scale) |
| | | let imageSize = CGSize(width: image.size.width * image.scale, |
| | | height: image.size.height * image.scale) |
| | | |
| | | if imageSize.equalTo(screenSize) { |
| | | return true |
| | | } |
| | | |
| | | if imageSize.equalTo(CGSize(width: screenSize.height, height: screenSize.width)) { |
| | | return true |
| | | } |
| | | |
| | | return false |
| | | } |
| | | } |
| | |
| | | private let voiceCacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("voices") |
| | | private static var _sharedInstance: VoicePlayer? |
| | | private var player:AVAudioPlayer? |
| | | private var tempPlayer:AVAudioPlayer? |
| | | |
| | | var delegate:VoicePlayerDelegate? |
| | | |
| | |
| | | guard let u = url else {return} |
| | | if player?.isPlaying ?? false{ |
| | | player?.stop() |
| | | self.playComplete?() //先通知完成播放 |
| | | } |
| | | //文件存在:直接播放缓存路径的语音 |
| | | let fileURL = self.voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent).droppedScheme() |
| | | if FileManager.default.fileExists(atPath: fileURL!.absoluteString){ |
| | | self.player = try? AVAudioPlayer(contentsOf: fileURL!) |
| | | self.player?.delegate = self |
| | | self.player?.play() |
| | | self.delegate?.playing() |
| | | }else{ |
| | | //文件不存在:执行下载 |
| | | let downloadTask = URLSession.shared.downloadTask(with: URL(string: u)!) { tempLocalUrl, response, error in |
| | | if let tempLocalUrl = tempLocalUrl, error == nil { |
| | | do { |
| | | let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent) |
| | | try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl) |
| | | self.player = try? AVAudioPlayer(contentsOf: finalCacheUrl) |
| | | self.player?.delegate = self |
| | | self.player?.play() |
| | | self.delegate?.playing() |
| | | } catch { |
| | | print("视频缓存失败:catch") |
| | | } |
| | | } else { |
| | | print("视频缓存失败:\(error?.localizedDescription ?? "")") |
| | | } |
| | | } |
| | | downloadTask.resume() |
| | | } |
| | | } |
| | | |
| | | //文件存在:直接播放缓存路径的语音 |
| | | let fileURL = voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent).droppedScheme() |
| | | if FileManager.default.fileExists(atPath: fileURL!.absoluteString){ |
| | | player = try? AVAudioPlayer(contentsOf: fileURL!) |
| | | player?.delegate = self |
| | | player?.play() |
| | | delegate?.playing() |
| | | }else{ |
| | | func playerEnd(){ |
| | | player?.stop() |
| | | playComplete?() |
| | | } |
| | | |
| | | //打断播放未完全播放 |
| | | func playerInterrupt(){ |
| | | player?.stop() |
| | | } |
| | | |
| | | func playEnd(course:@escaping ()->Void){ |
| | | self.playComplete = course |
| | | } |
| | | |
| | | func playSuccessVoice(){ |
| | | let list = try? FileManager.default.contentsOfDirectory(atPath: voiceCacheDirectory.droppedScheme()!.absoluteString) |
| | | var promoteName:String? |
| | | for v in list ?? []{ |
| | | if v.contains("SuccessPromote"){promoteName = v;break} |
| | | } |
| | | guard let url = promoteName else { return } |
| | | let promote = self.voiceCacheDirectory.appendingPathComponent(url) |
| | | tempPlayer = try? AVAudioPlayer(contentsOf: promote) |
| | | tempPlayer?.play() |
| | | } |
| | | |
| | | func playFailVoice(){ |
| | | let list = try? FileManager.default.contentsOfDirectory(atPath: voiceCacheDirectory.droppedScheme()!.absoluteString) |
| | | var promoteName:String? |
| | | for v in list ?? []{ |
| | | if v.contains("FailPromote"){promoteName = v;break} |
| | | } |
| | | guard let url = promoteName else { return } |
| | | let promote = self.voiceCacheDirectory.appendingPathComponent(url) |
| | | tempPlayer = try? AVAudioPlayer(contentsOf: promote) |
| | | tempPlayer?.play() |
| | | } |
| | | |
| | | func donwloadPromoteVoice(successVoice:String,failVoice:String,updateTime:String){ |
| | | print("-->\(VoicePlayer.share().voiceCacheDirectory)") |
| | | let group = DispatchGroup() |
| | | let promoteQueue = DispatchQueue(label: "promoteVoice") |
| | | promoteQueue.async(group: group){ |
| | | //文件不存在:执行下载 |
| | | let downloadTask = URLSession.shared.downloadTask(with: URL(string: u)!) { tempLocalUrl, response, error in |
| | | let downloadTask = URLSession.shared.downloadTask(with: URL(string: successVoice)!) { tempLocalUrl, response, error in |
| | | if let tempLocalUrl = tempLocalUrl, error == nil { |
| | | do { |
| | | let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent(URL(fileURLWithPath: u).lastPathComponent) |
| | | let fileType = URL(fileURLWithPath: successVoice).lastPathComponent.components(separatedBy: ".").last ?? "mp3" |
| | | let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent("SuccessPromote.\(fileType)") |
| | | if FileManager.default.fileExists(atPath: finalCacheUrl.droppedScheme()!.absoluteString){ |
| | | try? FileManager.default.removeItem(at: finalCacheUrl) |
| | | } |
| | | try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl) |
| | | self.player = try? AVAudioPlayer(contentsOf: finalCacheUrl) |
| | | self.player?.delegate = self |
| | | self.player?.play() |
| | | self.delegate?.playing() |
| | | } catch { |
| | | print("视频缓存失败:catch") |
| | | } |
| | |
| | | } |
| | | downloadTask.resume() |
| | | } |
| | | } |
| | | |
| | | func playerEnd(){ |
| | | player?.stop() |
| | | playComplete?() |
| | | } |
| | | promoteQueue.async(group: group){ |
| | | //文件不存在:执行下载 |
| | | let downloadTask = URLSession.shared.downloadTask(with: URL(string: failVoice)!) { tempLocalUrl, response, error in |
| | | if let tempLocalUrl = tempLocalUrl, error == nil { |
| | | do { |
| | | let fileType = URL(fileURLWithPath: successVoice).lastPathComponent.components(separatedBy: ".").last ?? "mp3" |
| | | let finalCacheUrl = self.voiceCacheDirectory.appendingPathComponent("FailPromote.\(fileType)") |
| | | if FileManager.default.fileExists(atPath: finalCacheUrl.droppedScheme()!.absoluteString){ |
| | | try? FileManager.default.removeItem(at: finalCacheUrl) |
| | | } |
| | | try FileManager.default.moveItem(at: tempLocalUrl, to: finalCacheUrl) |
| | | } catch { |
| | | print("视频缓存失败:catch") |
| | | } |
| | | } else { |
| | | print("视频缓存失败:\(error?.localizedDescription ?? "")") |
| | | } |
| | | } |
| | | downloadTask.resume() |
| | | } |
| | | |
| | | func playEnd(course:@escaping ()->Void){ |
| | | self.playComplete = course |
| | | group.notify(queue: .main){ |
| | | UserDefaults.standard.setValue(updateTime, forKey: "promptVoiceDate") |
| | | UserDefaults.standard.synchronize() |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| | | <plist version="1.0"> |
| | | <dict> |
| | | <key>NSAppTransportSecurity</key> |
| | | <dict> |
| | | <key>NSAllowsArbitraryLoads</key> |
| | | <true/> |
| | | </dict> |
| | | <key>UIApplicationSceneManifest</key> |
| | | <dict> |
| | | <key>UIApplicationSupportsMultipleScenes</key> |
| | |
| | | var isOpen:Bool = false |
| | | var type = 0 // 1:图片 2:音频 |
| | | } |
| | | |
| | | struct PromptVoiceModel:HandyJSON{ |
| | | var correct: String = "" |
| | | var createBy: String = "" |
| | | var createTime: String = "" |
| | | var disabled: Bool = false |
| | | var error: String = "" |
| | | var id: Int = 0 |
| | | var img: String = "" |
| | | var integral: String = "" |
| | | var integralShare: String = "" |
| | | var phone: String = "" |
| | | var time: String = "" |
| | | var title: String = "" |
| | | var updateBy: String = "" |
| | | var updateTime: String = "" |
| | | } |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | VoicePlayer.share().delegate = nil |
| | | VoicePlayer.share().playerInterrupt() |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | |
| | | private func getNextAnswer(){ |
| | | isListen = false |
| | | if isAnsterModel.count == 4{ |
| | | print("答题答满了");return |
| | | } |
| | |
| | | viewModel.answerType.accept(.none) |
| | | collectionView.reloadData() |
| | | setUI() |
| | | |
| | | //自动播放下一题语音 |
| | | DispatchQueue.main.asyncAfter(deadline: .now()+2) { |
| | | VoicePlayer.share().playerAt(url: self.randomElement?.correct) |
| | | self.menuView?.playing() |
| | | } |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | var answer:Fight_lessonType = .none |
| | | if self?.randomElement?.id == weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].id{ |
| | | answer = .success |
| | | self?.isListen = false |
| | | if self?.isAnsterComplete == false{ |
| | | self?.rootViewModel.correctNum += 1 |
| | | } |
| | | self?.isAnsterComplete = true |
| | | VoicePlayer.share().playerAt(url: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].correct) |
| | | |
| | | VoicePlayer.share().playSuccessVoice() |
| | | DispatchQueue.main.asyncAfter(deadline: .now()+2) { |
| | | VoicePlayer.share().playerAt(url: weakSelf.listenNewModel.subjectList[weakSelf.page][index.row].correct) |
| | | } |
| | | }else{ |
| | | answer = .fail |
| | | VoicePlayer.share().playFailVoice() |
| | | self?.isListen = false |
| | | if self?.isAnsterComplete == false{ |
| | | self?.rootViewModel.errorNum += 1 |
| | | } |
| | |
| | | weakSelf.viewModel.answerType.accept(.none) |
| | | weakSelf.viewModel.selectIndex.accept(nil) |
| | | weakSelf.rootViewModel.answerItems[weakSelf.page] = weakSelf.listenNewModel.subjectList[weakSelf.page] |
| | | weakSelf.isListen = false |
| | | } |
| | | } |
| | | |
| | |
| | | // |
| | | |
| | | import UIKit |
| | | import SDWebImage |
| | | |
| | | class HomeVC: BaseVC { |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | Services.goodRecommend().subscribe(onNext: { data in |
| | | AwardListView.show(items: data.data ?? []) {[weak self] model in |
| | | let vc = MarketContentVC(goodsId: model.id) |
| | | self?.push(vc: vc) |
| | | }closeClouse: {[weak self] in |
| | | let listenMenuVC = HomeListenMenuVC() |
| | | listenMenuVC.title = "第一年学习周目选择" |
| | | self?.push(vc: listenMenuVC) |
| | | |
| | | Services.parentPage().subscribe(onNext: {data in |
| | | if let img = data.data{ |
| | | SDWebImageDownloader.shared.downloadImage(with: URL(string: img)) { image, _, _, _ in |
| | | if let img = image{ |
| | | LaunchImageHelper.changeAllLaunchImageToLandscape(img) |
| | | } |
| | | } |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | |
| | | Services.promptVoice().subscribe(onNext: {data in |
| | | if let model = data.data{ |
| | | let voice = UserDefaults.standard.object(forKey: "promptVoiceDate") as? String |
| | | if model.updateTime != voice{ |
| | | VoicePlayer.share().donwloadPromoteVoice(successVoice: model.correct, failVoice: model.error,updateTime: model.updateTime) |
| | | } |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | |
| | | </view> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="听" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rYc-7O-AJr"> |
| | | <rect key="frame" x="99" y="0.0" width="281" height="88"/> |
| | | <fontDescription key="fontDescription" type="system" pointSize="17"/> |
| | | <fontDescription key="fontDescription" type="system" weight="medium" pointSize="24"/> |
| | | <nil key="textColor"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | |
| | | @IBOutlet weak var view_container: UIView! |
| | | @IBOutlet weak var btn_play: UIButton! |
| | | @IBOutlet weak var btn_playing: UIButton! |
| | | |
| | | @IBOutlet weak var view_playHandle: UIView! |
| | | @IBOutlet weak var img_playing: UIImageView! |
| | | |
| | | private var model:Listen1SubModel! |
| | | private var playAtClouse:((IndexPath)->Void)? |
| | | var indexPath:IndexPath! |
| | |
| | | func setModel(_ model:Listen1SubModel,isplaying:Bool){ |
| | | self.model = model |
| | | self.btn_play.alpha = (isplaying ? 0:1) |
| | | self.btn_playing.alpha = (isplaying ? 0:1) |
| | | self.img_playing.alpha = (isplaying ? 1:0) |
| | | } |
| | | |
| | | func palyVoiceAt(_ clouse:@escaping(IndexPath)->Void){ |
| | | self.playAtClouse = clouse |
| | | } |
| | | |
| | | func canClick(_ state:Bool){ |
| | | btn_play.isEnabled = state |
| | | view_playHandle.backgroundColor = state == true ? UIColor(hexString: "#41A2EB") : .gray |
| | | } |
| | | |
| | | |
| | | @IBAction func playAction(_ sender: Any) { |
| | | VoicePlayer.share().playerAt(url: model.correct) |
| | | playAtClouse?(indexPath) |
| | | } |
| | | } |
| | |
| | | <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> |
| | | <state key="normal" image="icon_play_1"/> |
| | | </button> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_playing" translatesAutoresizingMaskIntoConstraints="NO" id="vMh-x4-Y7a"> |
| | | <rect key="frame" x="57" y="10.5" width="45" height="31"/> |
| | | </imageView> |
| | | </subviews> |
| | | <color key="backgroundColor" red="0.25490196078431371" green="0.63529411764705879" blue="0.92156862745098034" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
| | | <constraints> |
| | | <constraint firstItem="9Dc-Ns-SMP" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="C8e-Iq-OaB"/> |
| | | <constraint firstItem="7yc-PU-RgV" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="EUe-pu-sAP"/> |
| | | <constraint firstItem="vMh-x4-Y7a" firstAttribute="centerX" secondItem="vtr-2e-B5C" secondAttribute="centerX" id="ObG-a0-OcJ"/> |
| | | <constraint firstAttribute="trailing" secondItem="7yc-PU-RgV" secondAttribute="trailing" constant="23" id="Rms-9S-fkG"/> |
| | | <constraint firstAttribute="height" constant="52" id="Y2Z-EL-K2n"/> |
| | | <constraint firstAttribute="width" constant="159" id="oI7-Oh-ubD"/> |
| | | <constraint firstItem="9Dc-Ns-SMP" firstAttribute="leading" secondItem="vtr-2e-B5C" secondAttribute="leading" constant="25" id="xRi-cc-X9V"/> |
| | | <constraint firstItem="vMh-x4-Y7a" firstAttribute="centerY" secondItem="vtr-2e-B5C" secondAttribute="centerY" id="xql-kz-i8e"/> |
| | | </constraints> |
| | | <userDefinedRuntimeAttributes> |
| | | <userDefinedRuntimeAttribute type="boolean" keyPath="ld_maskToBoundsXIB" value="YES"/> |
| | |
| | | <outlet property="btn_play" destination="7yc-PU-RgV" id="b2b-5W-bGC"/> |
| | | <outlet property="btn_playing" destination="9Dc-Ns-SMP" id="PsY-Nd-d5z"/> |
| | | <outlet property="img_cover" destination="n5n-eb-5xI" id="Wk5-s2-MQj"/> |
| | | <outlet property="img_playing" destination="vMh-x4-Y7a" id="jWd-mt-qFI"/> |
| | | <outlet property="view_container" destination="qxz-6s-e5b" id="Ikx-kW-UkZ"/> |
| | | <outlet property="view_playHandle" destination="vtr-2e-B5C" id="I10-7z-6kl"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="352.6829268292683" y="263.38983050847457"/> |
| | | </collectionViewCell> |
| | |
| | | <resources> |
| | | <image name="icon_play" width="32" height="32"/> |
| | | <image name="icon_play_1" width="27" height="27"/> |
| | | <image name="icon_playing" width="45" height="31"/> |
| | | <systemColor name="systemBackgroundColor"> |
| | | <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| | | </systemColor> |
| | |
| | | setPages() |
| | | pageVC.reloadData() |
| | | |
| | | // if viewModel.listenType.value == .game1 || viewModel.listenType.value == .game2{ |
| | | // btn_forward.isHidden = true |
| | | // label_pageNum.isHidden = true |
| | | // |
| | | // if viewModel.listenType.value == .game1{ |
| | | // showGameLevel() |
| | | // } |
| | | // }else if viewModel.listenType.value == .story1 || viewModel.listenType.value == .story2{ |
| | | // let count = (data as! Listen1Model).storyList.count |
| | | // viewModel.maxPage.accept(count) |
| | | // label_pageNum.text = "已完成:\(viewModel.currentPage.value + 1)/\(viewModel.maxPage.value)" |
| | | // }else{ |
| | | // pageVC.reloadData() |
| | | // } |
| | | |
| | | timer = Timer(fire: .distantPast, interval: 1.0, repeats: true, block: {[weak self] _ in |
| | | self?.viewModel.times += 1 |
| | | }) |
| | |
| | | weakSelf.btn_exit.setTitle("完成", for: .normal) |
| | | } |
| | | } |
| | | |
| | | |
| | | //以下无效代码 |
| | | // |
| | | // |
| | | // //完成 |
| | | // if nextPage >= weakSelf.viewModel.maxPage.value{ |
| | | // switch weakSelf.viewModel.listenType.value { |
| | | // case .game1,.game2: |
| | | // if let dict = noti.object as? Dictionary<String,Any>{ |
| | | // weakSelf.gamesComplete(gameId: dict["gameId"] as! Int,integral: dict["gameIntegral"] as! Int) |
| | | // } |
| | | // case .lesson1,.lesson2,.lesson3,.lesson4,.lesson5: |
| | | // weakSelf.studyComplete() |
| | | // case .story1,.story2: |
| | | // if let dict = noti.object as? Dictionary<String,Any>{ |
| | | // weakSelf.storyComplete(storyId: dict["storyId"] as! Int, integral: dict["storyIntegral"] as! Int) |
| | | // } |
| | | // } |
| | | // return |
| | | // } |
| | | |
| | | weakSelf.listenFightLine = .next |
| | | weakSelf.pageVC.scroll(toPage: nextPage, animation: true) |
| | |
| | | |
| | | @objc func beforeAction(){ |
| | | listenFightLine = .before |
| | | let beforePage = max(0, viewModel.currentPage.value - 1) |
| | | var beforePage = max(0, viewModel.currentPage.value - 1) |
| | | pageVC.scroll(toPage: beforePage, animation: true) |
| | | viewModel.currentPage.accept(beforePage) |
| | | |
| | |
| | | class HomeListenFight_lesson_2_VC: BaseVC { |
| | | |
| | | private var viewModel = FightAnswerViewModel() |
| | | |
| | | private var listenNewModel:ListenNewModel! |
| | | // private var randomElement:Listen1SubModel? |
| | | private var page:Int! |
| | | var rootViewModel:HomeListenFightViewModel! |
| | | private var tempViews = [StudyHandleView]() |
| | |
| | | private var playedIndex = Set<Int>() //已经播放过的view |
| | | private var voicePlayer = VoicePlayer.share() |
| | | private var isAnsterModel = Set<Listen1SubModel>() |
| | | |
| | | // private var isAnsterComplete:Bool = false //是否已经回答完成[小题] |
| | | // private var isAnsterDone:Bool = false //是否已经回答完成[大题] |
| | | |
| | | private lazy var stackView:UIStackView = { |
| | | let stackView = UIStackView() |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | VoicePlayer.share().delegate = nil |
| | | VoicePlayer.share().playerInterrupt() |
| | | } |
| | | |
| | | func restore(){ |
| | |
| | | var lessionType:Fight_lessonType = .none |
| | | if handleView.vioceSoundUrl == weakSelf.listenNewModel.subjectList[weakSelf.page][row].correct{ |
| | | lessionType = .success |
| | | weakSelf.voicePlayer.playerEnd() |
| | | weakSelf.voicePlayer.playSuccessVoice() |
| | | // weakSelf.voicePlayer.playerInterrupt() |
| | | }else{ |
| | | lessionType = .fail |
| | | weakSelf.voicePlayer.playFailVoice() |
| | | } |
| | | |
| | | switch lessionType { |
| | |
| | | } |
| | | } |
| | | |
| | | // if isAnsterComplete{ |
| | | //// getNextAnswer() |
| | | // if !isAnsterDone{ |
| | | // let v = rootViewModel.answerCount.value |
| | | // rootViewModel.answerCount.accept(v + 1) |
| | | // } |
| | | // } |
| | | } |
| | | } |
| | |
| | | private var answterCount:Int = 0 //回答计数,用于确定角标 |
| | | var rootViewModel:HomeListenFightViewModel! |
| | | private var voicePlayer = VoicePlayer.share() |
| | | private var playIndex = Set<IndexPath>() //顺序播放 |
| | | private var isPlayingIndex:IndexPath? //正在播放中 |
| | | |
| | | 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) { |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.delegate = nil |
| | | VoicePlayer.share().playerInterrupt() |
| | | } |
| | | |
| | | |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | navigationItem.titleView = UIView() |
| | | // viewModel.selectIndex.accept(IndexPath(row: 0, section: 0)) |
| | | |
| | | setAnswerStackView() |
| | | playIndex.insert(IndexPath(row: 0, section: 0)) |
| | | // setAnswerStackView() |
| | | } |
| | | |
| | | func restore(){ |
| | |
| | | } |
| | | |
| | | override func setRx() { |
| | | viewModel.selectIndex.subscribe(onNext: {[weak self] index in |
| | | |
| | | guard let index = index else { return } |
| | | //判断选中是否正确逻辑 |
| | | if let cell = self?.collectionView.dequeueReusableCell(withReuseIdentifier: "_ListenFight_lesson_3_CCell", for: index) as? ListenFight_lesson_1_CCell{ |
| | | var answerType:Fight_lessonType = .none |
| | | answerType = .success |
| | | |
| | | switch answerType { |
| | | case .success: |
| | | self?.viewModel.answerType.accept(.success) |
| | | case .fail: |
| | | self?.viewModel.answerType.accept(.fail) |
| | | default:break |
| | | } |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | private func setAnswerStackView(){ |
| | |
| | | tempImageArray.append(listenNewModel.subjectList[page][2].img) |
| | | tempImageArray.append(listenNewModel.subjectList[page][4].img) |
| | | tempImageArray.append(listenNewModel.subjectList[page][5].img) |
| | | // tempImageArray.shuffle() |
| | | |
| | | view.addSubview(stackView) |
| | | stackView.snp.makeConstraints { make in |
| | |
| | | make.centerY.equalToSuperview() |
| | | make.height.equalTo(52) |
| | | } |
| | | |
| | | var tempAnswerViews = [Lesson_3_AnswerView]() |
| | | for i in 0...2{ |
| | | let answerView = Lesson_3_AnswerView.jq_loadNibView() |
| | | answerView.alpha = 0 |
| | |
| | | UIView.animate(withDuration: 0.05 + Double(i)) { |
| | | answerView.alpha = 1 |
| | | } |
| | | |
| | | stackView.insertArrangedSubview(answerView, at: 0) |
| | | tempAnswerViews.append(answerView) |
| | | } |
| | | tempAnswerViews.shuffle() |
| | | stackView.addArrangedSubviews(tempAnswerViews) |
| | | } |
| | | |
| | | @objc private func chooseAnswerAction(btn:UIButton){ |
| | | if viewModel.selectIndex.value == nil{alertError(msg: "请先听题");return} |
| | | |
| | | let index = btn.tag - 10 |
| | | if isPlayingIndex != nil { |
| | | alertError(msg: "请先听题");return |
| | | } |
| | | |
| | | if rootViewModel.correctNum == 0 && !playIndex.contains(IndexPath(row: 2, section: 0)){ |
| | | alertError(msg: "请先听题");return |
| | | } |
| | | |
| | | if rootViewModel.correctNum == 1 && !playIndex.contains(IndexPath(row: 1, section: 1)){ |
| | | alertError(msg: "请先听题");return |
| | | } |
| | | |
| | | if rootViewModel.correctNum == 2 && !playIndex.contains(IndexPath(row: 2, section: 1)){ |
| | | alertError(msg: "请先听题");return |
| | | } |
| | | |
| | | var subV:Lesson_3_AnswerView? |
| | | |
| | | for (i,v) in stackView.arrangedSubviews.reversed().enumerated(){ |
| | | if index == i{subV = v as? Lesson_3_AnswerView;break} |
| | | for (_,v) in (stackView.arrangedSubviews as! [Lesson_3_AnswerView]).enumerated(){ |
| | | if v.btn_choose.tag == btn.tag{ |
| | | subV = v;break |
| | | } |
| | | } |
| | | |
| | | var answerType:Fight_lessonType = .none |
| | |
| | | |
| | | if subV?.imageUrl == listenNewModel.subjectList[page][valueIndex].img{ |
| | | answerType = .success |
| | | voicePlayer.playSuccessVoice() |
| | | }else{ |
| | | answerType = .fail |
| | | voicePlayer.playFailVoice() |
| | | } |
| | | |
| | | switch answerType { |
| | |
| | | var ansterIndePath:IndexPath? |
| | | if viewModel.selectIndex.value?.section == 0{ |
| | | ansterIndePath = IndexPath(row: 2, section: 0) |
| | | playIndex.insert(IndexPath(row: 0, section: 1)) //下一个准备播放 |
| | | } |
| | | |
| | | if viewModel.selectIndex.value?.section == 1 && (viewModel.selectIndex.value?.row == 0 || viewModel.selectIndex.value?.row == 1){ |
| | | ansterIndePath = IndexPath(row: 1, section: 1) |
| | | playIndex.insert(IndexPath(row: 2, section: 1)) //下一个准备播放 |
| | | } |
| | | |
| | | if viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 2{ |
| | |
| | | self.viewModel.selectIndex.accept(nil) |
| | | let v = self.rootViewModel.answerCount.value + 1 |
| | | self.rootViewModel.answerCount.accept(v) |
| | | self.collectionView.reloadData() |
| | | } |
| | | } |
| | | } |
| | |
| | | cell.backgroundColor = .clear |
| | | cell.indexPath = indexPath |
| | | cell.contentView.backgroundColor = .clear |
| | | cell.canClick(playIndex.contains(indexPath)) |
| | | cell.palyVoiceAt {[weak self] index in |
| | | self?.viewModel.selectIndex.accept(index) |
| | | guard let weakSelf = self else { return } |
| | | weakSelf.isPlayingIndex = index |
| | | |
| | | weakSelf.voicePlayer.playerEnd() |
| | | weakSelf.voicePlayer.playerAt(url: weakSelf.listenNewModel.subjectList[weakSelf.page][indexPath.row].correct) |
| | | |
| | | weakSelf.viewModel.selectIndex.accept(index) |
| | | //点击答案,就显示 |
| | | if (index.section == 0 && index.row == 2) || (index.section == 1 && index.row > 0){ |
| | | self?.viewModel.selectIndex.accept(index) |
| | | self?.setAnswerStackView() |
| | | weakSelf.viewModel.selectIndex.accept(index) |
| | | weakSelf.setAnswerStackView() |
| | | } |
| | | |
| | | collectionView.reloadItems(at: [index]) |
| | |
| | | }else{ |
| | | cell.img_cover.image = nil |
| | | } |
| | | cell.setModel(model,isplaying: viewModel.selectIndex.value == indexPath) |
| | | cell.setModel(model,isplaying: isPlayingIndex == indexPath) |
| | | } |
| | | |
| | | if indexPath.section == 1{ |
| | |
| | | }else{ |
| | | cell.img_cover.image = nil |
| | | } |
| | | cell.setModel(model,isplaying: viewModel.selectIndex.value == indexPath) |
| | | cell.setModel(model,isplaying: isPlayingIndex == indexPath) |
| | | } |
| | | return cell |
| | | } |
| | |
| | | |
| | | extension HomeListenFight_lesson_3_VC:VoicePlayerDelegate{ |
| | | func playComplete() { |
| | | collectionView.reloadData() |
| | | isPlayingIndex = nil |
| | | |
| | | var nextRow = (viewModel.selectIndex.value?.row ?? 0) + 1 |
| | | var section = (viewModel.selectIndex.value?.section ?? 0) + 0 |
| | | |
| | | if nextRow >= 3{ |
| | | nextRow = 0;section = 1 |
| | | } |
| | | |
| | | |
| | | if self.answterCount == 3{ |
| | | self.rootViewModel.answerItems[self.page] = self.listenNewModel.subjectList[self.page] |
| | | NotificationCenter.default.post(name: NextLession_Noti, object: nil) |
| | | VoicePlayer.share().playerEnd() |
| | | return |
| | | } |
| | | |
| | | if (viewModel.selectIndex.value?.section == 0 && viewModel.selectIndex.value?.row == 2) || (viewModel.selectIndex.value?.section == 1 && viewModel.selectIndex.value?.row == 1){ |
| | | collectionView.reloadData() |
| | | return |
| | | } |
| | | playIndex.insert(IndexPath(row: nextRow, section: section)) //下一个准备播放 |
| | | collectionView.reloadData() |
| | | } |
| | | |
| | | func playing() { |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.delegate = nil |
| | | VoicePlayer.share().playerInterrupt() |
| | | } |
| | | |
| | | |
| | |
| | | var answerType:Fight_lessonType = .none |
| | | if tempSubV?.voiceUrl == answerModel?.correct{ |
| | | answerType = .success |
| | | voicePlayer.playSuccessVoice() |
| | | answerCount += 1 |
| | | rootViewModel.correctNum += 1 |
| | | let v = rootViewModel.answerCount.value + 1 |
| | | rootViewModel.answerCount.accept(v) |
| | | }else{ |
| | | answerType = .fail |
| | | voicePlayer.playFailVoice() |
| | | rootViewModel.errorNum += 1 |
| | | } |
| | | voicePlayer.playerEnd() |
| | |
| | | private var playVoiceRealAt:Int? //播放声音的View -被乱序后,真实Index |
| | | var rootViewModel:HomeListenFightViewModel! |
| | | private var voicePlayer = VoicePlayer.share() |
| | | private var isListen:Bool = false |
| | | |
| | | private lazy var collectionView:UICollectionView = { |
| | | let flowLayout = UICollectionViewFlowLayout() |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.delegate = nil |
| | | VoicePlayer.share().playerInterrupt() |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | extension HomeListenFight_lesson_5_VC:UICollectionViewDelegate{ |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | |
| | | if playVoiceAt == nil{ |
| | | if isListen == false{ |
| | | alertError(msg: "请先听题");return |
| | | } |
| | | isListen = false |
| | | |
| | | viewModel.selectIndex.accept(indexPath) |
| | | |
| | |
| | | let selectAnswer = listenNewModel.subjectList[page][indexPath.row] |
| | | |
| | | var answerType:Fight_lessonType = .none |
| | | |
| | | if answer.id == selectAnswer.id{ |
| | | answerType = .success |
| | | voicePlayer.playSuccessVoice() |
| | | }else{ |
| | | answerType = .fail |
| | | voicePlayer.playFailVoice() |
| | | } |
| | | |
| | | let tempSubV = stackView.arrangedSubviews[self.playVoiceRealAt!] as! VoiceHandleView |
| | |
| | | |
| | | extension HomeListenFight_lesson_5_VC:VoicePlayerDelegate{ |
| | | func playComplete() { |
| | | isListen = true |
| | | for subV in stackView.arrangedSubviews as! [VoiceHandleView]{ |
| | | subV.resetView() |
| | | } |
| | |
| | | super.viewDidDisappear(animated) |
| | | timer?.invalidate() |
| | | voicePlayer.delegate = nil |
| | | voicePlayer.playerEnd() |
| | | voicePlayer.playerInterrupt() |
| | | } |
| | | |
| | | override func viewDidLoad() { |
| | |
| | | |
| | | if currentAnswer?.id == listen1Model?.subjectList[row].id{ |
| | | answerType = .success |
| | | voicePlayer.playSuccessVoice() |
| | | }else{ |
| | | answerType = .fail |
| | | voicePlayer.playFailVoice() |
| | | } |
| | | switch answerType { |
| | | case .success: |
| | |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.delegate = nil |
| | | timer?.invalidate() |
| | | voicePlayer.playerInterrupt() |
| | | } |
| | | |
| | | override func viewDidLoad() { |
| | |
| | | |
| | | switch answerType { |
| | | case .success: |
| | | voicePlayer.playSuccessVoice() |
| | | viewModel.answerType.accept(.success) |
| | | collectionView.reloadData() |
| | | |
| | | case .fail: |
| | | voicePlayer.playFailVoice() |
| | | viewModel.answerType.accept(.fail) |
| | | collectionView.reloadData() |
| | | DispatchQueue.main.asyncAfter(wallDeadline: .now()+2.5) { |
| | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | getData() |
| | | Services.goodRecommend().subscribe(onNext: { data in |
| | | AwardListView.show(items: data.data ?? []) { _ in |
| | | |
| | | }closeClouse: { () in |
| | | |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | |
| | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.delegate = nil |
| | | voicePlayer.playerInterrupt() |
| | | } |
| | | |
| | | required init?(coder: NSCoder) { |
| | |
| | | var lessionType:Fight_lessonType = .none |
| | | if weakSelf.listen1Model.storyList[weakSelf.page].correct == handleView.vioceSoundUrl{ |
| | | lessionType = .success |
| | | weakSelf.voicePlayer.playSuccessVoice() |
| | | }else{ |
| | | lessionType = .fail |
| | | weakSelf.voicePlayer.playFailVoice() |
| | | } |
| | | |
| | | switch lessionType { |
| | |
| | | |
| | | } |
| | | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | voicePlayer.playerInterrupt() |
| | | } |
| | | |
| | | |
| | | override func setUI() { |
| | | super.setUI() |
| | |
| | | DispatchQueue.main.asyncAfter(wallDeadline: .now()+2){ |
| | | handleView.playUrl = self.listen1Model.storyList[self.page].correct |
| | | } |
| | | // handleView.chooseClouse {[weak self] btn in |
| | | //// guard let weakSelf = self else { return } |
| | | // var lessionType:Fight_lessonType = .none |
| | | // lessionType = .success |
| | | // switch lessionType { |
| | | // case .success: |
| | | // handleView.btn_choose.isSelected = true |
| | | // handleView.btn_state.setImage(UIImage(named: "icon_success_small"), for: .normal) |
| | | // UIView.animate(withDuration: 0.5) { |
| | | // handleView.btn_state.alpha = 1 |
| | | // } |
| | | // case .fail: |
| | | // handleView.btn_state.setImage(UIImage(named: "icon_waring_small"), for: .normal) |
| | | // UIView.animate(withDuration: 0.5) { |
| | | // handleView.btn_state.alpha = 1 |
| | | // } |
| | | // default: |
| | | // handleView.btn_state.setImage(nil, for: .normal) |
| | | // } |
| | | // } |
| | | |
| | | handleView.snp.makeConstraints { make in |
| | | make.height.equalTo(52) |
| | |
| | | } |
| | | |
| | | private func getData(){ |
| | | Services.studySchedule(week: week, day: page + 1).subscribe(onNext: {data in |
| | | Services.studySchedule(week: week).subscribe(onNext: {data in |
| | | self.studyScheduleModel = data.data |
| | | self.tableView.reloadData() |
| | | }).disposed(by: disposeBag) |
| | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | |
| | | Services.studySchedule(week: week, day: 1).subscribe(onNext: {data in |
| | | Services.studySchedule(week: week).subscribe(onNext: {data in |
| | | self.limitDay = data.data?.day ?? 0 |
| | | |
| | | #if DEBUG |
| | |
| | | |
| | | stackView.isHidden = listenType == .story2 |
| | | label_ratioNum.isHidden = listenType == .story2 |
| | | |
| | | NotificationCenter.default.post(name: Refresh_ListenSchedule_Noti, object: nil) |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="请输入" textAlignment="right" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="9Rg-uR-zdR" customClass="QMUITextField"> |
| | | <rect key="frame" x="90.5" y="0.0" width="778.5" height="54"/> |
| | | <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/> |
| | | <textInputTraits key="textInputTraits" keyboardType="phonePad"/> |
| | | <textInputTraits key="textInputTraits"/> |
| | | <userDefinedRuntimeAttributes> |
| | | <userDefinedRuntimeAttribute type="number" keyPath="maximumTextLength"> |
| | | <integer key="value" value="11"/> |
| | |
| | | class SceneDelegate: UIResponder, UIWindowSceneDelegate { |
| | | |
| | | var window: UIWindow? |
| | | private var disposeBag = JQ_disposeBag |
| | | |
| | | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { |
| | | guard let windowScene = (scene as? UIWindowScene) else { return } |
| | |
| | | func needLogin(){ |
| | | sceneDelegate?.window?.rootViewController = BaseNav(rootViewController: LoginVC()) |
| | | sceneDelegate?.window?.makeKeyAndVisible() |
| | | LoginTokenModel.clearToken() |
| | | |
| | | } |
| | | } |
| | | |
| | |
| | | switch next.code{ |
| | | case 200:ob.onNext(next) |
| | | case 504: //登录设备最大限制 |
| | | let attribute = AttributedStringbuilder.build().add(string:next.msg, withFont: .systemFont(ofSize: 14, weight: .medium), withColor: .black.withAlphaComponent(0.6)).mutableAttributedString |
| | | // CommonAlertView.show(title: "提示", attribute: attribute, cancelStr: "关闭", completeStr: "关闭", isSingle: true, customBtnWidth: JQ_ScreenW - 170) { _ in |
| | | // |
| | | // } |
| | | break |
| | | case 503: //登录被冻结 |
| | | let attribute = AttributedStringbuilder.build().add(string:next.msg, withFont: .systemFont(ofSize: 14, weight: .medium), withColor: .black.withAlphaComponent(0.6)).mutableAttributedString |
| | | // CommonAlertView.show(title: "提示", attribute: attribute, cancelStr: "关闭", completeStr: "关闭", isSingle: true, customBtnWidth: JQ_ScreenW - 170) { _ in |
| | | // |
| | | // } |
| | | DispatchQueue.main.async { |
| | | alert(msg: next.msg) |
| | | } |
| | | // case 501: |
| | | // CommonAlertView.show(title: "提示", content: next.msg,isSingle: true) { _ in |
| | | // |
| | |
| | | sceneDelegate?.needLogin() |
| | | default: |
| | | if !ignoreAlert{ |
| | | alertError(msg: "\(next.msg)") |
| | | DispatchQueue.main.async { |
| | | alertError(msg: "\(next.msg)") |
| | | } |
| | | } |
| | | ob.onError(NetRequestError.Other(next.code,next.msg)) |
| | | } |
| | |
| | | |
| | | #if DEBUG |
| | | let All_Url = "http://192.168.110.237:9000" |
| | | //let All_Url = "http://1.95.15.237:9000" |
| | | #else |
| | | let All_Url = "http://192.168.110.237:9000" |
| | | let All_Url = "http://1.95.15.237:9000" |
| | | #endif |
| | | |
| | | class Services: NSObject { |
| | |
| | | return NetworkRequest.request(params: params, method: .get, progress: true) |
| | | } |
| | | |
| | | class func studySchedule(week:Int,day:Int)->Observable<BaseResponse<StudyScheduleModel>>{ |
| | | class func studySchedule(week:Int)->Observable<BaseResponse<StudyScheduleModel>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/study/base/study/studySchedule") |
| | | params.append(key: "week", value: week) |
| | | params.append(key: "day", value: day) |
| | | return NetworkRequest.request(params: params, method: .get, progress: true) |
| | | } |
| | | |
| | | class func parentPage()->Observable<BaseResponse<String>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/study/base/user/parentPage") |
| | | return NetworkRequest.request(params: params, method: .post, progress: false) |
| | | } |
| | | |
| | | class func promptVoice()->Observable<BaseResponse<PromptVoiceModel>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/study/base/study/promptVoice") |
| | | return NetworkRequest.request(params: params, method: .get, progress: false) |
| | | } |
| | | } |
| | | |
| | | // MARK: -- 登录部分 |