| | |
| | | 1385E0072C6C558200AADB1F /* HomeRelaxBanner_2_CCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1385E0052C6C558200AADB1F /* HomeRelaxBanner_2_CCell.xib */; }; |
| | | 1385E00A2C6C57A900AADB1F /* HomeItemDetailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1385E0082C6C57A900AADB1F /* HomeItemDetailVC.swift */; }; |
| | | 1385E00B2C6C57A900AADB1F /* HomeItemDetailVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1385E0092C6C57A900AADB1F /* HomeItemDetailVC.xib */; }; |
| | | 1388840E2C92B5E80043AAB4 /* CommonBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1388840D2C92B5E80043AAB4 /* CommonBannerView.swift */; }; |
| | | 13897D892C7DB9D7006209E0 /* EqualCellSpaceFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13897D882C7DB9D7006209E0 /* EqualCellSpaceFlowLayout.swift */; }; |
| | | 138F0C322C7594BB0072A16C /* TreatyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138F0C312C7594BB0072A16C /* TreatyVC.swift */; }; |
| | | 138F0C352C7597CA0072A16C /* HelpCenterVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138F0C332C7597CA0072A16C /* HelpCenterVC.swift */; }; |
| | |
| | | 1385E0052C6C558200AADB1F /* HomeRelaxBanner_2_CCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeRelaxBanner_2_CCell.xib; sourceTree = "<group>"; }; |
| | | 1385E0082C6C57A900AADB1F /* HomeItemDetailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeItemDetailVC.swift; sourceTree = "<group>"; }; |
| | | 1385E0092C6C57A900AADB1F /* HomeItemDetailVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeItemDetailVC.xib; sourceTree = "<group>"; }; |
| | | 1388840D2C92B5E80043AAB4 /* CommonBannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonBannerView.swift; sourceTree = "<group>"; }; |
| | | 13897D882C7DB9D7006209E0 /* EqualCellSpaceFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EqualCellSpaceFlowLayout.swift; sourceTree = "<group>"; }; |
| | | 138F0C312C7594BB0072A16C /* TreatyVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreatyVC.swift; sourceTree = "<group>"; }; |
| | | 138F0C332C7597CA0072A16C /* HelpCenterVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpCenterVC.swift; sourceTree = "<group>"; }; |
| | |
| | | 13D256B52C6C68EB006FC2D7 /* View */ = { |
| | | isa = PBXGroup; |
| | | children = ( |
| | | 1388840D2C92B5E80043AAB4 /* CommonBannerView.swift */, |
| | | 136C7C7C2C7715C9004540CD /* BitrhdayPickerView.swift */, |
| | | 13F24E432C75901500D2BA90 /* CommonAlertView.swift */, |
| | | 13F24E442C75901600D2BA90 /* CommonAlertView.xib */, |
| | |
| | | 13985DAF2C69B7B00046B6DC /* BaseNav.swift in Sources */, |
| | | 134A45352C6E0E0000538D78 /* CourseVCTeacherSpecialVC.swift in Sources */, |
| | | 13E4ECEE2C80778A0095AD04 /* PlanGuide_2_VC.swift in Sources */, |
| | | 1388840E2C92B5E80043AAB4 /* CommonBannerView.swift in Sources */, |
| | | 134A45392C6E167D00538D78 /* CourseOfficalCommendTopCCell.swift in Sources */, |
| | | 1338A6DD2C76DD5E006CDD15 /* SpendingDetailInfoVC.swift in Sources */, |
| | | 138FE0E22C757DE900A964E8 /* BindPhone_2_VC.swift in Sources */, |
| | |
| | | GENERATE_INFOPLIST_FILE = YES; |
| | | INFOPLIST_FILE = XQMuse/Info.plist; |
| | | INFOPLIST_KEY_CFBundleDisplayName = "心泉冥想"; |
| | | INFOPLIST_KEY_LSApplicationCategoryType = ""; |
| | | INFOPLIST_KEY_NSCameraUsageDescription = "相机"; |
| | | INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "定位"; |
| | | INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "定位"; |
| | | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "相册"; |
| | | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; |
| | | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; |
| | |
| | | GENERATE_INFOPLIST_FILE = YES; |
| | | INFOPLIST_FILE = XQMuse/Info.plist; |
| | | INFOPLIST_KEY_CFBundleDisplayName = "心泉冥想"; |
| | | INFOPLIST_KEY_LSApplicationCategoryType = ""; |
| | | INFOPLIST_KEY_NSCameraUsageDescription = "相机"; |
| | | INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "定位"; |
| | | INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "定位"; |
| | | INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "相册"; |
| | | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; |
| | | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; |
New file |
| | |
| | | { |
| | | "images" : [ |
| | | { |
| | | "idiom" : "universal", |
| | | "scale" : "1x" |
| | | }, |
| | | { |
| | | "filename" : "icon_play_close@2x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "2x" |
| | | }, |
| | | { |
| | | "filename" : "icon_play_close@3x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "3x" |
| | | } |
| | | ], |
| | | "info" : { |
| | | "author" : "xcode", |
| | | "version" : 1 |
| | | } |
| | | } |
New file |
| | |
| | | { |
| | | "images" : [ |
| | | { |
| | | "idiom" : "universal", |
| | | "scale" : "1x" |
| | | }, |
| | | { |
| | | "filename" : "icon_play_small@2x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "2x" |
| | | }, |
| | | { |
| | | "filename" : "icon_play_small@3x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "3x" |
| | | } |
| | | ], |
| | | "info" : { |
| | | "author" : "xcode", |
| | | "version" : 1 |
| | | } |
| | | } |
New file |
| | |
| | | { |
| | | "images" : [ |
| | | { |
| | | "idiom" : "universal", |
| | | "scale" : "1x" |
| | | }, |
| | | { |
| | | "filename" : "icon_use_small_s@2x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "2x" |
| | | }, |
| | | { |
| | | "filename" : "icon_use_small_s@3x.png", |
| | | "idiom" : "universal", |
| | | "scale" : "3x" |
| | | } |
| | | ], |
| | | "info" : { |
| | | "author" : "xcode", |
| | | "version" : 1 |
| | | } |
| | | } |
| | |
| | | case payment = 3 // 需支付 |
| | | } |
| | | |
| | | enum DeliverStudyType:Int,HandyJSONEnum{ |
| | | case online = 1 // 线上课程 |
| | | case offline = 2 //线下课程 |
| | | } |
| | | |
| | | enum ConditionType:Int,HandyJSONEnum{ |
| | | case yes = 1 |
| | | case no = 2 |
| | |
| | | |
| | | class CourseOfficalCommendTopCCell: UICollectionViewCell { |
| | | |
| | | @IBOutlet weak var view_bannerContentView: UIView! |
| | | @IBOutlet weak var view_bannerContentView: CommonBannerView! |
| | | @IBOutlet weak var collectionView: UICollectionView! |
| | | @IBOutlet weak var cons_hei: NSLayoutConstraint! |
| | | private var titleItems = [TitleItem]() |
| | | private var bannerModels = [CommonBannerModel]() |
| | | private var clouse:((Int)->Void)? |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | |
| | | |
| | | func setTitles(_ items:[TitleItem]){ |
| | | titleItems = items |
| | | cons_hei.constant = ceil(Double(items.count) / 4) * 103.75 |
| | | collectionView.reloadData() |
| | | } |
| | | |
| | | func setBanners(_ items:[CommonBannerModel]){ |
| | | bannerModels = items |
| | | view_bannerContentView.setItems(items: items) |
| | | } |
| | | |
| | | func clickAtClouse(_ clouse:@escaping (Int)->Void){ |
| | | self.clouse = clouse |
| | | } |
| | |
| | | <rect key="frame" x="0.0" y="0.0" width="328" height="408"/> |
| | | <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> |
| | | <subviews> |
| | | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LpV-bY-BWS"> |
| | | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LpV-bY-BWS" customClass="CommonBannerView" customModule="XQMuse" customModuleProvider="target"> |
| | | <rect key="frame" x="0.0" y="0.0" width="328" height="171"/> |
| | | <color key="backgroundColor" systemColor="systemBackgroundColor"/> |
| | | <constraints> |
| | |
| | | <size key="customSize" width="328" height="408"/> |
| | | <connections> |
| | | <outlet property="collectionView" destination="IAR-Fq-op8" id="B6V-Mt-S1J"/> |
| | | <outlet property="cons_hei" destination="JM2-hK-TGn" id="ICv-Ho-iAa"/> |
| | | <outlet property="view_bannerContentView" destination="LpV-bY-BWS" id="AyT-5Y-Vaq"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="293.12977099236639" y="146.47887323943664"/> |
| | |
| | | |
| | | import UIKit |
| | | import WebKit |
| | | import RxSwift |
| | | |
| | | class CourseDetail_1_TCell: UITableViewCell { |
| | | |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var webView: WKWebView! |
| | | @IBOutlet weak var cons_webHei: NSLayoutConstraint! |
| | | |
| | | private var disposeBag = DisposeBag() |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | backgroundColor = .clear |
| | |
| | | webView.scrollView.isScrollEnabled = false |
| | | webView.scrollView.backgroundColor = .clear |
| | | webView.isOpaque = false |
| | | |
| | | self.webView.scrollView.rx.observe(CGSize.self, "contentSize").map { (size) -> CGFloat? in |
| | | if let size = size{ |
| | | return size.height |
| | | } |
| | | return nil |
| | | }.subscribe(onNext: { [unowned self](height) in |
| | | if let height = height{ |
| | | self.cons_webHei.constant = height |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | func setContent(title:String,content:String){ |
| | |
| | | </constraints> |
| | | </tableViewCellContentView> |
| | | <connections> |
| | | <outlet property="cons_webHei" destination="hfa-qQ-TpS" id="y2I-NC-2Cu"/> |
| | | <outlet property="label_title" destination="Lg9-70-w3n" id="NPf-Sa-PgL"/> |
| | | <outlet property="webView" destination="WYV-JQ-E3S" id="wJi-nk-vgm"/> |
| | | </connections> |
| | |
| | | |
| | | class CourseDetail_2_Inner_TCell: UITableViewCell { |
| | | |
| | | @IBOutlet weak var label_index: UILabel! |
| | | @IBOutlet weak var btn_study: UIButton! |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_time: UILabel! |
| | | @IBOutlet weak var label_people: UILabel! |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | selectionStyle = .none |
| | | btn_study.isUserInteractionEnabled = false |
| | | } |
| | | |
| | | func setModel(_ item:CourseItemModel,index:IndexPath){ |
| | | label_index.text = "\(index.row + 1)" |
| | | label_title.text = item.chapterTitle |
| | | label_time.text = Date.jq_formateToTime(item.duration) |
| | | label_people.text = "\(item.realLearnedNum + item.virtualLearnedNum)" |
| | | } |
| | | |
| | | @IBAction func studyAction(_ sender: Any) { |
| | | let vc = CourseDetialVideoVC() |
| | | JQ_currentViewController().jq_push(vc: vc) |
| | | // let vc = CourseDetialVideoVC() |
| | | // JQ_currentViewController().jq_push(vc: vc) |
| | | } |
| | | |
| | | } |
| | |
| | | <viewLayoutGuide key="safeArea" id="njF-e1-oar"/> |
| | | <connections> |
| | | <outlet property="btn_study" destination="q5a-cH-u74" id="vg6-WV-mdc"/> |
| | | <outlet property="label_index" destination="uL3-AT-OQF" id="4bx-wD-U6f"/> |
| | | <outlet property="label_people" destination="c0R-vF-UBG" id="S53-bw-wc8"/> |
| | | <outlet property="label_time" destination="bXU-MP-QyS" id="O7i-eB-GXV"/> |
| | | <outlet property="label_title" destination="awd-gM-ZRD" id="Tpo-Bu-RQX"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="148.85496183206106" y="41.549295774647888"/> |
| | | </tableViewCell> |
| | |
| | | // |
| | | |
| | | import UIKit |
| | | import JQTools |
| | | |
| | | class CourseDetail_2_TCell: UITableViewCell { |
| | | |
| | | @IBOutlet weak var tableView: UITableView! |
| | | @IBOutlet weak var cons_tableHei: NSLayoutConstraint! |
| | | |
| | | // private(set) var clouse:((IndexPath)->Void)? |
| | | private var items = [CourseItemModel]() |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | |
| | | tableView.backgroundColor = UIColor(hexString: "f6f6f6") |
| | | tableView.separatorStyle = .none |
| | | tableView.register(UINib(nibName: "CourseDetail_2_Inner_TCell", bundle: nil), forCellReuseIdentifier: "_CourseDetail_2_Inner_TCell") |
| | | cons_tableHei.constant = 70.5 * 5 |
| | | cons_tableHei.constant = 0 |
| | | } |
| | | |
| | | // func selectAt(_ clouse:@escaping (IndexPath)->Void){ |
| | | // if self.clouse == nil{ |
| | | // self.clouse = clouse |
| | | // } |
| | | // } |
| | | func setItems(_ items:[CourseItemModel]){ |
| | | self.items = items |
| | | cons_tableHei.constant = 70.5 * Double(items.count) |
| | | self.tableView.reloadData() |
| | | } |
| | | } |
| | | |
| | | extension CourseDetail_2_TCell:UITableViewDelegate & UITableViewDataSource{ |
| | | |
| | | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { |
| | | // clouse?(indexPath) |
| | | let vc = CourseDetialVideoVC(items: items, selectIndex: indexPath) |
| | | JQ_currentViewController().jq_push(vc: vc) |
| | | } |
| | | |
| | | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| | | return 5 |
| | | return items.count |
| | | } |
| | | |
| | | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| | | let model = items[indexPath.row] |
| | | let cell = tableView.dequeueReusableCell(withIdentifier: "_CourseDetail_2_Inner_TCell", for: indexPath) as! CourseDetail_2_Inner_TCell |
| | | cell.setModel(model, index: indexPath) |
| | | cell.backgroundColor = .clear |
| | | return cell |
| | | } |
| | |
| | | private let CellW = (JQ_ScreenW - 21.5 * 2 - 13.5) / 2 |
| | | private let CellH = ((JQ_ScreenW - 21.5 * 2 - 13.5) / 2) * 1.313 |
| | | |
| | | var items = [CourseModel]() |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | backgroundColor = .clear |
| | |
| | | collectionView.contentInset = UIEdgeInsets(top: 0, left: 21.5, bottom: 0, right: 21.5) |
| | | collectionView.register(UINib(nibName: "HomeRelaxBanner_2_CCell", bundle: nil), forCellWithReuseIdentifier: "_HomeRelaxBanner_2_CCell") |
| | | |
| | | cons_hei.constant = ceil(5.0 / 2.0) * CellH + floor(5.0 / 2.0) * 13.5 |
| | | cons_hei.constant = 0 |
| | | } |
| | | |
| | | func setItems(_ items:[CourseModel]){ |
| | | self.items = items |
| | | cons_hei.constant = ceil(Double(items.count) / 2.0) * CellH + floor(Double(items.count) / 2.0) * 13.5 |
| | | collectionView.reloadData() |
| | | } |
| | | } |
| | | |
| | | extension CourseDetail_3_TCell:UICollectionViewDelegate & UICollectionViewDataSource{ |
| | | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
| | | return 5 |
| | | return items.count |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_HomeRelaxBanner_2_CCell", for: indexPath) as! HomeRelaxBanner_2_CCell |
| | | cell.setCourseModel(items[indexPath.row]) |
| | | cell.backgroundColor = .jq_randomColor |
| | | return cell |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let model = items[indexPath.row] |
| | | let vc = CourseDetialVC(courseId: model.id) |
| | | JQ_currentViewController().jq_push(vc: vc) |
| | | } |
| | | } |
| | | |
| | | extension CourseDetail_3_TCell:UICollectionViewDelegateFlowLayout{ |
| | |
| | | private var currentShowIndex:IndexPath = IndexPath(row: 0, section: 0) |
| | | var isAnimationing = false |
| | | private var style:CourseDetialStyle = .style1 |
| | | private var courseId:Int! |
| | | private var courseDetailModel:CourseModel? |
| | | |
| | | private var section0TCell:CourseDetail_1_TCell! |
| | | private var section1TCell:CourseDetail_2_TCell! |
| | | private var section2TCell:CourseDetail_3_TCell! |
| | | private var section0Height:Double = 0 |
| | | |
| | | private(set) var pageMenu:SPPageMenu = { |
| | | let pageMenu = SPPageMenu(frame: .zero, trackerStyle: .line) |
| | |
| | | navigationController?.navigationBar.standardAppearance.backgroundColor = .clear |
| | | } |
| | | |
| | | init(courseSytle:CourseDetialStyle) { |
| | | init(courseId:Int) { |
| | | super.init(nibName: nil, bundle: nil) |
| | | self.style = courseSytle |
| | | self.courseId = courseId |
| | | } |
| | | |
| | | required init?(coder: NSCoder) { |
| | |
| | | super.viewDidLoad() |
| | | title = "课程详情" |
| | | |
| | | if style == .style1{ |
| | | pageMenu.setItems(["简介","章节","相关推荐"], selectedItemIndex: 0) |
| | | Services.getCourseDetail(courseId: courseId).subscribe(onNext: {data in |
| | | if let m = data.data{ |
| | | self.courseDetailModel = m |
| | | self.headerView.setCourseModel(m) |
| | | self.section1TCell.setItems(m.list) |
| | | self.section2TCell.setItems(m.list2) |
| | | |
| | | if m.detailUrl.jq_isVideo{ |
| | | self.style = .style1 |
| | | self.pageMenu.setItems(["简介","章节","相关推荐"], selectedItemIndex: 0) |
| | | }else{ |
| | | pageMenu.setItems(["简介"], selectedItemIndex: 0) |
| | | self.style = .style2 |
| | | self.pageMenu.setItems(["简介"], selectedItemIndex: 0) |
| | | } |
| | | self.tableView?.reloadData() |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | tableView!.register(UINib(nibName: "CourseDetail_1_TCell", bundle: nil), forCellReuseIdentifier: "_CourseDetail_1_TCell") |
| | | tableView!.register(UINib(nibName: "CourseDetail_2_TCell", bundle: nil), forCellReuseIdentifier: "_CourseDetail_2_TCell") |
| | | tableView!.register(UINib(nibName: "CourseDetail_3_TCell", bundle: nil), forCellReuseIdentifier: "_CourseDetail_3_TCell") |
| | | |
| | | section0TCell = (tableView!.dequeueReusableCell(withIdentifier: "_CourseDetail_1_TCell") as! CourseDetail_1_TCell) |
| | | section1TCell = (tableView!.dequeueReusableCell(withIdentifier: "_CourseDetail_2_TCell") as! CourseDetail_2_TCell) |
| | | section2TCell = (tableView!.dequeueReusableCell(withIdentifier: "_CourseDetail_3_TCell") as! CourseDetail_3_TCell) |
| | | |
| | | view.addSubview(tableView!) |
| | | tableView!.snp.makeConstraints { make in |
| | |
| | | } |
| | | } |
| | | |
| | | @objc func handleAction(_ btn:QMUIButton){ |
| | | override func setRx() { |
| | | section0TCell.webView.scrollView.rx.observe(CGSize.self, "contentSize").map { (size) -> CGFloat? in |
| | | if let size = size{ |
| | | return size.height |
| | | } |
| | | return nil |
| | | }.subscribe(onNext: { [unowned self](height) in |
| | | if let height = height{ |
| | | if height > section0Height{ |
| | | self.section0Height = height |
| | | self.tableView?.reloadData() |
| | | } |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | @objc func handleAction(_ btn:QMUIButton){ |
| | | if let items = courseDetailModel?.list{ |
| | | let vc = CourseDetialVideoVC(items: items, selectIndex: IndexPath(row: 0, section: 0)) |
| | | push(vc: vc) |
| | | } |
| | | } |
| | | |
| | | @objc func sendGift(_ btn:QMUIButton){ |
| | | CourseSendGiftView.show { |
| | | if let price = courseDetailModel?.generalPrice{ |
| | | CourseSendGiftView.show(price:price) { |
| | | let vc = PaymentOrderVC() |
| | | self.push(vc: vc) |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { |
| | | if indexPath.row == 0 || indexPath.row == 1{ |
| | | return UITableView.automaticDimension |
| | | } |
| | | return 770.736 |
| | | } |
| | | |
| | | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| | | |
| | | if indexPath.row == 0{ |
| | | let cell = tableView.dequeueReusableCell(withIdentifier: "_CourseDetail_1_TCell") as! CourseDetail_1_TCell |
| | | cell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return cell |
| | | section0TCell.webView.loadHTMLString(courseDetailModel?.briefIntroduction.jq_wrapHtml() ?? "", baseURL: nil) |
| | | section0TCell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return section0TCell |
| | | } |
| | | |
| | | if indexPath.row == 1{ |
| | | let cell = tableView.dequeueReusableCell(withIdentifier: "_CourseDetail_2_TCell") as! CourseDetail_2_TCell |
| | | cell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return cell |
| | | section1TCell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return section1TCell |
| | | } |
| | | |
| | | if indexPath.row == 2{ |
| | | let cell = tableView.dequeueReusableCell(withIdentifier: "_CourseDetail_3_TCell") as! CourseDetail_3_TCell |
| | | cell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return cell |
| | | section2TCell.backgroundColor = UIColor(hexString: "#f6f6f6") |
| | | return section2TCell |
| | | } |
| | | |
| | | var cell = tableView.dequeueReusableCell(withIdentifier: "cell") |
| | |
| | | @IBOutlet weak var tableView: UITableView! |
| | | |
| | | private var videoView:VideoView? |
| | | private var items = [CourseItemModel]() |
| | | private var selectIndex:IndexPath! |
| | | |
| | | override func viewWillAppear(_ animated: Bool) { |
| | | super.viewWillAppear(animated) |
| | | (navigationItem.leftBarButtonItem?.customView as? UIButton)?.setImage(UIImage(named: "btn_back")?.withTintColor(.white), for: .normal) |
| | | } |
| | | |
| | | required init(items:[CourseItemModel],selectIndex:IndexPath) { |
| | | super.init(nibName: nil, bundle: nil) |
| | | self.items = items |
| | | self.selectIndex = selectIndex |
| | | } |
| | | |
| | | required init?(coder: NSCoder) { |
| | | fatalError("init(coder:) has not been implemented") |
| | | } |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | title = "课程详情" |
| | | |
| | | videoView = VideoView(url: "http://vfx.mtime.cn/Video/2021/07/10/mp4/210710094507540173.mp4") |
| | | videoView = VideoView(url: items[selectIndex.row].videoUrl) |
| | | view_bg_video.addSubview(videoView!) |
| | | videoView!.snp.makeConstraints { make in |
| | | make.edges.equalToSuperview() |
| | |
| | | } |
| | | |
| | | extension CourseDetialVideoVC:UITableViewDataSource{ |
| | | |
| | | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| | | return 10 |
| | | return items.count |
| | | } |
| | | |
| | | func numberOfSections(in tableView: UITableView) -> Int { |
| | |
| | | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| | | |
| | | let cell = tableView.dequeueReusableCell(withIdentifier: "_CourseDetail_2_Inner_TCell") as! CourseDetail_2_Inner_TCell |
| | | |
| | | cell.setModel(items[indexPath.row], index: indexPath) |
| | | |
| | | if items[indexPath.row].isOver == .yes{ |
| | | cell.btn_study.setTitle("已学习", for: .normal) |
| | | cell.btn_study.backgroundColor = UIColor(hexString: "#CDCDCD") |
| | | }else{ |
| | | cell.btn_study.setTitle("去学习", for: .normal) |
| | | cell.btn_study.backgroundColor = UIColor(hexString: "#8AAE65") |
| | | } |
| | | |
| | | if indexPath.row == selectIndex.row{ |
| | | cell.btn_study.setTitle("正在学习", for: .normal) |
| | | cell.btn_study.backgroundColor = UIColor(hexString: "#E3B25C") |
| | | } |
| | | return cell |
| | | } |
| | | } |
| | | |
| | | extension CourseDetialVideoVC:UITableViewDelegate{ |
| | | |
| | | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { |
| | | |
| | | if selectIndex == indexPath{return} |
| | | |
| | | selectIndex = indexPath |
| | | videoView?.updateVideoUrl(items[indexPath.row].videoUrl) |
| | | tableView.reloadData() |
| | | } |
| | | } |
| | |
| | | private var collectionView:UICollectionView! |
| | | private var titleItems = [TitleItem]() |
| | | private var selectIndex:Int = 0 |
| | | private var viewModel = CourseVCOfficalViewModel() |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | title = "全部课程" |
| | | |
| | | titleItems.append(TitleItem(title: "会员专区")) |
| | | titleItems.append(TitleItem(title: "免费专区")) |
| | | titleItems.append(TitleItem(title: "付费专区")) |
| | | titleItems.append(TitleItem(title: "线下课程")) |
| | | titleItems.append(TitleItem(title: "水晶疗愈")) |
| | | viewModel.cateId.accept(titleItems[selectIndex].id) |
| | | viewModel.configure(collectionView) |
| | | viewModel.beginRefresh() |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | collectionView.delegate = self |
| | | collectionView.dataSource = self |
| | | collectionView.showsVerticalScrollIndicator = false |
| | | collectionView.register(UINib(nibName: "HomeRelaxBanner_2_CCell", bundle: nil), forCellWithReuseIdentifier: "_HomeRelaxBanner_2_CCell") |
| | | collectionView.contentInset = UIEdgeInsets(top: 34, left: 21, bottom: 0, right: 21) |
| | | collectionView.register(UINib(nibName: "HomeRelaxBanner_2_1_CCell", bundle: nil), forCellWithReuseIdentifier: "_HomeRelaxBanner_2_1_CCell") |
| | | collectionView.contentInset = UIEdgeInsets(top: 0, left: 21, bottom: 0, right: 21) |
| | | view.addSubview(collectionView) |
| | | collectionView.snp.makeConstraints { make in |
| | | make.left.equalTo(tableView.snp.right) |
| | | make.right.equalToSuperview() |
| | | make.top.bottom.equalTo(tableView) |
| | | make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(18) |
| | | make.bottom.equalTo(tableView) |
| | | } |
| | | } |
| | | |
| | | func setTitleItem(_ items:[TitleItem],defaultSelectIndex:Int = 0){ |
| | | self.selectIndex = defaultSelectIndex |
| | | self.titleItems = items |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { |
| | | selectIndex = indexPath.row |
| | | viewModel.cateId.accept(titleItems[selectIndex].id) |
| | | viewModel.beginRefresh() |
| | | tableView.reloadData() |
| | | } |
| | | |
| | |
| | | extension CourseMenuVC:UICollectionViewDelegate & UICollectionViewDataSource{ |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | if indexPath.row == 0{ |
| | | let vc = CourseDetialVC(courseSytle: .style1) |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let vc = CourseDetialVC(courseId: model.id) |
| | | push(vc: vc) |
| | | }else{ |
| | | let vc = CourseDetialVC(courseSytle: .style2) |
| | | push(vc: vc) |
| | | } |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_HomeRelaxBanner_2_CCell", for: indexPath) as! HomeRelaxBanner_2_CCell |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_HomeRelaxBanner_2_1_CCell", for: indexPath) as! HomeRelaxBanner_2_1_CCell |
| | | cell.backgroundColor = .jq_randomColor |
| | | cell.setCourseModel(model) |
| | | return cell |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
| | | return 2 |
| | | return viewModel.dataSource.value?.list.count ?? 0 |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | import UIKit |
| | | import JQTools |
| | | import RxRelay |
| | | import RxSwift |
| | | |
| | | class CourseVCOfficalViewModel:RefreshInnerModel<CourseModel>{ |
| | | var cateId = BehaviorRelay<Int?>(value: nil) |
| | | override func api() -> (Observable<BaseResponse<BaseResponseList<CourseModel>>>)? { |
| | | return Services.getCoursePageList(page: page) |
| | | } |
| | | } |
| | | |
| | | class CourseVCOfficalCommentVC: BaseVC { |
| | | |
| | | private var collectionView:UICollectionView! |
| | | private var titleItems = [TitleItem]() |
| | | private var subTitleItems = [TitleItem]() |
| | | private var bannerModels = [CommonBannerModel]() |
| | | private var viewModel = CourseVCOfficalViewModel() |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | |
| | | titleItems.append(TitleItem(title: "新手冥想指南", subTitle: "Meditation guide")) |
| | | titleItems.append(TitleItem(title: "推荐课程", subTitle: "与内心的宁静与喜悦入睡")) |
| | | |
| | | subTitleItems.append(TitleItem(title: "会员专区",coverImage: "course_1")) |
| | | subTitleItems.append(TitleItem(title: "免费专区",coverImage: "course_2")) |
| | | subTitleItems.append(TitleItem(title: "付费专区",coverImage: "course_3")) |
| | | subTitleItems.append(TitleItem(title: "线下课程",coverImage: "course_4")) |
| | | subTitleItems.append(TitleItem(title: "疗愈",coverImage: "course_5")) |
| | | subTitleItems.append(TitleItem(title: "关系",coverImage: "course_6")) |
| | | subTitleItems.append(TitleItem(title: "财富",coverImage: "course_7")) |
| | | subTitleItems.append(TitleItem(title: "分类",coverImage: "course_8")) |
| | | Services.getCourseCategory().subscribe(onNext: {data in |
| | | for v in data.data ?? []{ |
| | | self.subTitleItems.append(TitleItem(id:v.id,title: v.name,coverImage: v.imageUrl)) |
| | | } |
| | | self.collectionView.reloadData() |
| | | }).disposed(by: disposeBag) |
| | | |
| | | Services.getCourseBannerList().subscribe(onNext: {data in |
| | | for (index,v) in (data.data ?? []).enumerated(){ |
| | | self.bannerModels.append(CommonBannerModel(index: index, id: v.id, name: v.name, resource:v.imageUrl, mediaType: .imageUrl)) |
| | | } |
| | | self.collectionView.reloadData() |
| | | }).disposed(by: disposeBag) |
| | | |
| | | viewModel.configure(collectionView) |
| | | viewModel.beginRefresh() |
| | | |
| | | } |
| | | |
| | |
| | | extension CourseVCOfficalCommentVC:UICollectionViewDelegate & UICollectionViewDataSource{ |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let vc = CourseDetialVC(courseSytle: .style1) |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let vc = CourseDetialVC(courseId: model.id) |
| | | JQ_currentViewController().jq_push(vc: vc) |
| | | } |
| | | |
| | |
| | | let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! HomeHeaderView_1 |
| | | let m = titleItems[indexPath.section] |
| | | headerView.setTitle(m.title, subTitle: m.subTitle) |
| | | headerView.btn_more.isHidden = true |
| | | return headerView |
| | | } |
| | | return UICollectionReusableView() |
| | |
| | | if indexPath.section == 0{ |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_CourseOfficalCommendTopCCell", for: indexPath) as! CourseOfficalCommendTopCCell |
| | | cell.setTitles(subTitleItems) |
| | | cell.setBanners(bannerModels) |
| | | cell.clickAtClouse { index in |
| | | let vc = CourseMenuVC() |
| | | vc.setTitleItem(self.subTitleItems,defaultSelectIndex: index) |
| | | vc.hidesBottomBarWhenPushed = true |
| | | JQ_currentNavigationController().pushViewController(vc, animated: true) |
| | | } |
| | | return cell |
| | | } |
| | | |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_HomeRelaxBanner_2_CCell", for: indexPath) as! HomeRelaxBanner_2_CCell |
| | | cell.backgroundColor = .jq_randomColor |
| | | cell.setCourseModel(model) |
| | | return cell |
| | | } |
| | | |
| | |
| | | if section == 0{ |
| | | return 1 |
| | | } |
| | | return 20 |
| | | return viewModel.dataSource.value?.list.count ?? 0 |
| | | } |
| | | |
| | | func numberOfSections(in collectionView: UICollectionView) -> Int { |
| | |
| | | |
| | | class CourseDetailHeaderView: UIView,JQNibView{ |
| | | |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_teacher: UILabel! |
| | | @IBOutlet weak var label_studyNum: UILabel! |
| | | @IBOutlet weak var image_cover: UIImageView! |
| | | @IBOutlet weak var icon: UIImageView! |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | } |
| | | |
| | | func setCourseModel(_ model:CourseModel){ |
| | | label_title.text = model.courseTitle |
| | | label_teacher.text = "导师 \(model.tutor)" |
| | | label_studyNum.text = "\(model.count)人已加入学习" |
| | | image_cover.sd_setImage(with: URL(string: model.coverUrl)) |
| | | } |
| | | } |
| | |
| | | <color key="textColor" red="0.27058823529411763" green="0.27058823529411763" blue="0.27058823529411763" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_use_small" translatesAutoresizingMaskIntoConstraints="NO" id="QZb-OD-zkm"> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_user_1" translatesAutoresizingMaskIntoConstraints="NO" id="QZb-OD-zkm"> |
| | | <rect key="frame" x="20.666666666666668" y="63.333333333333364" width="10" height="10.666666666666664"/> |
| | | <constraints> |
| | | <constraint firstAttribute="width" constant="10" id="Oim-LL-682"/> |
| | | <constraint firstAttribute="height" constant="10.5" id="UW0-AR-Xhg"/> |
| | | </constraints> |
| | | </imageView> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="101人已加入学习" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0I9-CV-ojJ"> |
| | | <rect key="frame" x="35.666666666666664" y="62.666666666666686" width="78.666666666666686" height="12"/> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0人已加入学习" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0I9-CV-ojJ"> |
| | | <rect key="frame" x="35.666666666666657" y="62.666666666666686" width="69" height="12"/> |
| | | <fontDescription key="fontDescription" type="system" pointSize="10"/> |
| | | <color key="textColor" red="0.80392156862745101" green="0.80392156862745101" blue="0.80392156862745101" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
| | | <nil key="highlightedColor"/> |
| | |
| | | <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> |
| | | <connections> |
| | | <outlet property="icon" destination="QZb-OD-zkm" id="C8w-ga-BSc"/> |
| | | <outlet property="image_cover" destination="3FR-3L-eGU" id="zat-PK-Mtx"/> |
| | | <outlet property="label_studyNum" destination="0I9-CV-ojJ" id="Xu3-ID-OsO"/> |
| | | <outlet property="label_teacher" destination="DbG-SW-sik" id="BfM-77-ZSS"/> |
| | | <outlet property="label_title" destination="CgZ-Jh-uGm" id="zt0-Fw-MFu"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="59.541984732824424" y="-98.239436619718319"/> |
| | | </view> |
| | | </objects> |
| | | <resources> |
| | | <image name="demo_bg" width="621" height="1064"/> |
| | | <image name="icon_use_small" width="10" height="10.666666984558105"/> |
| | | <image name="icon_user_1" width="10" height="10.666666984558105"/> |
| | | <systemColor name="systemBackgroundColor"> |
| | | <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| | | </systemColor> |
| | |
| | | @IBOutlet weak var label_price: UILabel! |
| | | @IBOutlet weak var cons_bottom: NSLayoutConstraint! |
| | | |
| | | private var price:Double = 0 |
| | | |
| | | private var clouse:(()->Void)? |
| | | |
| | | override func awakeFromNib() { |
| | |
| | | layoutIfNeeded() |
| | | } |
| | | |
| | | static func show(_ clouse:@escaping()->Void){ |
| | | static func show(price:Double,clouse:@escaping()->Void){ |
| | | let view = CourseSendGiftView.jq_loadNibView() |
| | | view.label_price.text = "¥\(price.jq_formatFloat)" |
| | | sceneDelegate?.window?.addSubview(view) |
| | | view.clouse = clouse |
| | | view.cons_bottom.constant = 0 |
| | |
| | | class HomeRelaxBanner_2_1_CCell: UICollectionViewCell { |
| | | |
| | | @IBOutlet weak var view_text_bg: UIView! |
| | | @IBOutlet weak var img_cover: UIImageView! |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_subTitle: UILabel! |
| | | @IBOutlet weak var label_num: UILabel! |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | // Initialization code |
| | |
| | | visualEffectView.layer.masksToBounds = true |
| | | } |
| | | |
| | | func setCourseModel(_ model:CourseModel){ |
| | | img_cover.sd_setImage(with: URL(string: model.coverUrl)) |
| | | label_title.text = model.courseTitle |
| | | label_subTitle.text = model.briefIntroduction |
| | | label_num.text = "\(model.count)" |
| | | // img_vip.isHidden = model.chargeType != .vipFree |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | <rect key="frame" x="0.0" y="146" width="160" height="50"/> |
| | | <subviews> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="--" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6md-5M-Pce"> |
| | | <rect key="frame" x="20" y="5.9999999999999991" width="13" height="10.666666666666664"/> |
| | | <rect key="frame" x="20" y="6" width="130" height="20"/> |
| | | <constraints> |
| | | <constraint firstAttribute="height" constant="10.57" id="b4q-6z-Gno"/> |
| | | <constraint firstAttribute="height" constant="20" id="b4q-6z-Gno"/> |
| | | </constraints> |
| | | <fontDescription key="fontDescription" type="system" pointSize="14"/> |
| | | <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="--" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EeQ-hi-VEz"> |
| | | <rect key="frame" x="20" y="22" width="89" height="10.666666666666664"/> |
| | | <rect key="frame" x="20" y="31.666666666666654" width="89" height="10.333333333333332"/> |
| | | <constraints> |
| | | <constraint firstAttribute="height" constant="10.57" id="ma3-Tn-T3O"/> |
| | | </constraints> |
| | |
| | | <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_use_small" translatesAutoresizingMaskIntoConstraints="NO" id="Y7f-aB-0R5"> |
| | | <rect key="frame" x="119" y="22" width="10" height="10.666666666666664"/> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_use_small_s" translatesAutoresizingMaskIntoConstraints="NO" id="Y7f-aB-0R5"> |
| | | <rect key="frame" x="119" y="32.333333333333343" width="9" height="9"/> |
| | | <constraints> |
| | | <constraint firstAttribute="width" constant="10" id="3we-wk-eag"/> |
| | | <constraint firstAttribute="height" constant="10.5" id="T4v-eM-jjx"/> |
| | | <constraint firstAttribute="width" constant="9" id="3we-wk-eag"/> |
| | | <constraint firstAttribute="height" constant="9" id="T4v-eM-jjx"/> |
| | | </constraints> |
| | | </imageView> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" text="0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wxc-EY-qwU"> |
| | | <rect key="frame" x="131" y="22.666666666666657" width="6" height="9.6666666666666643"/> |
| | | <rect key="frame" x="130" y="32" width="28" height="9.6666666666666643"/> |
| | | <fontDescription key="fontDescription" type="system" pointSize="8"/> |
| | | <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | </subviews> |
| | | <constraints> |
| | | <constraint firstAttribute="trailing" secondItem="Wxc-EY-qwU" secondAttribute="trailing" constant="23" id="1lo-tj-hSD"/> |
| | | <constraint firstItem="Y7f-aB-0R5" firstAttribute="leading" secondItem="EeQ-hi-VEz" secondAttribute="trailing" constant="10" id="6Ta-sO-wtF"/> |
| | | <constraint firstItem="Wxc-EY-qwU" firstAttribute="leading" secondItem="Y7f-aB-0R5" secondAttribute="trailing" constant="2" id="HlL-mC-Zj3"/> |
| | | <constraint firstItem="6md-5M-Pce" firstAttribute="top" secondItem="nyj-tV-0t4" secondAttribute="top" constant="6" id="KXL-JH-Ymd"/> |
| | | <constraint firstAttribute="trailing" secondItem="Y7f-aB-0R5" secondAttribute="trailing" constant="32" id="M2f-Kb-UTr"/> |
| | | <constraint firstAttribute="trailing" secondItem="6md-5M-Pce" secondAttribute="trailing" constant="10" id="OYv-3V-kDP"/> |
| | | <constraint firstItem="Wxc-EY-qwU" firstAttribute="centerY" secondItem="EeQ-hi-VEz" secondAttribute="centerY" id="Pau-3T-hlR"/> |
| | | <constraint firstItem="Y7f-aB-0R5" firstAttribute="centerY" secondItem="EeQ-hi-VEz" secondAttribute="centerY" id="Te3-Aw-qID"/> |
| | | <constraint firstItem="EeQ-hi-VEz" firstAttribute="top" secondItem="6md-5M-Pce" secondAttribute="bottom" constant="5.5" id="TwN-Dx-nby"/> |
| | | <constraint firstItem="6md-5M-Pce" firstAttribute="leading" secondItem="nyj-tV-0t4" secondAttribute="leading" constant="20" id="UZ1-SQ-4vN"/> |
| | | <constraint firstAttribute="trailing" secondItem="Wxc-EY-qwU" secondAttribute="trailing" constant="2" id="Ys5-cn-zeq"/> |
| | | <constraint firstItem="Wxc-EY-qwU" firstAttribute="leading" secondItem="Y7f-aB-0R5" secondAttribute="trailing" constant="2" id="agG-lh-R3G"/> |
| | | <constraint firstItem="EeQ-hi-VEz" firstAttribute="leading" secondItem="6md-5M-Pce" secondAttribute="leading" id="qrg-fN-GGz"/> |
| | | <constraint firstAttribute="height" constant="50" id="tml-hU-Ojf"/> |
| | | </constraints> |
| | |
| | | </constraints> |
| | | <size key="customSize" width="160" height="196"/> |
| | | <connections> |
| | | <outlet property="img_cover" destination="VwD-36-ent" id="93Z-v8-bjM"/> |
| | | <outlet property="label_num" destination="Wxc-EY-qwU" id="zxI-Y4-tTv"/> |
| | | <outlet property="label_subTitle" destination="EeQ-hi-VEz" id="U39-7s-GGD"/> |
| | | <outlet property="label_title" destination="6md-5M-Pce" id="j50-e3-otB"/> |
| | | <outlet property="view_text_bg" destination="nyj-tV-0t4" id="Lef-w5-fi6"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="164.8854961832061" y="71.83098591549296"/> |
| | |
| | | </objects> |
| | | <resources> |
| | | <image name="icon_freee" width="50" height="18"/> |
| | | <image name="icon_use_small" width="10" height="10.666666984558105"/> |
| | | <image name="icon_use_small_s" width="9" height="9"/> |
| | | </resources> |
| | | </document> |
| | |
| | | class HomeRelaxBanner_2_CCell: UICollectionViewCell { |
| | | |
| | | @IBOutlet weak var view_text_bg: UIView! |
| | | |
| | | @IBOutlet weak var img_tipPeople: UIImageView! |
| | | @IBOutlet weak var img_vip: UIImageView! |
| | | @IBOutlet weak var img_cover: UIImageView! |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_subTitle: UILabel! |
| | | @IBOutlet weak var label_num: UILabel! |
| | | @IBOutlet weak var cons_maxSubTitle: NSLayoutConstraint! |
| | | |
| | | private var meditationModel:MeditationModel? |
| | | override func awakeFromNib() { |
| | |
| | | label_num.text = "\(model.realLearnedNum)" |
| | | img_vip.isHidden = model.chargeType != .vipFree |
| | | } |
| | | |
| | | func setCourseModel(_ model:CourseModel){ |
| | | img_cover.sd_setImage(with: URL(string: model.coverUrl)) |
| | | label_title.text = model.courseTitle |
| | | label_subTitle.text = model.briefIntroduction |
| | | label_num.text = "\(model.count)" |
| | | } |
| | | } |
| | |
| | | <rect key="frame" x="0.0" y="236" width="178" height="65"/> |
| | | <subviews> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="--" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="byc-Ar-sJL"> |
| | | <rect key="frame" x="20" y="17" width="148" height="10.666666666666664"/> |
| | | <constraints> |
| | | <constraint firstAttribute="height" constant="10.57" id="ZW4-0S-dSX"/> |
| | | </constraints> |
| | | <rect key="frame" x="20" y="17" width="148" height="12"/> |
| | | <fontDescription key="fontDescription" type="system" pointSize="10"/> |
| | | <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="--" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z5u-My-91l"> |
| | | <rect key="frame" x="20" y="33" width="10.666666666666664" height="10.666666666666664"/> |
| | | <rect key="frame" x="20" y="34.666666666666686" width="10.666666666666664" height="10.333333333333336"/> |
| | | <constraints> |
| | | <constraint firstAttribute="width" relation="lessThanOrEqual" constant="100" id="Fam-ne-FM1"/> |
| | | <constraint firstAttribute="height" constant="10.57" id="epJ-Xi-ukn"/> |
| | |
| | | <nil key="highlightedColor"/> |
| | | </label> |
| | | <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_use_small" translatesAutoresizingMaskIntoConstraints="NO" id="hE1-AV-t8f"> |
| | | <rect key="frame" x="136" y="33" width="10" height="10.666666666666664"/> |
| | | <constraints> |
| | | <constraint firstAttribute="height" constant="10.5" id="429-dC-RSu"/> |
| | | <constraint firstAttribute="width" constant="10" id="AvO-zD-TXx"/> |
| | | </constraints> |
| | | <rect key="frame" x="136" y="34.666666666666686" width="10" height="10.333333333333336"/> |
| | | </imageView> |
| | | <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="0" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JaY-19-IAL"> |
| | | <rect key="frame" x="148" y="31.666666666666682" width="7" height="13.333333333333332"/> |
| | | <rect key="frame" x="148" y="33" width="7" height="13.333333333333336"/> |
| | | <fontDescription key="fontDescription" type="system" pointSize="11"/> |
| | | <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> |
| | | <nil key="highlightedColor"/> |
| | |
| | | </constraints> |
| | | <size key="customSize" width="178" height="301"/> |
| | | <connections> |
| | | <outlet property="cons_maxSubTitle" destination="Fam-ne-FM1" id="L5X-GP-uev"/> |
| | | <outlet property="img_cover" destination="cCC-0f-GSV" id="85x-ue-edy"/> |
| | | <outlet property="img_tipPeople" destination="hE1-AV-t8f" id="ObN-5h-jnl"/> |
| | | <outlet property="img_vip" destination="yBF-fO-fwz" id="icq-m9-dx3"/> |
| | | <outlet property="label_num" destination="JaY-19-IAL" id="2RU-X7-0wb"/> |
| | | <outlet property="label_subTitle" destination="z5u-My-91l" id="j4L-bl-DhS"/> |
| | |
| | | let SetBGMSuccess_Noti = Notification.Name.init("SetBGMSuccess_Noti") |
| | | |
| | | struct TitleItem{ |
| | | var id = 0 |
| | | var title = "" |
| | | var subTitle = "" |
| | | var hasMore:Bool = false |
| | |
| | | |
| | | if let setting = UserDefaultSettingViewModel.getSetting(){ |
| | | if let audioFile = setting.bgm?.audioFile{ |
| | | AudioPlayer.getSharedInstance().playBGMAt(audioFile) |
| | | AudioPlayer.getSharedInstance().playSceneAt(audioFile) |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | override func viewDidDisappear(_ animated: Bool) { |
| | | super.viewDidDisappear(animated) |
| | | audioPlayer.playBGM() |
| | | audioPlayer.playScene() |
| | | } |
| | | |
| | | override func viewDidAppear(_ animated: Bool) { |
| | | super.viewDidAppear(animated) |
| | | audioPlayer.pauseBGM() |
| | | audioPlayer.pauseScene() |
| | | } |
| | | |
| | | override func viewDidLoad() { |
| | |
| | | settingModel?.bgm = items[index.row] |
| | | settingModel?.volume = Double(slider_voice.value) |
| | | UserDefaultSettingViewModel.saveSetting(settingModel!) |
| | | audioPlayer.playBGMAt(items[index.row].audioFile) |
| | | audioPlayer.playSceneAt(items[index.row].audioFile) |
| | | alertSuccess(msg: "设置成功") |
| | | NotificationCenter.default.post(name: SetBGMSuccess_Noti, object: items[index.row]) |
| | | } |
| | |
| | | import UIKit |
| | | import JQTools |
| | | |
| | | class HomeItemDetailVC: BaseVC { |
| | | class HomeItemDetailVC: BaseVC,PayMusicDelegate{ |
| | | |
| | | @IBOutlet weak var slider_voice: UISlider! |
| | | @IBOutlet weak var view_function: UIView! |
| | |
| | | @IBOutlet weak var view_playState: UIView! |
| | | @IBOutlet weak var label_playState: UILabel! |
| | | @IBOutlet weak var btn_playMode: UIButton! |
| | | @IBOutlet weak var label_currentTime: UILabel! |
| | | @IBOutlet weak var label_totalTime: UILabel! |
| | | |
| | | private var collect_bitem:UIBarButtonItem! |
| | | |
| | | private var audioPlayer:AudioPlayer = AudioPlayer.getSharedInstance() |
| | | private var id:Int! |
| | | private var settingViewModel = UserDefaultSettingViewModel.getSetting() |
| | | private var model:MeditationModel?{ |
| | |
| | | |
| | | btn_playMode.isSelected = settingViewModel?.playModel == .singleLoop |
| | | |
| | | if AudioPlayer.getSharedInstance().times.value != nil{ |
| | | if audioPlayer.times.value != nil{ |
| | | startMiniRunloop() |
| | | }else{ |
| | | endMiniRunloop() |
| | | } |
| | | |
| | | Services.getMeditationDetail(id: id).subscribe(onNext: {data in |
| | | Services.getMeditationDetail(id: id).subscribe(onNext: {[unowned self] data in |
| | | self.model = data.data |
| | | self.collect_bitem.image = data.data?.favorite == .yes ? UIImage(named: "btn_collect_s"):UIImage(named: "btn_collect_1") |
| | | self.collect_bitem.tintColor = data.data?.favorite == .yes ? UIColor(hexString: "fe5b60"):.white |
| | | |
| | | if audioPlayer.meditationModel?.id == data.data?.id{ |
| | | audioPlayer.delegate = self |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | override func setRx() { |
| | | AudioPlayer.getSharedInstance().times.subscribe {[weak self] t in |
| | | audioPlayer.times.subscribe {[weak self] t in |
| | | guard let weakSelf = self else { return } |
| | | guard let time = t,time > 0 else {weakSelf.endMiniRunloop();return} |
| | | let turple = Date.jq_formateToTime_tuple(time) |
| | |
| | | super.viewWillAppear(animated) |
| | | (navigationItem.leftBarButtonItem?.customView as? UIButton)?.setImage(UIImage(named: "btn_back")?.withTintColor(.white), for: .normal) |
| | | } |
| | | |
| | | |
| | | private func startMiniRunloop(){ |
| | | // 创建旋转动画 |
| | |
| | | |
| | | @IBAction func playAction(_ sender: UIButton) { |
| | | |
| | | // showHUD("准备播放") |
| | | |
| | | if var m = model{ |
| | | m.backgroundUrl = "https://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3,https://www.cambridgeenglish.org/images/153149-movers-sample-listening-test-vol2.mp3" |
| | | |
| | | m.tutorAudioUrl = "https://downsc.chinaz.net/files/download/sound1/201206/1638.mp3" |
| | | audioPlayer.playBGMAt(firstPlayIndex: 0, model: m, delegate: self) |
| | | } |
| | | } |
| | | |
| | | @IBAction func timeAction(_ sender: UIButton) { |
| | | CountdownChooseListView.show {[weak self] times in |
| | | guard let weakSelf = self else { return } |
| | | AudioPlayer.getSharedInstance().setTimer(times: times * 60) |
| | | weakSelf.audioPlayer.setTimer(times: times * 60) |
| | | |
| | | // if times <= 0{ |
| | | // weakSelf.timer = nil |
| | |
| | | vc.modalPresentationStyle = .custom |
| | | present(vc, animated: true) |
| | | } |
| | | |
| | | @IBAction func beLikeAction(_ sender: UIButton) { |
| | | |
| | | } |
| | | |
| | | |
| | | @IBAction func playWayAction(_ sender: UIButton) { |
| | | UIView.animate(withDuration: 0.5) { |
| | |
| | | override var preferredStatusBarStyle: UIStatusBarStyle{ |
| | | return .lightContent |
| | | } |
| | | |
| | | func playState(_ state:PlayMusicState){ |
| | | print("music:当前状态:\(state)") |
| | | |
| | | guard audioPlayer.meditationModel?.id == model?.id else {return} |
| | | |
| | | switch state { |
| | | case .playing: |
| | | PayMusicVC.show(model: model!) |
| | | if btn_play.isHidden == false {btn_play.isHidden = true} |
| | | hiddenHUD() |
| | | case .paurse: |
| | | btn_play.isHidden = false |
| | | case .end:break |
| | | case .next:break |
| | | } |
| | | } |
| | | |
| | | func playListen(currentInterval:TimeInterval,totalInterval:TimeInterval){ |
| | | guard audioPlayer.meditationModel?.id == model?.id else {return} |
| | | guard !totalInterval.isNaN else {return} |
| | | guard !currentInterval.isNaN else {return} |
| | | |
| | | let v = currentInterval / totalInterval * 100 |
| | | print("music:当前时间:\(currentInterval) ---- \(totalInterval) -- \(v)%") |
| | | self.slider_voice.value = Float(v) |
| | | let current = Date.jq_formateToTime_tuple(Int(currentInterval)) |
| | | label_currentTime.text = String(format: "%02ld:%02ld", current.hour * 60 + current.minute,current.second) |
| | | |
| | | let total = Date.jq_formateToTime_tuple(Int(totalInterval)) |
| | | label_totalTime.text = String(format: "%02ld:%02ld", total.hour * 60 + total.minute,total.second) |
| | | } |
| | | } |
| | |
| | | <outlet property="img_countdonw" destination="gxd-0O-eUD" id="rzB-8i-qOk"/> |
| | | <outlet property="label_commentNum" destination="IkS-qR-riK" id="9zO-Xt-6XC"/> |
| | | <outlet property="label_countdown" destination="akf-L4-aPE" id="goO-Gd-TLu"/> |
| | | <outlet property="label_currentTime" destination="zwK-yd-ILF" id="3gm-3s-4eF"/> |
| | | <outlet property="label_playState" destination="4zW-Bq-V7P" id="vSm-Rr-gtL"/> |
| | | <outlet property="label_subtitle" destination="xYv-9B-0fk" id="6WT-eV-ZES"/> |
| | | <outlet property="label_title" destination="0CQ-Qn-OOa" id="3vY-s9-wNj"/> |
| | | <outlet property="label_totalTime" destination="rML-fW-BqA" id="ibD-S5-fds"/> |
| | | <outlet property="slider_voice" destination="P8W-2A-zOo" id="hiF-Hr-YQ0"/> |
| | | <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> |
| | | <outlet property="view_coutdown" destination="V28-7K-Bfm" id="pqU-2r-w5U"/> |
| | |
| | | extension SearchContentVC:UICollectionViewDelegate & UICollectionViewDataSource{ |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let vc = CourseDetialVC(courseSytle: .style1) |
| | | let m = viewModel.dataSource.value!.list[indexPath.row] |
| | | let vc = CourseDetialVC(courseId: m.id) |
| | | JQ_currentViewController().jq_push(vc: vc) |
| | | } |
| | | |
| | |
| | | var replyTime: String = "" |
| | | } |
| | | |
| | | struct PavilionDetailModel:HandyJSON{ |
| | | var address: String = "" |
| | | var addressDetail: String = "" |
| | | var briefIntroduction: String = "" |
| | | var businessHours: String = "" |
| | | var contactNumber: String = "" |
| | | var coverUrl: String = "" |
| | | var createBy: String = "" |
| | | var createTime: String = "" |
| | | var delFlag: Int = 0 |
| | | var detailBannerUrl: String = "" |
| | | var distance: Double = 0 |
| | | var hallName: String = "" |
| | | var id: Int = 0 |
| | | var latitude: Int = 0 |
| | | var listingStatus: Int = 0 |
| | | var longitude: Int = 0 |
| | | var sortNum: Int = 0 |
| | | var updateBy: String = "" |
| | | var updateTime: String = "" |
| | | } |
| | | |
| | | struct CategoryModel:HandyJSON{ |
| | | var id = 0 |
| | | var imageUrl = "" |
| | | var name = "" |
| | | } |
| | | |
| | | struct CourseModel:HandyJSON{ |
| | | var address:String = "" |
| | | var addressDetail:String = "" |
| | | var briefIntroduction:String = "" |
| | | var cateId: Int = 0 |
| | | var chargeType: ChargeType = .free |
| | | var count: Int = 0 |
| | | var courseTitle:String = "" |
| | | var courseType: DeliverStudyType = .online |
| | | var coverUrl:String = "" |
| | | var description:String = "" |
| | | var detailUrl:String = "" |
| | | var generalPrice: Double = 0 |
| | | var headers = [String]() |
| | | var id: Int = 0 |
| | | var iosPrice: Int = 0 |
| | | var isBuy: Int = 0 |
| | | var isVip: Int = 0 |
| | | var latitude: Int = 0 |
| | | var listingStatus: Int = 0 |
| | | var longitude: Int = 0 |
| | | var recommend: Int = 0 |
| | | var sortNum: Int = 0 |
| | | var tutor:String = "" |
| | | var wxQrCode:String = "" |
| | | |
| | | var list2 = [CourseModel]() |
| | | var list = [CourseItemModel]() |
| | | } |
| | | |
| | | struct CourseItemModel:HandyJSON{ |
| | | var chapterTitle:String = "" |
| | | var courseId: Int = 0 |
| | | var createBy:String = "" |
| | | var createTime:String = "" |
| | | var delFlag: Int = 0 |
| | | var duration: Int = 0 |
| | | var id: Int = 0 |
| | | var isOver: ConditionType = .yes |
| | | var minuteLook: Int = 0 |
| | | var realLearnedNum: Int = 0 |
| | | var secondLook: Int = 0 |
| | | var sortNum: Int = 0 |
| | | var updateBy:String = "" |
| | | var updateTime:String = "" |
| | | var videoUrl:String = "" |
| | | var virtualLearnedNum: Int = 0 |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | |
| | | import RxSwift |
| | | import Alamofire |
| | | import JQTools |
| | | import CoreLocation |
| | | |
| | | #if DEBUG |
| | | let All_Url = "https://mock.apipost.net/mock/31b303c60464000" |
| | |
| | | return NetworkRequest.request(params: params, method: .get, progress: false) |
| | | } |
| | | |
| | | /// |
| | | /// 搜索 |
| | | class func search(text:String,page:Int,pageSize:Int = 20)->Observable<BaseResponse<BaseResponseList<MeditationModel>>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/meditation/client/meditation/home/getHotWordList") |
| | |
| | | } |
| | | } |
| | | |
| | | /// 课程 |
| | | extension Services{ |
| | | //课程分类 |
| | | class func getCourseCategory()->Observable<BaseResponse<[CategoryModel]>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/course/client/course/course/getCourseCategoryList") |
| | | .append(key: "apipost_id", value: "2d2eb9d1f993bb") |
| | | return NetworkRequest.request(params: params, method: .get, progress: false) |
| | | } |
| | | |
| | | |
| | | /// 获取banner |
| | | class func getCourseBannerList()->Observable<BaseResponse<[CategoryModel]>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/course/client/course/course/getBannerList") |
| | | .append(key: "apipost_id", value: "2d2eb9d1f993ba") |
| | | return NetworkRequest.request(params: params, method: .get, progress: false) |
| | | } |
| | | |
| | | /// 获取课程列表 |
| | | class func getCoursePageList(page:Int,pageSize:Int = 20,cateId:String? = nil,courseTitle:String? = nil)->Observable<BaseResponse<BaseResponseList<CourseModel>>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/course/client/course/course/getCoursePageList") |
| | | .append(key: "pageCurr", value: page) |
| | | .append(key: "pageSize", value: pageSize) |
| | | .append(key: "cateId", value: cateId) |
| | | .append(key: "courseTitle", value: courseTitle) |
| | | .append(key: "apipost_id", value: "2d6f7f04f99813") |
| | | return NetworkRequest.request(params: params, method: .post, progress: false) |
| | | } |
| | | |
| | | class func getCourseDetail(courseId:Int)->Observable<BaseResponse<CourseModel>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/course/client/course/course/getPayCourseInfoById") |
| | | .append(key: "id", value: courseId) |
| | | .append(key: "apipost_id", value: "2d2eb9d23993bd") |
| | | return NetworkRequest.request(params: params, method: .post, progress: true) |
| | | } |
| | | } |
| | | |
| | | /// 疗愈馆 |
| | | extension Services{ |
| | | /// 疗愈馆 |
| | | class func getPavlilonPage(location:CLLocationCoordinate2D?,search:String? = nil,page:Int,pageSize:Int = 20)->Observable<BaseResponse<BaseResponseList<PavilionDetailModel>>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/meditation/client/meditation/meditation/getMeditationPage") |
| | | .append(key: "name", value: search) |
| | | .append(key: "lat", value: location?.latitude) |
| | | .append(key: "lon", value: location?.longitude) |
| | | .append(key: "pageCurr", value: page) |
| | | .append(key: "pageSize", value: pageSize) |
| | | .append(key: "apipost_id", value: "2d2eb5e7f991ab") |
| | | return NetworkRequest.request(params: params, method: .get, progress: false) |
| | | } |
| | | |
| | | /// 疗愈馆 |
| | | class func getPavlilonDetail(id:Int)->Observable<BaseResponse<PavilionDetailModel>>{ |
| | | let params = ParamsAppender.build(url: All_Url) |
| | | params.interface(url: "/meditation/client/meditation/meditation/getMeditationInfo") |
| | | .append(key: "id", value: id) |
| | | .append(key: "apipost_id", value: "2d2eb5e7b991aa") |
| | | return NetworkRequest.request(params: params, method: .get, progress: true) |
| | | } |
| | | } |
| | | |
| | | extension Services{ |
| | | /// 获取协议 |
| | | class func agreementBy(_ type:AgreementType)->Observable<BaseResponse<HtmlModel>>{ |
New file |
| | |
| | | // |
| | | // CommonBannerView.swift |
| | | // WanPai |
| | | // |
| | | // Created by 无故事王国 on 2023/6/30. |
| | | // |
| | | |
| | | import UIKit |
| | | import SDWebImage |
| | | struct CommonBannerModel { |
| | | var index = 0 //自定义索引 |
| | | var id:Int? //ID |
| | | var name:String? //名称 |
| | | var resource:String? //数据源:URL等 |
| | | var mediaType:CommonBannerView.MediaType? |
| | | } |
| | | |
| | | class CommonBannerView: UIView, UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout { |
| | | |
| | | enum MediaType { |
| | | case videoUrl,imageUrl,imageLocal,videoLocal |
| | | } |
| | | |
| | | private(set) lazy var collectionView:UICollectionView = { |
| | | var layout = UICollectionViewFlowLayout() |
| | | layout.minimumLineSpacing = 0 |
| | | layout.minimumInteritemSpacing = 0 |
| | | layout.scrollDirection = .horizontal |
| | | layout.sectionInset = .zero |
| | | layout.headerReferenceSize = .zero |
| | | layout.footerReferenceSize = .zero |
| | | layout.itemSize = CGSize(width: self.width, height: self.height) |
| | | let collectionView = UICollectionView(frame:CGRect(x: 0, y: 0, width: self.width, height: self.height), collectionViewLayout: layout) |
| | | collectionView.delegate = self |
| | | collectionView.dataSource = self |
| | | collectionView.isPagingEnabled = true |
| | | collectionView.showsHorizontalScrollIndicator = false |
| | | collectionView.register(CommonBannerViewCell.self, forCellWithReuseIdentifier: "BannerView") |
| | | collectionView.decelerationRate = .normal |
| | | collectionView.contentInsetAdjustmentBehavior = .never |
| | | collectionView.backgroundColor = .white |
| | | collectionView.bounces = false |
| | | return collectionView |
| | | }() |
| | | |
| | | private lazy var pageControl:UIPageControl = { |
| | | let control = UIPageControl() |
| | | control.currentPageIndicatorTintColor = .white |
| | | control.pageIndicatorTintColor = .gray.withAlphaComponent(0.6) |
| | | return control |
| | | }() |
| | | |
| | | private var timer:Timer? |
| | | |
| | | private var items = [CommonBannerModel]() |
| | | private var selectClouse:((CommonBannerModel)->Void)? |
| | | private var autoRoll:Bool = true |
| | | private var currentPage:Int = 0 |
| | | private var timeInterval:Int = 5 |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | setUI() |
| | | } |
| | | |
| | | public func setItems(items:[CommonBannerModel],autoRoll:Bool = true,selectClouse:((CommonBannerModel)->Void)? = nil){ |
| | | |
| | | self.items = items |
| | | if items.count > 1{ |
| | | self.items.append(items.first!) |
| | | } |
| | | self.autoRoll = autoRoll |
| | | self.selectClouse = selectClouse |
| | | if items.count <= 1{self.autoRoll = false} |
| | | |
| | | setUI() |
| | | collectionView.reloadData() |
| | | |
| | | if self.autoRoll{ |
| | | DispatchQueue.main.asyncAfter(deadline: .now()+5) { |
| | | self.startTimer() |
| | | } |
| | | } |
| | | } |
| | | |
| | | private func setUI(){ |
| | | addSubview(collectionView) |
| | | collectionView.snp.makeConstraints { make in |
| | | make.edges.equalToSuperview() |
| | | } |
| | | |
| | | if items.count > 1{ |
| | | pageControl.numberOfPages = items.count - 1 |
| | | |
| | | addSubview(pageControl) |
| | | pageControl.snp.makeConstraints { make in |
| | | make.centerX.equalToSuperview() |
| | | make.bottom.equalToSuperview().offset(-10) |
| | | make.height.equalTo(8) |
| | | } |
| | | } |
| | | } |
| | | |
| | | private func startTimer(){ |
| | | guard timer == nil else {return} |
| | | timer = Timer(timeInterval: TimeInterval(timeInterval), repeats: true, block: {[weak self] t in |
| | | guard let weakSelf = self else { return } |
| | | |
| | | let page = weakSelf.collectionView.contentOffset.x / weakSelf.collectionView.width |
| | | weakSelf.currentPage = Int(page + 1) |
| | | |
| | | if weakSelf.currentPage >= weakSelf.pageControl.numberOfPages{ |
| | | weakSelf.currentPage = 0 |
| | | weakSelf.collectionView.setContentOffset(.zero, animated: false) |
| | | weakSelf.pageControl.currentPage = 0 |
| | | } |
| | | weakSelf.collectionView.setContentOffset(CGPoint(x: weakSelf.currentPage * Int(weakSelf.width), y: 0), animated: true) |
| | | }) |
| | | timer?.fire() |
| | | RunLoop.current.add(timer!, forMode: .common) |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
| | | return items.count |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let item = items[indexPath.row] |
| | | selectClouse?(item) |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { |
| | | return CGSize(width:collectionView.bounds.size.width,height:ceil(collectionView.bounds.size.height)) |
| | | } |
| | | |
| | | func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { |
| | | print("--开始滑动") |
| | | timer?.fireDate = Date.distantFuture |
| | | } |
| | | |
| | | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { |
| | | let page = scrollView.contentOffset.x / scrollView.width |
| | | if page.int >= pageControl.numberOfPages{ |
| | | pageControl.currentPage = 0 |
| | | }else{ |
| | | pageControl.currentPage = page.int |
| | | } |
| | | print("--结束滑动") |
| | | timer?.fireDate = Date.init(timeIntervalSinceNow: 3.0) //3秒后开启 |
| | | } |
| | | |
| | | func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { |
| | | let page = scrollView.contentOffset.x / scrollView.width |
| | | if page.int >= pageControl.numberOfPages{ |
| | | pageControl.currentPage = 0 |
| | | }else{ |
| | | pageControl.currentPage = page.int |
| | | } |
| | | } |
| | | |
| | | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { |
| | | let page = scrollView.contentOffset.x / scrollView.width |
| | | if page.int >= pageControl.numberOfPages{ |
| | | pageControl.currentPage = 0 |
| | | collectionView.setContentOffset(.zero, animated: false) |
| | | }else{ |
| | | pageControl.currentPage = page.int |
| | | } |
| | | } |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BannerView", for: indexPath) as! CommonBannerViewCell |
| | | let item = items[indexPath.row] |
| | | |
| | | switch item.mediaType { |
| | | case .imageUrl: |
| | | if let i = item.resource{ |
| | | cell.imgView.sd_setImage(with: URL(string: i)) |
| | | } |
| | | case .imageLocal: |
| | | if let i = item.resource{ |
| | | cell.imgView.image = UIImage(named: i) |
| | | } |
| | | default:break |
| | | } |
| | | return cell |
| | | } |
| | | } |
| | | |
| | | class CommonBannerViewCell: UICollectionViewCell { |
| | | |
| | | lazy var imgView:UIImageView = { |
| | | let img = UIImageView() |
| | | img.contentMode = .scaleToFill |
| | | return img |
| | | }() |
| | | |
| | | override init(frame: CGRect) { |
| | | super.init(frame: frame) |
| | | contentView.addSubview(imgView) |
| | | imgView.snp.makeConstraints { make in |
| | | make.edges.equalToSuperview() |
| | | } |
| | | } |
| | | |
| | | required init?(coder aDecoder: NSCoder) { |
| | | fatalError("init(coder:) has not been implemented") |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | func updateVideoUrl(_ url:String){ |
| | | if let Url = URL(string: url){ |
| | | player.url = Url |
| | | player.play() |
| | | } |
| | | } |
| | | |
| | | required init?(coder: NSCoder) { |
| | | fatalError("init(coder:) has not been implemented") |
| | | } |
| | |
| | | // |
| | | |
| | | import UIKit |
| | | import JQTools |
| | | |
| | | class PavilionItemCell: UICollectionViewCell { |
| | | |
| | | var pavilionDetailModel:PavilionDetailModel? |
| | | |
| | | @IBOutlet weak var image_cover: UIImageView! |
| | | @IBOutlet weak var view_distance: UIView! |
| | | @IBOutlet weak var label_distance: UILabel! |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_address: UILabel! |
| | | @IBOutlet weak var label_phone: UILabel! |
| | | |
| | | override func awakeFromNib() { |
| | | super.awakeFromNib() |
| | | |
| | | } |
| | | |
| | | func setPavilionDetailModel(_ model:PavilionDetailModel){ |
| | | image_cover.sd_setImage(with: URL(string: model.coverUrl)) |
| | | label_title.text = model.hallName |
| | | view_distance.isHidden = model.distance < 0 |
| | | if model.distance >= 1000{ |
| | | label_distance.text = String(format: "距你%@km", (model.distance / 1000).jq_formatFloat) |
| | | }else{ |
| | | label_distance.text = String(format: "距你%@m", model.distance.jq_formatFloat) |
| | | } |
| | | |
| | | label_address.text = model.address |
| | | label_phone.text = model.contactNumber |
| | | } |
| | | |
| | | } |
| | |
| | | <dependencies> |
| | | <deployment identifier="iOS"/> |
| | | <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/> |
| | | <capability name="Safe area layout guides" minToolsVersion="9.0"/> |
| | | <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> |
| | | </dependencies> |
| | | <objects> |
| | |
| | | </label> |
| | | </subviews> |
| | | </view> |
| | | <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/> |
| | | <constraints> |
| | | <constraint firstItem="Yck-TZ-33e" firstAttribute="leading" secondItem="Mdg-Ka-kmF" secondAttribute="trailing" constant="5" id="03Z-wu-01o"/> |
| | | <constraint firstAttribute="trailing" secondItem="xE9-lZ-06u" secondAttribute="trailing" constant="15" id="1kY-DQ-Phl"/> |
| | |
| | | <constraint firstItem="xE9-lZ-06u" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="15" id="yl4-HG-eHc"/> |
| | | </constraints> |
| | | <size key="customSize" width="233" height="337"/> |
| | | <connections> |
| | | <outlet property="image_cover" destination="LoX-Q1-cyl" id="hAf-Zb-M0x"/> |
| | | <outlet property="label_address" destination="Yck-TZ-33e" id="uz7-5G-xdo"/> |
| | | <outlet property="label_distance" destination="XAi-il-afI" id="8zs-Rr-5oD"/> |
| | | <outlet property="label_phone" destination="Ig6-yP-XKK" id="RJm-oJ-O9e"/> |
| | | <outlet property="label_title" destination="xE9-lZ-06u" id="XJZ-8L-KE9"/> |
| | | <outlet property="view_distance" destination="BbC-q1-W3U" id="mp1-nu-ZuA"/> |
| | | </connections> |
| | | <point key="canvasLocation" x="156.4885496183206" y="120.77464788732395"/> |
| | | </collectionViewCell> |
| | | </objects> |
| | |
| | | |
| | | import UIKit |
| | | import JQTools |
| | | import RxSwift |
| | | import RxRelay |
| | | import CoreLocation |
| | | |
| | | class PavilionViewModel:RefreshInnerModel<PavilionDetailModel>{ |
| | | var location = BehaviorRelay<CLLocationCoordinate2D?>(value: nil) |
| | | var search = BehaviorRelay<String?>(value: nil) |
| | | |
| | | override func api() -> (Observable<BaseResponse<BaseResponseList<PavilionDetailModel>>>)? { |
| | | return Services.getPavlilonPage(location: location.value, search: search.value, page: page) |
| | | } |
| | | } |
| | | |
| | | class PavilionVC: BaseVC { |
| | | @IBOutlet weak var collectionView: UICollectionView! |
| | | |
| | | private var viewModel = PavilionViewModel() |
| | | private var locationTool = JQ_LocationTool.instance() |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | |
| | | viewModel.configure(collectionView) |
| | | viewModel.beginRefresh() |
| | | |
| | | locationTool.startLocation { location in |
| | | self.viewModel.location.accept(location.coordinate) |
| | | self.viewModel.beginRefresh() |
| | | } errorClouse: { error in |
| | | } |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | collectionView.backgroundColor = .clear |
| | | collectionView.showsVerticalScrollIndicator = false |
| | | collectionView.backgroundColor = .clear |
| | | collectionView.contentInset = UIEdgeInsets(top: 52.5, left: 28.5, bottom: 0, right: 28.5) |
| | | collectionView.contentInset = UIEdgeInsets(top: 0, left: 28.5, bottom: 0, right: 28.5) |
| | | collectionView.register(UINib(nibName: "PavilionItemCell", bundle: nil), forCellWithReuseIdentifier: "_PavilionItemCell") |
| | | } |
| | | |
| | | |
| | | @IBAction func searchAction(_ sender: Any) { |
| | | let searchVC = PavilionSearchVC() |
| | | searchVC.viewModel = viewModel |
| | | push(vc: searchVC) |
| | | } |
| | | |
| | |
| | | |
| | | extension PavilionVC:UICollectionViewDelegate{ |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let detailVC = PavilionDetailVC() |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let detailVC = PavilionDetailVC(id: model.id) |
| | | push(vc: detailVC) |
| | | } |
| | | } |
| | | |
| | | extension PavilionVC:UICollectionViewDataSource{ |
| | | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| | | |
| | | let model = viewModel.dataSource.value!.list[indexPath.row] |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_PavilionItemCell", for: indexPath) as! PavilionItemCell |
| | | cell.backgroundColor = .jq_randomColor |
| | | cell.jq_cornerRadius = 16 |
| | | cell.setPavilionDetailModel(model) |
| | | return cell |
| | | |
| | | } |
| | | |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
| | | return 10 |
| | | return viewModel.dataSource.value?.list.count ?? 0 |
| | | } |
| | | } |
| | | |
| | |
| | | </constraints> |
| | | </view> |
| | | <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="cjl-Fc-U1i"> |
| | | <rect key="frame" x="0.0" y="246.33333333333331" width="393" height="571.66666666666674"/> |
| | | <rect key="frame" x="0.0" y="298.66666666666669" width="393" height="519.33333333333326"/> |
| | | <color key="backgroundColor" systemColor="systemBackgroundColor"/> |
| | | <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="b8V-X4-f6k"> |
| | | <size key="itemSize" width="128" height="128"/> |
| | |
| | | <constraint firstItem="X4g-Ky-JIp" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="PZU-6m-kVv"/> |
| | | <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="cjl-Fc-U1i" secondAttribute="bottom" id="TDO-TE-7xi"/> |
| | | <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="cjl-Fc-U1i" secondAttribute="trailing" id="TNn-sQ-vVu"/> |
| | | <constraint firstItem="cjl-Fc-U1i" firstAttribute="top" secondItem="X4g-Ky-JIp" secondAttribute="bottom" constant="-107.5" id="lcy-0i-Wi9"/> |
| | | <constraint firstItem="cjl-Fc-U1i" firstAttribute="top" secondItem="X4g-Ky-JIp" secondAttribute="bottom" constant="-55" id="lcy-0i-Wi9"/> |
| | | <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="X4g-Ky-JIp" secondAttribute="trailing" id="xr8-4F-iZy"/> |
| | | </constraints> |
| | | <point key="canvasLocation" x="14.503816793893129" y="19.718309859154932"/> |
| | |
| | | |
| | | @IBOutlet weak var scrollView: UIScrollView! |
| | | @IBOutlet weak var webView: WKWebView! |
| | | @IBOutlet weak var view_bannerBg: UIView! |
| | | @IBOutlet weak var view_bannerBg: CommonBannerView! |
| | | @IBOutlet weak var label_title: UILabel! |
| | | @IBOutlet weak var label_phone: UILabel! |
| | | @IBOutlet weak var label_address: UILabel! |
| | | @IBOutlet weak var label_openTime: UILabel! |
| | | @IBOutlet weak var cons_webHei: NSLayoutConstraint! |
| | | |
| | | private var id:Int! |
| | | |
| | | override func viewWillAppear(_ animated: Bool) { |
| | | super.viewWillAppear(animated) |
| | | (navigationItem.leftBarButtonItem?.customView as? UIButton)?.setImage(UIImage(named: "btn_back")?.withTintColor(.white), for: .normal) |
| | | } |
| | | |
| | | required init(id:Int) { |
| | | super.init(nibName: nil, bundle: nil) |
| | | self.id = id |
| | | } |
| | | |
| | | required init?(coder: NSCoder) { |
| | | fatalError("init(coder:) has not been implemented") |
| | | } |
| | | |
| | | override func viewDidLoad() { |
| | |
| | | scrollView.contentInsetAdjustmentBehavior = .never |
| | | view_bannerBg.backgroundColor = .jq_randomColor |
| | | webView.scrollView.isScrollEnabled = false |
| | | |
| | | Services.getPavlilonDetail(id: id).subscribe(onNext: {data in |
| | | if let m = data.data{ |
| | | self.label_title.text = m.hallName |
| | | self.label_phone.text = m.contactNumber |
| | | self.label_address.text = m.addressDetail |
| | | self.label_openTime.text = "营业时间:\(m.businessHours)" |
| | | |
| | | let bannerUrl = m.detailBannerUrl.components(separatedBy: ",").filter({$0.jq_isURL}) |
| | | if bannerUrl.count > 0{ |
| | | var items = [CommonBannerModel]() |
| | | for (index,v) in bannerUrl.enumerated(){ |
| | | items.append(CommonBannerModel(index: index, id: index, name: nil, resource: v, mediaType: .imageUrl)) |
| | | } |
| | | self.view_bannerBg.setItems(items: items) |
| | | self.view_bannerBg.collectionView.backgroundColor = .clear |
| | | } |
| | | |
| | | self.webView.loadHTMLString(m.briefIntroduction.jq_wrapHtml(), baseURL: nil) |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | override func setRx() { |
| | | webView.scrollView.rx.observe(CGSize.self, "contentSize").map { (size) -> CGFloat? in |
| | | if let size = size{ |
| | | return size.height |
| | | } |
| | | return nil |
| | | }.subscribe(onNext: { [unowned self](height) in |
| | | if let height = height{ |
| | | self.cons_webHei.constant = height |
| | | } |
| | | }).disposed(by: disposeBag) |
| | | |
| | | } |
| | | |
| | | |
| | |
| | | <objects> |
| | | <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PavilionDetailVC" customModule="XQMuse" customModuleProvider="target"> |
| | | <connections> |
| | | <outlet property="cons_webHei" destination="rWr-aC-9mh" id="YMb-gq-BbT"/> |
| | | <outlet property="label_address" destination="28i-aJ-cBs" id="7x5-fD-eb1"/> |
| | | <outlet property="label_openTime" destination="49g-Ie-o9H" id="hC8-Km-Oux"/> |
| | | <outlet property="label_phone" destination="fDf-4X-hUI" id="kKe-Cn-2Bg"/> |
| | | <outlet property="label_title" destination="ej2-gY-nmo" id="WZH-nb-YiM"/> |
| | | <outlet property="scrollView" destination="yya-lM-LKe" id="3Mj-Nj-oHg"/> |
| | | <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> |
| | | <outlet property="view_bannerBg" destination="bRv-iS-Gxa" id="Crd-Q4-llL"/> |
| | |
| | | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SvO-06-u0I"> |
| | | <rect key="frame" x="0.0" y="0.0" width="393" height="758.33333333333337"/> |
| | | <subviews> |
| | | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRv-iS-Gxa"> |
| | | <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRv-iS-Gxa" customClass="CommonBannerView" customModule="XQMuse" customModuleProvider="target"> |
| | | <rect key="frame" x="0.0" y="0.0" width="393" height="314.33333333333331"/> |
| | | <color key="backgroundColor" systemColor="systemBackgroundColor"/> |
| | | <constraints> |
| | |
| | | |
| | | import UIKit |
| | | import JQTools |
| | | import RxSwift |
| | | import RxRelay |
| | | import CoreLocation |
| | | |
| | | class PavilionSearchVC: BaseVC { |
| | | |
| | | @IBOutlet weak var tf_search: UITextField! |
| | | @IBOutlet weak var collectionView: UICollectionView! |
| | | |
| | | var viewModel:PavilionViewModel? |
| | | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | title = "疗愈馆" |
| | | tf_search.delegate = self |
| | | tf_search.returnKeyType = .search |
| | | viewModel?.configure(collectionView) |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | collectionView.contentInset = UIEdgeInsets(top: 0, left: 28.5, bottom: 0, right: 28.5) |
| | | collectionView.register(UINib(nibName: "PavilionItemCell", bundle: nil), forCellWithReuseIdentifier: "_PavilionItemCell") |
| | | } |
| | | |
| | | @IBAction func cancelAction(_ sender: UIButton) { |
| | | self.navigationController?.popViewController() |
| | | } |
| | | } |
| | | |
| | | extension PavilionSearchVC:UITextFieldDelegate{ |
| | | func textFieldShouldReturn(_ textField: UITextField) -> Bool { |
| | | textField.resignFirstResponder() |
| | | viewModel?.search.accept(textField.text!) |
| | | viewModel?.beginRefresh() |
| | | return true |
| | | } |
| | | } |
| | |
| | | extension PavilionSearchVC:UICollectionViewDataSource{ |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { |
| | | let detailVC = PavilionDetailVC() |
| | | let model = viewModel!.dataSource.value!.list[indexPath.row] |
| | | let detailVC = PavilionDetailVC(id: model.id) |
| | | push(vc: detailVC) |
| | | } |
| | | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { |
| | | |
| | | let model = viewModel!.dataSource.value!.list[indexPath.row] |
| | | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_PavilionItemCell", for: indexPath) as! PavilionItemCell |
| | | cell.backgroundColor = .jq_randomColor |
| | | cell.jq_cornerRadius = 16 |
| | | cell.setPavilionDetailModel(model) |
| | | return cell |
| | | |
| | | } |
| | | |
| | | |
| | | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
| | | return 10 |
| | | return viewModel?.dataSource.value?.list.count ?? 0 |
| | | } |
| | | } |
| | | |
| | |
| | | <state key="normal" title="取消"> |
| | | <color key="titleColor" red="0.039215686274509803" green="0.25882352941176467" blue="0.074509803921568626" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> |
| | | </state> |
| | | <connections> |
| | | <action selector="cancelAction:" destination="-1" eventType="touchUpInside" id="7Y6-am-0Dj"/> |
| | | </connections> |
| | | </button> |
| | | <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="GII-EZ-ldn"> |
| | | <rect key="frame" x="0.0" y="149" width="393" height="669"/> |
| | |
| | | import MediaPlayer |
| | | import RxRelay |
| | | |
| | | @objc enum PlayMusicState:Int{ |
| | | case playing = 1 |
| | | case paurse = 2 |
| | | case end = 3 |
| | | case next = 4 |
| | | } |
| | | |
| | | @objc protocol PayMusicDelegate{ |
| | | func playState(_ state:PlayMusicState) |
| | | @objc optional func playListen(currentInterval:TimeInterval,totalInterval:TimeInterval) |
| | | } |
| | | |
| | | class PayMusicVC: BaseVC { |
| | | |
| | | private var coverImage:UIImageView! |
| | | private var label_name:UILabel! |
| | | private var btn_handle:UIButton! |
| | | private var btn_handleClose:UIButton! |
| | | private var audioPlayer:AudioPlayer! |
| | | private var isAniLoop:Bool = false |
| | | private var meditationModel:MeditationModel?{ |
| | | didSet{ |
| | | if let m = meditationModel{ |
| | | coverImage.sd_setImage(with: URL(string: m.coverUrl),placeholderImage: UIImage(named: "login_top_bg")) |
| | | label_name.text = m.meditationTitle |
| | | } |
| | | } |
| | | } |
| | | |
| | | private init() { |
| | | super.init(nibName: nil, bundle: nil) |
| | |
| | | override func viewDidLoad() { |
| | | super.viewDidLoad() |
| | | audioPlayer = AudioPlayer.getSharedInstance() |
| | | |
| | | let tap = UITapGestureRecognizer(target: self, action: #selector(showDetailAction)) |
| | | coverImage.isUserInteractionEnabled = true |
| | | coverImage.addGestureRecognizer(tap) |
| | | } |
| | | |
| | | override func setUI() { |
| | |
| | | |
| | | btn_handle = UIButton(type: .custom) |
| | | btn_handle.setImage(UIImage(named: "icon_play_purse"), for: .normal) |
| | | btn_handle.setImage(UIImage(named: "icon_play_small"), for: .selected) |
| | | btn_handle.addTarget(self, action: #selector(tapHandleAction), for: .touchUpInside) |
| | | view.addSubview(btn_handle) |
| | | btn_handle.snp.makeConstraints { make in |
| | | |
| | | btn_handleClose = UIButton(type: .custom) |
| | | btn_handleClose.setImage(UIImage(named: "icon_play_close"), for: .normal) |
| | | btn_handleClose.addTarget(self, action: #selector(closeAction), for: .touchUpInside) |
| | | view.addSubview(btn_handleClose) |
| | | |
| | | let statckView = UIStackView(arrangedSubviews: [btn_handle,btn_handleClose]) |
| | | statckView.axis = .horizontal |
| | | statckView.distribution = .equalSpacing |
| | | statckView.spacing = 18 |
| | | view.addSubview(statckView) |
| | | statckView.snp.makeConstraints { make in |
| | | make.right.equalToSuperview().offset(-24.5) |
| | | make.centerY.equalToSuperview() |
| | | make.width.height.equalTo(28) |
| | | } |
| | | } |
| | | |
| | | static func show(){ |
| | | static func show(model:MeditationModel){ |
| | | if let tabBarVC = JQ_currentViewController().navigationController?.tabBarController as? BaseTabBarVC{ |
| | | if !tabBarVC.children.contains(where: {$0 is PayMusicVC}){ |
| | | let vc = PayMusicVC() |
| | | vc.view.isHidden = true |
| | | vc.meditationModel = model |
| | | let tabBarHeight = JQ_currentViewController().navigationController?.tabBarController?.tabBar.height ?? 0 |
| | | JQ_currentViewController().navigationController?.tabBarController?.addChild(vc) |
| | | JQ_currentViewController().navigationController?.tabBarController?.view.addSubview(vc.view) |
| | |
| | | make.bottom.equalToSuperview().offset(-(tabBarHeight)) |
| | | } |
| | | vc.startRunloopAni() |
| | | } |
| | | } |
| | | } |
| | | |
| | | var testURL = [URL]() |
| | | testURL.append(URL(string: "https://downsc.chinaz.net/files/download/sound1/201206/1638.mp3")!) |
| | | testURL.append(URL(string: "https://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3")!) |
| | | testURL.append(URL(string: "https://www.cambridgeenglish.org/images/153149-movers-sample-listening-test-vol2.mp3")!) |
| | | vc.audioPlayer.playAt(firstPlayIndex: 2, urls: testURL) |
| | | @objc func showDetailAction(){ |
| | | if let id = meditationModel?.id{ |
| | | let vc = HomeItemDetailVC(id: id) |
| | | vc.hidesBottomBarWhenPushed = true |
| | | JQ_currentNavigationController().pushViewController(vc) |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | private func startRunloopAni(){ |
| | | isAniLoop = true |
| | | // 创建旋转动画 |
| | | let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") |
| | | rotationAnimation.fromValue = 0 |
| | |
| | | rotationAnimation.duration = 5 // 动画持续时间 |
| | | rotationAnimation.repeatCount = .greatestFiniteMagnitude // 无限重复 |
| | | coverImage.layer.add(rotationAnimation, forKey: nil) |
| | | } |
| | | |
| | | private func stopRunloopAni(){ |
| | | isAniLoop = false |
| | | coverImage.layer.removeAllAnimations() |
| | | } |
| | | |
| | | @objc func tapHandleAction(_ btn:UIButton){ |
| | | btn.isSelected = !btn.isSelected |
| | | |
| | | if btn.isSelected{ |
| | | self.audioPlayer.bgmPlayer?.pause() |
| | | self.audioPlayer.masterPlayer?.pause() |
| | | self.stopRunloopAni() |
| | | }else{ |
| | | self.audioPlayer.bgmPlayer?.play() |
| | | self.audioPlayer.masterPlayer?.play() |
| | | self.startRunloopAni() |
| | | } |
| | | } |
| | | |
| | | @objc func closeAction(_ btn:UIButton){ |
| | | CommonAlertView.show(title: "提示", content: "是否关闭当前播放音频?") { state in |
| | | if state{ |
| | | self.audioPlayer.clean() |
| | | self.view.removeFromSuperview() |
| | | self.removeFromParent() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | class AudioPlayer { |
| | | private var player:AVPlayer? |
| | | private var BGMplayer:AVPlayer? |
| | | private var playIndex:Int = 0 //播放的角标 |
| | | private(set) var bgmPlayer:AVPlayer? // 背景音 |
| | | private(set) var scenePlayer:AVPlayer? //场景音 |
| | | private(set) var masterPlayer:AVPlayer? //大师音 |
| | | private(set) var playIndex:Int = 0 //播放的角标 |
| | | private var cacheDirectory:URL! |
| | | private let session = URLSession.shared |
| | | private var urls = [URL]() |
| | | private var timer:Timer? |
| | | private(set) var times = BehaviorRelay<Int?>(value: nil) |
| | | private(set) var times = BehaviorRelay<Int?>(value: nil) //倒计时定时器 |
| | | private static var _sharedInstance: AudioPlayer? |
| | | private(set) var meditationModel:MeditationModel? |
| | | weak var delegate:PayMusicDelegate? |
| | | |
| | | class func getSharedInstance() -> AudioPlayer { |
| | | guard let instance = _sharedInstance else { |
| | |
| | | _sharedInstance = nil |
| | | } |
| | | |
| | | func playAt(firstPlayIndex:Int,urls:[URL]){ |
| | | self.playIndex = firstPlayIndex |
| | | func clean(){ |
| | | self.bgmPlayer?.pause() |
| | | self.masterPlayer?.pause() |
| | | self.meditationModel = nil |
| | | self.timer = nil |
| | | AudioPlayer.destroy() |
| | | } |
| | | |
| | | autoreleasepool{ |
| | | func playBGMAt(firstPlayIndex:Int,model:MeditationModel,delegate:PayMusicDelegate?){ |
| | | self.delegate = delegate |
| | | self.playIndex = firstPlayIndex |
| | | self.meditationModel = model |
| | | |
| | | let urls = model.backgroundUrl.components(separatedBy: ",").map { str in |
| | | return URL(string: str)! |
| | | } |
| | | |
| | | let masterUrl = URL(string: model.tutorAudioUrl) |
| | | |
| | | autoreleasepool{[unowned self] () in |
| | | for url in urls { |
| | | checkCacheAudio(from: url) { _, url in |
| | | self.checkCacheAudio(from: url) {[unowned self] _, url in |
| | | self.urls.append(url) |
| | | } |
| | | } |
| | | player = AVPlayer(url: self.urls[firstPlayIndex]) |
| | | player?.play() |
| | | self.bgmPlayer = AVPlayer(url: self.urls[firstPlayIndex]) |
| | | self.bgmPlayer?.play() |
| | | |
| | | |
| | | if masterUrl != nil{ |
| | | self.masterPlayer = AVPlayer(url: masterUrl!) |
| | | self.masterPlayer?.play() |
| | | } |
| | | } |
| | | |
| | | self.player!.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 1), queue: DispatchQueue.main) { [unowned self](time) in |
| | | self.bgmPlayer!.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 1), queue: DispatchQueue.main) { [weak self](time) in |
| | | guard let weakSelf = self else { return } |
| | | weakSelf.delegate?.playState(.playing) |
| | | |
| | | //当前正在播放的时间 |
| | | let loadTime = CMTimeGetSeconds(time) |
| | | //视频总时间 |
| | | let totalTime = CMTimeGetSeconds((self.player?.currentItem?.duration)!) |
| | | let totalTime = CMTimeGetSeconds((weakSelf.bgmPlayer?.currentItem?.duration)!) |
| | | |
| | | |
| | | if loadTime >= totalTime{ |
| | | if weakSelf.playIndex <= urls.count - 1{ |
| | | weakSelf.next() |
| | | weakSelf.delegate?.playState(.next) |
| | | }else{ |
| | | weakSelf.delegate?.playState(.end) |
| | | } |
| | | } |
| | | |
| | | weakSelf.delegate?.playListen?(currentInterval: loadTime, totalInterval: totalTime) |
| | | |
| | | if let m = weakSelf.meditationModel{ |
| | | var dic = [String:Any]() |
| | | dic[MPMediaItemPropertyTitle] = "测试" |
| | | dic[MPMediaItemPropertyTitle] = m |
| | | dic[MPMediaItemPropertyArtist] = "心泉·疗愈" |
| | | dic[MPMediaItemPropertyDiscNumber] = 1 |
| | | dic[MPNowPlayingInfoPropertyElapsedPlaybackTime] = loadTime |
| | | dic[MPNowPlayingInfoPropertyPlaybackRate] = 1 |
| | | // 获取时长。item.duration.seconds 不凑效 |
| | | let asset = self.player?.currentItem?.asset |
| | | let asset = weakSelf.bgmPlayer?.currentItem?.asset |
| | | dic[MPMediaItemPropertyPlaybackDuration] = CMTimeGetSeconds(asset!.duration) |
| | | dic[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: CGSize(width: 50, height: 50), requestHandler: { s in |
| | | return UIImage(named: "home_top_bg")! |
| | | }) |
| | | MPNowPlayingInfoCenter.default().nowPlayingInfo = dic |
| | | } |
| | | |
| | | } |
| | | |
| | | //播放完成 |
| | | NotificationCenter.default.addObserver(self, selector: #selector(playbackEnd), name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil) |
| | | // NotificationCenter.default.addObserver(self, selector: #selector(playbackEnd), name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil) |
| | | |
| | | setLockScreen() |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | func playBGMAt(_ url:String){ |
| | | /// 播放场景音乐 |
| | | func playSceneAt(_ url:String){ |
| | | guard let URL = URL(string: url) else { return } |
| | | BGMplayer?.pause() |
| | | scenePlayer?.pause() |
| | | |
| | | if BGMplayer == nil{ |
| | | BGMplayer = AVPlayer(url: URL) |
| | | if scenePlayer == nil{ |
| | | scenePlayer = AVPlayer(url: URL) |
| | | }else{ |
| | | BGMplayer?.replaceCurrentItem(with: AVPlayerItem(url: URL)) |
| | | scenePlayer?.replaceCurrentItem(with: AVPlayerItem(url: URL)) |
| | | } |
| | | DispatchQueue.main.asyncAfter(delay: 3.0) { |
| | | self.BGMplayer?.play() |
| | | self.BGMplayer?.volume = Float(UserDefaultSettingViewModel.getSetting()?.volume ?? 0.5) |
| | | self.scenePlayer?.play() |
| | | self.scenePlayer?.volume = Float(UserDefaultSettingViewModel.getSetting()?.volume ?? 0.5) |
| | | } |
| | | |
| | | |
| | | //播放完成 |
| | | NotificationCenter.default.addObserver(self, selector: #selector(playBGMbackEnd), name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil) |
| | | // NotificationCenter.default.addObserver(self, selector: #selector(playBGMbackEnd), name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil) |
| | | } |
| | | |
| | | func dellocBGM(){ |
| | | BGMplayer?.pause() |
| | | BGMplayer = nil |
| | | func dellocScene(){ |
| | | scenePlayer?.pause() |
| | | scenePlayer = nil |
| | | } |
| | | |
| | | func pauseBGM(){ |
| | | BGMplayer?.pause() |
| | | func pauseScene(){ |
| | | scenePlayer?.pause() |
| | | } |
| | | |
| | | func playBGM(){ |
| | | BGMplayer?.play() |
| | | func playScene(){ |
| | | scenePlayer?.play() |
| | | } |
| | | |
| | | func next(){ |
| | | playIndex += 1 |
| | | let index = min((urls.count - 1), playIndex) |
| | | player?.replaceCurrentItem(with: AVPlayerItem(url: urls[index])) |
| | | player?.play() |
| | | bgmPlayer?.replaceCurrentItem(with: AVPlayerItem(url: urls[index])) |
| | | bgmPlayer?.play() |
| | | } |
| | | |
| | | @objc private func playbackEnd(){ |
| | | |
| | | } |
| | | |
| | | // @objc private func playbackEnd(){ |
| | | // if bgmPlayer?.actionAtItemEnd == AVPlayer.ActionAtItemEnd.pause && playIndex <= urls.count - 1{ |
| | | // next() |
| | | // self.delegate?.playState(.next) |
| | | // }else{ |
| | | // print("背景音乐播放完毕") |
| | | // self.delegate?.playState(.end) |
| | | // } |
| | | // |
| | | // if masterPlayer?.actionAtItemEnd == AVPlayer.ActionAtItemEnd.pause{ |
| | | // print("导师播放完毕") |
| | | // } |
| | | // } |
| | | |
| | | @objc private func playBGMbackEnd(){ |
| | | self.BGMplayer?.seek(to: CMTimeMake(value: 0, timescale: 1)) |
| | | self.BGMplayer?.play() |
| | | self.scenePlayer?.seek(to: CMTimeMake(value: 0, timescale: 1)) |
| | | self.scenePlayer?.play() |
| | | } |
| | | |
| | | func previous(){ |
| | | playIndex -= 1 |
| | | let index = max(0, playIndex) |
| | | player?.replaceCurrentItem(with: AVPlayerItem(url: urls[index])) |
| | | player?.play() |
| | | bgmPlayer?.replaceCurrentItem(with: AVPlayerItem(url: urls[index])) |
| | | bgmPlayer?.play() |
| | | } |
| | | |
| | | func setLockScreen(){ |
| | |
| | | return .commandFailed |
| | | } |
| | | |
| | | self.player?.seek(to: CMTime(seconds: event.positionTime, preferredTimescale: 1), |
| | | self.bgmPlayer?.seek(to: CMTime(seconds: event.positionTime, preferredTimescale: 1), |
| | | toleranceBefore: CMTime(seconds: 0, preferredTimescale: 1), |
| | | toleranceAfter: CMTime(seconds: 0, preferredTimescale: 1)) |
| | | |
| | |
| | | |
| | | // 播放 |
| | | center.playCommand.addTarget {[unowned self] event in |
| | | self.player?.play() |
| | | self.bgmPlayer?.play() |
| | | return .success |
| | | } |
| | | |
| | | // 暂停 |
| | | center.pauseCommand.addTarget {[unowned self] event in |
| | | self.player?.pause() |
| | | self.bgmPlayer?.pause() |
| | | return .success |
| | | } |
| | | |