// // QuoteViewController.swift // MTP2_iOS // // Created by zhongyuan on 2018/4/3. // Copyright © 2018年 zhongyuan.All rights reserved. // import UIKit import GTMRefresh import JXSegmentedView import WHToast /// 期货商品行情报价列表视图容器控制类 class QuoteViewController: BaseTableViewController, QuoteSubscriptDelete { // MARK: - 属性列表 /// 类目 @IBOutlet weak var segmentedView: JXSegmentedView! { didSet { segmentedView.dataSource = subDataSource segmentedView.indicators = [backgroundIndicator] segmentedView.delegate = self } } /// 数据显示列表 @IBOutlet weak var tableView: UITableView! /// 涨跌按钮 @IBOutlet weak var riseButton: UIButton! /// 持仓量按钮 @IBOutlet weak var volumnButton: UIButton! /// 无数据按钮 @IBOutlet weak var dataButton: UIButton! { didSet { dataButton.set(image: UIImage.getImage(name: "noData-universal"), title: "没有查询记录", titlePosition: .bottom, additionalSpacing: 5.0, state: .normal) dataButton.tintColor = .hex999() dataButton.isHidden = true } } /// 所属市场 var marketID: Int = Int.max { didSet { /// 构建行情列表 self.buildQuoteModels() } } /// 外部交易市场 var exchanges: [MoExternalExchange] = [] // MARK: - 生命周期相关 override func viewDidLoad() { super.viewDidLoad() /// loding... addLoadingView() /// cellReuseIdentifier cellReuseIdentifier = "Quote_Cell" /// 构建外部交易市场 buildExternalExchanges() /// 添加下拉刷新控件 tableView.gtm_addRefreshHeaderView(refreshHeader: DefaultGTMRefreshHeader()) { [weak self] in if self?.segmentedView.selectedIndex == 0 { /// 自选 self?.buildMyFavoriteGoods() } else if self?.segmentedView.selectedIndex == 1 { /// 主力 self?.buildGoodsSortByPosition() } else { /// 请求数据 self?.buildQuoteModels() } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) /// 清除广播侦听 MTP2BusinessCore.shared.broadcastManager?.removeBroadcastListener(owner: self) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) /// 初始化广播侦听 initListen() } /// 初始化广播侦听 func initListen() { /// 侦听行情推送广播 MTP2BusinessCore.shared.broadcastManager?.addBroadcastListener(owner: self, action: .ReceiveTradeQuote) { [weak self] in guard let quoteGoodsInfos = $0.object as? [(goodsHqCode: String, exchHqCode: String)], let goodsManager = MTP2BusinessCore.shared.goodsManager, let weakSelf = self else { return } /// 有数据 if quoteGoodsInfos.count != 0 { /// 待刷新行 for (index, quoteModel) in weakSelf.tableCellModels.enumerated() { if quoteGoodsInfos.first(where: { $0.goodsHqCode == quoteModel.outgoodscode }) != nil { let indexPath = IndexPath(row: index, section: 0) /// 更新显示行情数据 guard let moGoodsInfo = goodsManager.goodsInfos?.first(where: { $0.goodscode == quoteModel.goodscode }) else { continue } if quoteModel.last != moGoodsInfo.moQuoteInfo?.last { quoteModel.update(moGoodsInfo: moGoodsInfo) DispatchQueue.main.async { /// 判断是否在可见区域 if weakSelf.tableView.indexPathsForVisibleRows?.firstIndex(of: indexPath) != nil { weakSelf.tableView.reloadRows(at: [indexPath], with: .none) } } } } } } } } // MARK: - 数据初始化 /// 构建外部交易市场 fileprivate func buildExternalExchanges() { /// 加载资管外部交易市场 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let externals = goodsManager.exchanges?.filter({$0.goodsGroups.count != 0}), externals.count != 0 else { return } /// 外部交易市场 exchanges = externals /// subDataSource subDataSource.titles = externals.map({ (obj) -> String in return obj.exexchangename }) subDataSource.titles.insert(contentsOf: ["自选", "主力"], at: 0) /// 默认选中 segmentedView.defaultSelectedIndex = 1 segmentedView.reloadData() segmentedView.delegate?.segmentedView(segmentedView, didSelectedItemAt: 1) } // MARK: - 行情相关 /// 构建行情 fileprivate func buildQuoteModels() { /// 获取当前外部交易市场下的所有商品组的商品信息 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let exchange = goodsManager.getMoExternalExchange(autoID: marketID) else { return } /// 显示外部交易所下面商品组的商品信息 var showGoodsInfos: [MoGoodsInfo] = [] if let goodsInfos = goodsManager.goodsInfos { for obj in exchange.goodsGroups { let desGoodsInfos = goodsInfos.filter { $0.goodsgroupid == obj.goodsgroupid } showGoodsInfos.append(contentsOf: desGoodsInfos) } /// endRefreshing tableView.endRefreshing() /// 移出所有数据 tableCellModels.removeAll() /// 构建数据 showGoodsInfos.forEach { tableCellModels.append(QuoteModel(moGoodsInfo: $0, rise: false, volumn: 0)) } /// 刷新数据 tableView.reloadData() /// 无数据按钮 dataButton.isHidden = tableCellModels.count != 0 if tableCellModels.count > 0 { /// 等待UI操作完成,也就是tableView刷新完之后执行 DispatchQueue.main.async { [weak self] in if self?.visibleGoodsCodes.count != 0 { self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes } } } } } /// 构建我的自选商品信息 fileprivate func buildMyFavoriteGoods() { /// 获取当前外部交易市场下的所有商品组的商品信息 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let goodsInfos = goodsManager.goodsInfos else { return } /// startAnimating _anim?.startAnimating() /// 查询我得收藏商品 goodsManager.queryUserFavoriteGoodses { (isSuccess, error, ids) in DispatchQueue.main.async { /// endRefreshing self.tableView.endRefreshing() /// stopAnimating self._anim?.stopAnimating() /// 查询失败 if !isSuccess { /// 为空 self.tableCellModels = [] /// 刷新数据 self.tableView.reloadData() /// 无数据按钮 self.dataButton.isHidden = self.tableCellModels.count != 0 /// show error WHToast.showMessage("自选获取失败", duration: ToastTimer, finishHandler: {}) return } /// 行情显示数据 var objs: [QuoteModel] = [] if let goodsids = ids, goodsids.count != 0 { goodsids.forEach { (id) in if let goodsInfo = goodsInfos.first(where: {$0.goodsid == id}) { objs.append(QuoteModel(moGoodsInfo: goodsInfo, rise: false, volumn: 0)) } } } self.tableCellModels = objs /// 刷新数据 self.tableView.reloadData() /// 无数据按钮 self.dataButton.isHidden = self.tableCellModels.count != 0 /// 等待UI操作完成,也就是tableView刷新完之后执行 if self.tableCellModels.count > 0 { DispatchQueue.main.async { [weak self] in if self?.visibleGoodsCodes.count != 0 { self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes } } } } } } /// 获取主力合约商品信息 fileprivate func buildGoodsSortByPosition() { /// 获取当前外部交易市场下的所有商品组的商品信息 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let goodsInfos = goodsManager.goodsInfos else { return } /// endRefreshing tableView.endRefreshing() /// 行情显示数据 var objs: [QuoteModel] = [] let infos = goodsManager.sortByPositions if infos.count != 0 { infos.forEach { (id) in if let goodsInfo = goodsInfos.first(where: {$0.goodscode == id.goodscode}) { goodsInfo.ismian = true objs.append(QuoteModel(moGoodsInfo: goodsInfo, rise: false, volumn: 0)) } } } tableCellModels = objs /// 刷新数据 tableView.reloadData() /// 无数据按钮 dataButton.isHidden = self.tableCellModels.count != 0 /// 等待UI操作完成,也就是tableView刷新完之后执行 if tableCellModels.count > 0 { DispatchQueue.main.async { [weak self] in if self?.visibleGoodsCodes.count != 0 { self?.shouldSubscriptGoodsCodes = self?.visibleGoodsCodes } } } } // MARK: - 交互相关 /// onButtonPressed /// - Parameter sender: sender @IBAction func onButtonPressed(_ sender: UIButton) {} // MARK: - 订阅行情相关 var uuid: String = UUID().uuidString var shouldSubscriptGoodsCodes: Set? { didSet { guard let quoteSubscriptManager = MTP2BusinessCore.shared.quoteSubscriptManager else { return } quoteSubscriptManager.updateQuoteSubcriptGoods(uuid: uuid, goodsCode: shouldSubscriptGoodsCodes ?? []) } } var visibleGoodsCodes: Set { var goodsCodes = Set() self.tableView.visibleCells.forEach { (cell) in if let quoteCell = cell as? QuoteCell, let goodsCode = quoteCell.model?.outgoodscode { goodsCodes.insert(goodsCode) } } return goodsCodes } // MARK: - UIScrollViewDelegate override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { if visibleGoodsCodes.count != 0 { shouldSubscriptGoodsCodes = visibleGoodsCodes } } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 60.0 } // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. if segue.identifier == "ShowATPTrade", let t = segue.destination as? ATPTradeViewController, let goodsid = (sender as? QuoteCell)?.model?.goodsid { /// 交易下单 t.moGoodsInfo = MTP2BusinessCore.shared.goodsManager?.goodsInfos?.first(where: {$0.goodsid == goodsid}) } else if segue.identifier == "ShowChart", let c = segue.destination as? ChartViewController, let goodsid = (sender as? QuoteCell)?.model?.goodsid { /// 图表 c.moGoodsInfo = MTP2BusinessCore.shared.goodsManager?.goodsInfos?.first(where: {$0.goodsid == goodsid}) } } } // MARK: - JXSegmentedViewDelegate extension QuoteViewController: JXSegmentedViewDelegate { func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) { if index == 0 { /// 自选 buildMyFavoriteGoods() } else if index == 1 { /// 主力 buildGoodsSortByPosition() } else { marketID = exchanges[index-2].autoid } } } // MARK: - 行情报价牌Cell class QuoteCell: BaseTableViewCell { /// 商品名称 @IBOutlet weak var goodsName: UILabel! /// 商品代码 @IBOutlet weak var goodsCode: UILabel! /// 最新价 @IBOutlet weak var last: UILabel! /// 涨跌 @IBOutlet weak var increase: UILabel! /// 最低 @IBOutlet weak var lowest: UILabel! /// 交易所标识 @IBOutlet weak var flag: UILabel! /// 自选按钮 @IBOutlet weak var favorit: UIButton! /// 最新价视图 @IBOutlet weak var lastBgView: UIView! /// 是否是自选 fileprivate var isSelfGoods = false { didSet { /// 图标 favorit.setImage(UIImage(named: isSelfGoods ? "favorited" : "unfavorited"), for: .normal) } } override var model: QuoteModel? { didSet { if let obj = model { /// 商品名称 goodsName.text = obj.goodsname /// 商品代码 goodsCode.text = obj.outgoodscode /// 最新价 last.text = (obj.last != nil && obj.last != 0.0) ? String(format: "%.\(obj.decimalplace)f", obj.last!) : "--" /// 自选商品 isSelfGoods = MTP2BusinessCore.shared.goodsManager?.favGoodsIds?.contains(where: {$0 == obj.goodsid}) ?? false /// 涨跌值 increase.text = obj.last == 0.0 ? "--" : "\(((obj.last ?? 0.0 )-obj.preSettle).toUpString(place: obj.decimalplace))" /// 持仓量 最低 最高 为0 则显示-- let high = Double(obj.highest ?? "0.0") == 0.0 ? "--" : obj.highest?.isBlank() let low = Double(obj.lowest ?? "0.0") == 0.0 ? "--" : obj.lowest?.isBlank() let totalVolumn = obj.totalVolumn ?? "--" lowest.text = obj.volumn == 0 ? totalVolumn : (obj.volumn == 1 ? high : low) /// 昨结颜色 if obj.preColorFlag == 1 { last.textColor = Color_Quote_Up increase.textColor = Color_Quote_Up lowest.textColor = Color_Quote_Up } else if obj.preColorFlag == -1 { last.textColor = Color_Quote_Down increase.textColor = Color_Quote_Down lowest.textColor = Color_Quote_Up } else { last.textColor = Color_Quote_Normal increase.textColor = Color_Quote_Normal lowest.textColor = Color_Quote_Up } /// 涨跌颜色 if obj.colorFlag == 1 { lastBgView.backgroundColor = .fromHex(rgbValue: 0xFFEAEC) } else if obj.colorFlag == -1 { lastBgView.backgroundColor = .fromHex(rgbValue: 0xDDFFF4) } else { lastBgView.backgroundColor = .white } /// 获取标志 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let goodsGroup = goodsManager.goodsGroups?.first(where: {$0.goodsgroupid == obj.goodsgroupid}), let exchange = goodsManager.exchanges?.first(where: {$0.autoid == goodsGroup.exexchangeid}) else { return } flag.text = exchange.exexchangecode.getExchangeCodeVerb() } } } @IBAction fileprivate func onButtonPressed(_ sender: UIButton) { switch sender { case favorit: /// 异常 guard let goodsManager = MTP2BusinessCore.shared.goodsManager, let goodsid = model?.goodsid else { return } /// 判断是否为自选 if isSelfGoods { /// 删自选 goodsManager.requestRemoveUserFavoriteGoods(goodsID: goodsid, callback: { (isSuccess, _) in DispatchQueue.main.async { self.isSelfGoods = !isSuccess } }) } else { /// 加自选 goodsManager.requestAddUserFavoriteGoods(goodsID: goodsid, callback: { (isSuccess, _) in DispatchQueue.main.async { self.isSelfGoods = isSuccess } }) } default: break } } } // MARK: - 行情显示模型 class QuoteModel { /// 商品ID var goodsid: Int = 0 /// 商品名称 var goodsname: String /// 商品代码 var goodscode: String /// 外部商品代码 var outgoodscode: String /// 最新价 var last: Double? /// 成交量 var totalVolumn: String? /// 最低 var lowest: String? /// 最高 var highest: String? /// 小数位 var decimalplace: Int = 0 /// 颜色标识 var colorFlag: Int = 0 /// 昨收颜色标识 var preColorFlag: Int = 0 /// 商品组ID var goodsgroupid: Int = 0 /// 涨跌变量 var rise: Bool = false /// 持仓量变量 var volumn: Int = 0 /// 昨结算 var preSettle: Double = 0.0 init(goodsid: Int = 0, goodscode: String, goodsname: String, outgoodscode: String) { self.goodscode = goodscode self.goodsname = goodsname self.goodsid = goodsid self.outgoodscode = outgoodscode } init(moGoodsInfo: MoGoodsInfo) { self.goodscode = moGoodsInfo.goodscode self.goodsname = moGoodsInfo.goodsname self.decimalplace = moGoodsInfo.decimalplace self.goodsgroupid = moGoodsInfo.goodsgroupid self.goodsid = moGoodsInfo.goodsid self.outgoodscode = moGoodsInfo.outgoodscode /// 更新行情信息 self.update(moGoodsInfo: moGoodsInfo) } init(moGoodsInfo: MoGoodsInfo, rise: Bool = false, volumn: Int = 0) { self.goodscode = moGoodsInfo.goodscode self.goodsname = moGoodsInfo.goodsname self.decimalplace = moGoodsInfo.decimalplace self.goodsgroupid = moGoodsInfo.goodsgroupid self.rise = rise self.volumn = volumn self.goodsid = moGoodsInfo.goodsid self.outgoodscode = moGoodsInfo.outgoodscode /// 更新行情信息 self.update(moGoodsInfo: moGoodsInfo) } func update(moGoodsInfo: MoGoodsInfo) { /// 行情相关字段赋值 if let quote = moGoodsInfo.moQuoteInfo { /// 最新价 self.last = quote.last /// 成交量 self.totalVolumn = Double(moGoodsInfo.moQuoteInfo?.totalVolume ?? 0).getTotalTurnoverString(reserve: 2).isBlank() /// 颜色 let preColorFlag = quote.last.compare(settle: moGoodsInfo.getPreClose()) /// 昨收颜色标识 self.preColorFlag = preColorFlag /// 昨结算 self.preSettle = quote.presettle } /// 颜色值 self.colorFlag = moGoodsInfo.colorFlag /// 最低价 self.lowest = moGoodsInfo.moQuoteInfo?.lowest.toUpString(place: moGoodsInfo.decimalplace).isBlank() /// 最高价 self.highest = moGoodsInfo.moQuoteInfo?.highest.toUpString(place: moGoodsInfo.decimalplace).isBlank() } }