// // BaseCollectionViewController.swift // MTP2_iOS // // Created by Muchinfo on 2021/1/11. // Copyright © 2021 Muchinfo. All rights reserved. // import UIKit import JXSegmentedView import IBAnimatable import SwiftyAttributes /// 列头宽度 var columnWidth: CGFloat = 100.0 /// 数据展示集合基类 class BaseCollectionViewController: BaseViewController { // MARK: - 属性列表 /// 操作视图 @IBOutlet weak var operationView: UIStackView! { didSet { operationView.isHidden = true } } /// 类目 @IBOutlet weak var segmentedView: JXSegmentedView! { didSet { segmentedView.indicators = [indicator] segmentedView.delegate = self } } /// 主滚动视图 @IBOutlet weak var scrollView: UIScrollView! /// 阴影视图 @IBOutlet weak var shadowView: AnimatableView! /// 坐标题 @IBOutlet weak var leftTitle: UILabel! /// 列头头部视图 @IBOutlet weak var headerCollectionView: UICollectionView! { didSet { /// 设置委托代理 headerDelegate = HeaderDelegate(self) headerCollectionView.dataSource = headerDelegate headerCollectionView.delegate = headerDelegate /// 设置约束 headerCollectionView.setCollectionViewLayout(flowLayout, animated: true) } } /// 左侧数据列表 @IBOutlet weak var leftTableView: UITableView! { didSet { leftDelegate = LeftDelegate(self) leftTableView.dataSource = leftDelegate leftTableView.delegate = leftDelegate } } /// 右侧数据列表 @IBOutlet weak var rightTableView: UITableView! { didSet { rightDelegate = RightDelegate(self) rightTableView.dataSource = rightDelegate rightTableView.delegate = rightDelegate } } /// 宽度约束 @IBOutlet weak var widthLayoutConstraint: NSLayoutConstraint! /// 数据显示集合视图约束 lazy var flowLayout: UICollectionViewFlowLayout = { /// 最小行间距,默认是0 $0.minimumLineSpacing = 0 /// 最小左右间距,默认是10 $0.minimumInteritemSpacing = 0 /// 区域内间距,默认是 UIEdgeInsetsMake(0, 0, 0, 0) $0.sectionInset = UIEdgeInsets(top: 0.0, left: 0, bottom: 0, right: 0) /// 水平滚动 $0.scrollDirection = .horizontal return $0 } (UICollectionViewFlowLayout()) /// 列头数据 var columnHeaders: [String] = [] { didSet { /// 刷新列表数据 self.headerCollectionView.reloadData() /// 高度约束 widthLayoutConstraint.constant = columnWidth*CGFloat(columnHeaders.count-1) /// 左侧标题 leftTitle.text = columnHeaders.first } } /// 左侧数据信息 var leftDatas: [String] = [] { didSet { /// 数据为空 if leftDatas.count == 0, operationView == nil { operationView.isHidden = true } /// 刷新左侧列表数据 leftTableView.reloadData() } } /// 右侧数据信息 var rightDatas: [[String]] = []{ didSet { /// 刷新左侧列表数据 rightTableView.reloadData() } } /// banners var banners: [String] = [] /// HeaderCellIdentifier let HeaderCellIdentifier = "Header_Cell" var headerDelegate: HeaderDelegate? /// LeftCellID let LeftCellID = "Left_Cell" var leftDelegate: LeftDelegate? /// RightCellID let RightCellID = "Right_Cell" var rightDelegate: RightDelegate? /// 执行回调 var segmentBlock: ((_ index: Int, _ segment: JXSegmentedView) -> Void)? /// 操作回调 var operatorBlock: ((_ type: OperatorType, _ takeInfo: Any?, _ row: Int?) -> Void)? /// 选中回调 var selectBlock: ((_ row: Int?) -> Void)? /// 是否允许选中 var canSelected: Bool = true /// 选中的位置 var selectedRow: (row: Int, isExpland: Bool)? { didSet { /// 数据为空会异常 if leftDatas.count == 0 || rightDatas.count == 0 { return } if let index = selectedRow { /// 刷新某一行 leftTableView.reloadRows(at: [IndexPath(row: index.row, section: 0)], with: .automatic) /// 刷新某一行 rightTableView.reloadRows(at: [IndexPath(row: index.row, section: 0)], with: .automatic) } else { /// 不为空 if operationView != nil { /// 每次更新都隐藏 operationView.isHidden = true } /// 重置选中行 if let cell = leftTableView.visibleCells.first(where: { (obj) -> Bool in return (obj as? LeftCell)?.isExpland ?? false }) as? LeftCell { cell.isExpland = false } if let cell = rightTableView.visibleCells.first(where: { (obj) -> Bool in return (obj as? RightCell)?.isExpland ?? false }) as? RightCell { cell.isExpland = false } /// 重新刷新数据 leftTableView.reloadData() rightTableView.reloadData() } } } /// 正常高度 let normalTableHeight: CGFloat = 55.0 /// 展开时高度 let explaneTableHeight: CGFloat = 95.0 // MARK: - 生命周期 override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. /// 不需要返回按钮 addBackBarButtonItem(true) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) /// 允许横屏 let app = UIApplication.shared.delegate as? AppDelegate app?.isRotation = true /// 侦听横竖屏切换 NotificationCenter.default.addObserver(self, selector: #selector(orientationDidChange(interfaceOrientation:)), name: UIDevice.orientationDidChangeNotification, object: nil) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) /// 清空数据 selectedRow = nil } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) /// 允许横屏 let app = UIApplication.shared.delegate as? AppDelegate app?.isRotation = true /// 移出侦听 NotificationCenter.default.removeObserver(self) } // MARK: - 交互相关 /// onTapGestureRecognizer /// - Parameter sender: sender @IBAction fileprivate func onTapGestureRecognizer(_ sender: UITapGestureRecognizer) { let point = sender.location(in: rightTableView) /// 异常 guard let index = rightTableView.indexPathForRow(at: point) else { return } /// 执行回调 rightTableView.delegate?.tableView?(rightTableView, didSelectRowAt: index) } /// orientationDidChange /// - Parameter interfaceOrientation: interfaceOrientation @objc func orientationDidChange(interfaceOrientation: UIInterfaceOrientation) { if segmentedView != nil { /// 横竖屏切换时 重新计算 segmentedView.width = self.view.width segmentedView.reloadData() } } } // MARK: - JXSegmentedViewDelegate extension BaseCollectionViewController: JXSegmentedViewDelegate { func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) { /// 清空数据 selectedRow = nil /// 执行回调 if let block = self.segmentBlock { block(index, segmentedView) } } } // MARK: - ColunmTitles、 UITableViewDelegate & UITableViewDataSource class HeaderDelegate: NSCoder, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { var owner: BaseCollectionViewController init(_ owner: BaseCollectionViewController) { self.owner = owner } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.owner.columnHeaders.count-1 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.owner.HeaderCellIdentifier, for: indexPath) as! HeaderCell cell.headerTitle.text = owner.columnHeaders[indexPath.row+1] return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: columnWidth, height: 35.0) } func scrollViewDidScroll(_ scrollView: UIScrollView) { self.owner.scrollView.contentOffset.x = scrollView.contentOffset.x } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {} } // MARK: - 左边 UITableViewDelegate & UITableViewDataSource class LeftDelegate: NSCoder, UITableViewDelegate, UITableViewDataSource { var owner: BaseCollectionViewController init(_ owner: BaseCollectionViewController) { self.owner = owner } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.owner.leftDatas.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: owner.LeftCellID, for: indexPath) as! LeftCell /// 所在的行数 cell.rowNum = indexPath.row /// 执行回调 cell.operatorBlock = { (_ type: OperatorType, _ takeInfo: Any?, _ row: Int?) in if let block = self.owner.operatorBlock { block(type, takeInfo, row) } } cell.model = owner.leftDatas[indexPath.row] return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { /// 防止数据为空异常 if owner.leftDatas.count == 0 || owner.rightDatas.count == 0 { return } /// 先清除选中状态 owner.leftTableView.visibleCells.filter({($0 as? LeftCell)?.isExpland == true}).forEach { (obj) in (obj as? LeftCell)?.isExpland = false } owner.rightTableView.visibleCells.filter({($0 as? RightCell)?.isExpland == true}).forEach { (obj) in (obj as? RightCell)?.isExpland = false } if let selected = owner.selectedRow, indexPath.row == selected.row { owner.selectedRow = (row: indexPath.row, !(selected.isExpland)) } else { owner.selectedRow = (row: indexPath.row, true) } /// 执行选中回调 if let select = owner.selectBlock { select(indexPath.row) } } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if let index = owner.selectedRow { return (index.row == indexPath.row && index.isExpland == true && owner.canSelected) ? owner.explaneTableHeight : owner.normalTableHeight } return owner.normalTableHeight } func scrollViewDidScroll(_ scrollView: UIScrollView) { owner.rightTableView.contentOffset.y = scrollView.contentOffset.y /// 执行回调 if let bk = owner.selectBlock, let index = owner.selectedRow, index.isExpland { bk(index.row) } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {} } // MARK: - 右边 UITableViewDelegate & UITableViewDataSource class RightDelegate: NSCoder, UITableViewDelegate, UITableViewDataSource { var owner: BaseCollectionViewController init(_ owner: BaseCollectionViewController) { self.owner = owner } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return owner.rightDatas.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: self.owner.RightCellID, for: indexPath) as! RightCell cell.rowNum = indexPath.row cell.model = owner.rightDatas[indexPath.row] cell.operatorBlock = { (_ type: OperatorType, _ takeInfo: Any?, _ row: Int?) in /// 执行回调 if let block = self.owner.operatorBlock { block(type, takeInfo, row) } } return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { /// 防止数据为空异常 if owner.leftDatas.count == 0 || owner.rightDatas.count == 0 { return } /// 先清除选中状态 owner.leftTableView.visibleCells.filter({($0 as? LeftCell)?.isExpland == true}).forEach { (obj) in (obj as? LeftCell)?.isExpland = false } owner.rightTableView.visibleCells.filter({($0 as? RightCell)?.isExpland == true}).forEach { (obj) in (obj as? RightCell)?.isExpland = false } if let selected = owner.selectedRow, indexPath.row == selected.row { owner.selectedRow = (row: indexPath.row, !(selected.isExpland)) } else { owner.selectedRow = (row: indexPath.row, true) } /// 执行选中回调 if let select = owner.selectBlock { select(indexPath.row) } } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if let index = owner.selectedRow { return (index.row == indexPath.row && index.isExpland == true && owner.canSelected) ? owner.explaneTableHeight : owner.normalTableHeight } return owner.normalTableHeight } func scrollViewDidScroll(_ scrollView: UIScrollView) { owner.leftTableView.contentOffset.y = scrollView.contentOffset.y /// 执行回调 if let bk = owner.selectBlock, let index = owner.selectedRow, index.isExpland { bk(index.row) } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {} } // MARK: - UIScrollViewDelegate extension BaseCollectionViewController: UIScrollViewDelegate { /// scrollViewDidScroll /// - Parameter scrollView: scrollView func scrollViewDidScroll(_ scrollView: UIScrollView) { /// contentOffset.x headerCollectionView.contentOffset.x = scrollView.contentOffset.x /// 左侧 if scrollView.contentOffset.x<=0 { headerCollectionView.contentOffset.x = 0.0 } /// 执行回调 if let bk = selectBlock, let index = selectedRow, index.isExpland { bk(index.row) } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {} func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {} } // MARK: - HeaderCell class HeaderCell: UICollectionViewCell { @IBOutlet weak var headerTitle: UILabel! } // MARK: - RightCell class RightCell: BaseTableViewCell<[String]> { /// 滚动视图 @IBOutlet weak var stackView: UIStackView! /// 列头头部视图 @IBOutlet weak var collectionView: UICollectionView! { didSet { /// 设置委托代理 collectionView.dataSource = self collectionView.delegate = self /// 设置约束 collectionView.setCollectionViewLayout(flowLayout, animated: true) /// 刷新数据 collectionView.reloadData() } } /// 数据显示集合视图约束 lazy var flowLayout: UICollectionViewFlowLayout = { /// 最小行间距,默认是0 $0.minimumLineSpacing = 0 /// 最小左右间距,默认是10 $0.minimumInteritemSpacing = 0 /// 区域内间距,默认是 UIEdgeInsetsMake(0, 0, 0, 0) $0.sectionInset = UIEdgeInsets(top: 0.0, left: 0, bottom: 0, right: 0) /// 水平滚动 $0.scrollDirection = .horizontal return $0 } (UICollectionViewFlowLayout()) /// CellIdentifier fileprivate let CellIdentifier = "Collection_Cell" /// 操作回调 var operatorBlock: ((_ type: OperatorType, _ takeInfo: Any?, _ row: Int?) -> Void)? /// 上层视图 var father: BaseCollectionViewController? /// 数据模型 override var model: [String]? { didSet { /// 异常 guard let _ = model else { return } /// 刷新列表数据 self.collectionView.reloadData() } } } // MARK: - UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout extension RightCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.model?.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! RightContentCell let titles = (self.model?[indexPath.item] ?? "\n").components(separatedBy: "\n") cell.titleLabel.text = titles.first?.isBlank() if titles.count > 1 { cell.detailLabel.text = titles.last?.isBlank() } cell.detailLabel.isHidden = titles.count <= 1 return cell } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: columnWidth, height: 55.0) } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { /// 执行回调 if let bk = operatorBlock { bk(.detail, nil, rowNum) } } } // MARK: - RightContentCell class RightContentCell: UICollectionViewCell { /// titleLabel @IBOutlet weak var titleLabel: UILabel! /// detailLabel @IBOutlet weak var detailLabel: UILabel! } // MARK: - LeftCell class LeftCell: BaseTableViewCell { /// 箭头 @IBOutlet weak var arrow: AnimatableImageView! /// titleLabel @IBOutlet weak var titleLabel: UILabel! /// detailLabel @IBOutlet weak var detailLabel: UILabel! /// 是否拓展 override var isExpland: Bool { didSet { arrow.image = UIImage(named: isExpland ? "arrow_up" : "arrow_down") } } /// 数据Model override var model: Any? { didSet { /// 异常 guard let obj = model as? String else { return } /// 数据显示 let titles = obj.components(separatedBy: "\n") titleLabel.text = titles.first?.isBlank() if titles.count > 1 { detailLabel.text = titles.last?.isBlank() } detailLabel.isHidden = titles.count <= 1 } } /// 操作回调 var operatorBlock: ((_ type: OperatorType, _ takeInfo: Any?, _ row: Int?) -> Void)? }