PopOverView.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. //
  2. // PopOverView.swift
  3. // SwiftBilibili
  4. //
  5. // Created by 罗文 on 2021/1/24.
  6. // Copyright © 2021年 罗文. All rights reserved.
  7. //
  8. import UIKit
  9. class Section<ActionDataType, SectionHeaderDataType> {
  10. open var data: SectionHeaderDataType? {
  11. get { return _data?.data }
  12. set { _data = RawData(data: newValue) }
  13. }
  14. open var actions = [Action<ActionDataType>]()
  15. fileprivate var _data: RawData<SectionHeaderDataType>?
  16. public init() {}
  17. }
  18. enum CellSpec<CellType: UITableViewCell,CellDataType> {
  19. case nibFile(nibName: String, bundle: Bundle?, height: ((CellDataType) -> CGFloat))
  20. case cellClass(height:((CellDataType) -> CGFloat))
  21. var height: ((CellDataType) -> CGFloat) {
  22. switch self {
  23. case .nibFile(_, _, let heightCallback):
  24. return heightCallback
  25. case .cellClass(let heightCallback):
  26. return heightCallback
  27. }
  28. }
  29. }
  30. enum HeaderSpec<HeaderType: UIView,HeaderDataType> {
  31. case nibFile(nibName: String, bundle: Bundle?, height: ((HeaderDataType) -> CGFloat))
  32. case cellClass(height:((HeaderDataType) -> CGFloat))
  33. var height: ((HeaderDataType) -> CGFloat) {
  34. switch self {
  35. case .nibFile(_, _, let heightCallback):
  36. return heightCallback
  37. case .cellClass(let heightCallback):
  38. return heightCallback
  39. }
  40. }
  41. }
  42. enum FooterSpec<FooterType: UIView,FooterDataType> {
  43. case nibFile(nibName: String, bundle: Bundle?, height: ((FooterDataType) -> CGFloat))
  44. case cellClass(height:((FooterDataType) -> CGFloat))
  45. var height: ((FooterDataType) -> CGFloat) {
  46. switch self {
  47. case .nibFile(_, _, let heightCallback):
  48. return heightCallback
  49. case .cellClass(let heightCallback):
  50. return heightCallback
  51. }
  52. }
  53. }
  54. private enum ReusableViewIds: String {
  55. case Cell = "Cell"
  56. case SectionHeader = "SectionHeader"
  57. }
  58. final class RawData<T> {
  59. var data: T!
  60. init?(data: T?) {
  61. guard let data = data else { return nil }
  62. self.data = data
  63. }
  64. }
  65. class PopOverView<ActionViewType: UITableViewCell,ActionDataType,HeaderViewType:UIView,HeaderDataType,FooterViewType:UIView,FooterDataType,SectionHeaderViewType: UITableViewHeaderFooterView, SectionHeaderDataType> : UIView,UITableViewDataSource,UITableViewDelegate {
  66. private let screenHeight = UIScreen.main.bounds.height
  67. private let screenWidth = UIScreen.main.bounds.width
  68. private let keyWindow = UIApplication.shared.keyWindow
  69. private var isUpward = true
  70. //MARK: - Private properties
  71. fileprivate var _footerData: RawData<FooterDataType>?
  72. fileprivate var _headerData: RawData<HeaderDataType>?
  73. fileprivate var _actions = [Action<ActionDataType>]()
  74. fileprivate var _sections = [Section<ActionDataType, SectionHeaderDataType>]()
  75. //MARK - Public properties
  76. var headerData: HeaderDataType? {
  77. set { _headerData = RawData(data: newValue)}
  78. get { return _headerData?.data}
  79. }
  80. var footerData: FooterDataType? {
  81. set { _footerData = RawData(data: newValue)}
  82. get { return _footerData?.data}
  83. }
  84. var settings: PopOverViewSettings = PopOverViewSettings.defaultSettings()
  85. var cellSpec: CellSpec<ActionViewType,ActionDataType>?
  86. var sectionHeaderSpec: HeaderSpec<SectionHeaderViewType, SectionHeaderDataType>?
  87. var headerSpec: HeaderSpec<HeaderViewType,HeaderDataType>?
  88. var footerSpec: FooterSpec<FooterViewType,FooterDataType>?
  89. var onConfigureCellForAction: ((ActionViewType,Action<ActionDataType>,IndexPath) -> ())?
  90. var onConfigureHeader: ((HeaderViewType,HeaderDataType) -> ())?
  91. var onConfigureSectionHeader: ((SectionHeaderViewType, SectionHeaderDataType) -> ())?
  92. var onConfigureFooter: ((FooterViewType,FooterDataType) -> ())?
  93. //MARK: - UI
  94. private lazy var backgroundView: UIView = {
  95. let backgroundView = UIView(frame: UIScreen.main.bounds)
  96. backgroundView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
  97. backgroundView.backgroundColor = self.settings.overView.coverViewColor
  98. if self.settings.behavior.hideOnTap {
  99. let tapGes = UITapGestureRecognizer(target: self, action: #selector(PopOverView.tapGestureDidRecognize(_:)))
  100. backgroundView.addGestureRecognizer(tapGes)
  101. }
  102. return backgroundView
  103. }()
  104. lazy var tableView: UITableView = {
  105. let tableView = UITableView(frame: .zero, style: .plain)
  106. tableView.bounces = self.settings.behavior.bounces
  107. tableView.autoresizingMask = [.flexibleHeight,.flexibleWidth]
  108. tableView.backgroundColor = UIColor.clear
  109. tableView.isScrollEnabled = self.settings.behavior.scrollEnable
  110. tableView.showsVerticalScrollIndicator = false
  111. tableView.estimatedRowHeight = 0
  112. tableView.estimatedSectionFooterHeight = 0
  113. tableView.estimatedSectionHeaderHeight = 0
  114. tableView.separatorStyle = .none
  115. tableView.dataSource = self
  116. tableView.delegate = self
  117. return tableView
  118. }()
  119. //MARK: - Initializers
  120. override init(frame: CGRect) {
  121. super.init(frame: frame)
  122. self.backgroundColor = settings.overView.backgroundColor
  123. self.addSubview(tableView)
  124. }
  125. required init?(coder aDecoder: NSCoder) {
  126. super.init(coder: aDecoder)
  127. self.backgroundColor = settings.overView.backgroundColor
  128. self.addSubview(tableView)
  129. }
  130. override func willMove(toSuperview newSuperview: UIView?) {
  131. super.willMove(toSuperview: newSuperview)
  132. initializers()
  133. }
  134. private func initializers() {
  135. if let cellSpec = self.cellSpec {
  136. switch cellSpec {
  137. case let .nibFile(nibName, bundle, _):
  138. tableView.register(UINib(nibName: nibName, bundle: bundle), forCellReuseIdentifier: ReusableViewIds.Cell.rawValue)
  139. case .cellClass:
  140. tableView.register(ActionViewType.self, forCellReuseIdentifier: ReusableViewIds.Cell.rawValue)
  141. }
  142. }
  143. if let headerSpec = headerSpec, let headerData = headerData {
  144. switch headerSpec {
  145. case let .nibFile(nibName, bundle, _):
  146. let bundle = bundle == nil ? Bundle.main : bundle
  147. let headerView = bundle?.loadNibNamed(nibName, owner: nil, options: nil)?.first as? UIView
  148. tableView.tableHeaderView = headerView
  149. onConfigureHeader?(headerView as! HeaderViewType,headerData)
  150. case .cellClass:
  151. tableView.tableHeaderView = HeaderViewType()
  152. }
  153. }
  154. if let footerSpec = footerSpec, let footerData = footerData {
  155. switch footerSpec {
  156. case let .nibFile(nibName, bundle, _):
  157. let bundle = bundle == nil ? Bundle.main : bundle
  158. let footView = bundle?.loadNibNamed(nibName, owner: nil, options: nil)?.first as? UIView
  159. tableView.tableFooterView = footView
  160. onConfigureFooter?(footView as! FooterViewType,footerData)
  161. case .cellClass:
  162. tableView.tableFooterView = FooterViewType()
  163. }
  164. }
  165. if let headerSpec = sectionHeaderSpec {
  166. switch headerSpec {
  167. case .cellClass:
  168. tableView.register(SectionHeaderViewType.self, forHeaderFooterViewReuseIdentifier: ReusableViewIds.SectionHeader.rawValue)
  169. case let .nibFile(nibName, bundle, _):
  170. tableView.register(UINib(nibName: nibName, bundle: bundle), forHeaderFooterViewReuseIdentifier: ReusableViewIds.SectionHeader.rawValue)
  171. }
  172. }
  173. }
  174. //MARK: - Public API
  175. func addAction(_ action: Action<ActionDataType>) {
  176. if let section = _sections.last {
  177. section.actions.append(action)
  178. }else{
  179. let section = Section<ActionDataType, SectionHeaderDataType>()
  180. addSection(section)
  181. section.actions.append(action)
  182. }
  183. }
  184. @discardableResult
  185. open func addSection(_ section: Section<ActionDataType, SectionHeaderDataType>) -> Section<ActionDataType, SectionHeaderDataType> {
  186. _sections.append(section)
  187. return section
  188. }
  189. // MARK: - Helpers
  190. func sectionForIndex(_ index: Int) -> Section<ActionDataType, SectionHeaderDataType>? {
  191. return _sections[index]
  192. }
  193. func actionForIndexPath(_ indexPath: IndexPath) -> Action<ActionDataType>? {
  194. return _sections[(indexPath as NSIndexPath).section].actions[(indexPath as NSIndexPath).item]
  195. }
  196. func actionIndexPathFor(_ indexPath: IndexPath) -> IndexPath {
  197. if hasHeader() {
  198. return IndexPath(item: (indexPath as NSIndexPath).item, section: (indexPath as NSIndexPath).section - 1)
  199. }
  200. return indexPath
  201. }
  202. private func actionSectionIndexFor(_ section: Int) -> Int {
  203. return hasHeader() ? section - 1 : section
  204. }
  205. override func layoutSubviews() {
  206. super.layoutSubviews()
  207. tableView.frame = CGRect(x: 0, y: isUpward ? settings.arrowView.height : 0, width: self.bounds.width, height: self.bounds.height - settings.arrowView.height)
  208. }
  209. //MARK: - Event handlers
  210. @objc func tapGestureDidRecognize(_ gesture: UITapGestureRecognizer) {
  211. dismiss()
  212. }
  213. //MARK: - Internal helpers
  214. func hasHeader() -> Bool {
  215. return headerData != nil && headerSpec != nil
  216. }
  217. func hasFooter() -> Bool {
  218. return footerData != nil && footerSpec != nil
  219. }
  220. private func numberOfSections() -> Int {
  221. return hasHeader() ? _sections.count + 1 : _sections.count
  222. }
  223. func show(pointView: UIView,_ completion:(() -> ())? = nil) {
  224. guard let pointViewRect = pointView.superview?.convert(pointView.frame, to: keyWindow) else { return }
  225. let pointViewUpLenth = pointViewRect.minY
  226. let pointViewDownLength = screenHeight - pointViewRect.maxY
  227. // 弹窗箭头指向的点
  228. var toPoint = CGPoint(x: pointViewRect.midX, y: 0)
  229. // 弹窗在 pointView 顶部
  230. if pointViewUpLenth > pointViewDownLength {
  231. if pointViewUpLenth > screenHeight - settings.arrowView.igoreOffest {
  232. toPoint.y = pointViewUpLenth - settings.arrowView.upOffest - settings.arrowView.targetOffest
  233. }else{
  234. toPoint.y = pointViewUpLenth - settings.arrowView.targetOffest
  235. }
  236. isUpward = false
  237. }else{
  238. toPoint.y = pointViewRect.maxY + settings.arrowView.targetOffest
  239. isUpward = true
  240. }
  241. show(toPoint: toPoint,completion:completion)
  242. }
  243. func show(tapPoint:CGPoint,completion:(() -> ())? = nil) {
  244. isUpward = tapPoint.y <= screenHeight - tapPoint.y
  245. show(toPoint: tapPoint,completion:completion)
  246. }
  247. func dismiss(isNeedAnimation:Bool = true,_ completion: (() -> ())? = nil) {
  248. let duration = isNeedAnimation ? settings.animation.duration : 0
  249. UIView.animate(withDuration: duration, animations: {
  250. self.alpha = 0.0
  251. self.backgroundView.alpha = 0.0
  252. self.transform = CGAffineTransform(scaleX: self.settings.animation.scale.width, y: self.settings.animation.scale.height)
  253. }) { (_) in
  254. self.backgroundView.removeFromSuperview()
  255. self.removeFromSuperview()
  256. if completion != nil {
  257. completion!()
  258. }
  259. }
  260. }
  261. private func show(toPoint:CGPoint,completion:(() -> ())? = nil) {
  262. //参数
  263. let edgeAlignment = settings.arrowView.edgeAlignment
  264. let arrowWidth = settings.arrowView.width
  265. let arrowHeight = settings.arrowView.height
  266. let viewCornerRadius = settings.overView.viewCornerRadius
  267. let arrowCornerRadius = settings.arrowView.arrowCornerRadius
  268. let arrowBottomCornerRadius = settings.arrowView.arrowBottomCornerRadius
  269. let horizontalMargin = settings.overView.horizontalMargin
  270. let verticalMargin = settings.overView.verticalMargin
  271. let viewWidth = settings.overView.viewWidth
  272. var toPoint = toPoint
  273. //如果不需要箭头边缘对齐
  274. if !edgeAlignment {
  275. let minHorizonalEdge = horizontalMargin + viewCornerRadius + arrowWidth/2
  276. if toPoint.x < minHorizonalEdge {
  277. toPoint.x = minHorizonalEdge
  278. }
  279. if screenWidth - toPoint.x < minHorizonalEdge {
  280. toPoint.x = screenWidth - minHorizonalEdge
  281. }
  282. }
  283. backgroundView.alpha = 0
  284. keyWindow!.addSubview(backgroundView)
  285. tableView.reloadData()
  286. let currentW = viewWidth
  287. var currentH = tableView.contentSize.height
  288. if let headerSpec = headerSpec,let headerData = headerData {
  289. currentH += headerSpec.height(headerData)
  290. }
  291. if let footerSpec = footerSpec,let footerData = footerData {
  292. currentH += footerSpec.height(footerData)
  293. }
  294. currentH += arrowHeight
  295. // 限制最高高度, 免得选项太多时超出屏幕
  296. let statusBarFrame = UIApplication.shared.statusBarFrame
  297. let maxHeight = isUpward ? screenHeight - toPoint.y - verticalMargin : toPoint.y - statusBarFrame.height
  298. if currentH > maxHeight {
  299. currentH = maxHeight
  300. tableView.isScrollEnabled = true
  301. }
  302. var currentX = toPoint.x - currentW/2 + horizontalMargin
  303. var currentY = toPoint.y
  304. var isLeft = false
  305. var isRight = false
  306. if edgeAlignment {
  307. isLeft = toPoint.x + currentW + horizontalMargin <= screenWidth
  308. isRight = horizontalMargin + currentW <= toPoint.x
  309. }else{
  310. isLeft = toPoint.x <= currentW/2 + horizontalMargin
  311. isRight = screenWidth - toPoint.x <= currentW/2 + horizontalMargin
  312. }
  313. // x: 窗口靠左
  314. if isLeft {
  315. currentX = edgeAlignment ? toPoint.x : horizontalMargin
  316. }
  317. // x: 窗口靠右
  318. if isRight {
  319. currentX = edgeAlignment ? toPoint.x - currentW : screenWidth - horizontalMargin - currentW
  320. }
  321. if !isUpward {
  322. currentY = toPoint.y - currentH
  323. }
  324. self.frame = CGRect(x: currentX, y: currentY, width: currentW, height: currentH)
  325. let arrowPoint = CGPoint(x: toPoint.x - self.frame.minX, y: isUpward ? 0 : currentH)
  326. let maskTop = isUpward ? arrowHeight : 0
  327. let maskBottom = isUpward ? currentH : currentH - arrowHeight
  328. let maskPath = UIBezierPath()
  329. if edgeAlignment && isLeft && isUpward {
  330. maskPath.move(to: CGPoint(x: 0, y: 0))
  331. }else{
  332. //左上圆角
  333. maskPath.move(to: CGPoint(x: 0, y: viewCornerRadius + maskTop))
  334. maskPath.addArc(withCenter: CGPoint(x: viewCornerRadius, y: viewCornerRadius + maskTop),
  335. radius: viewCornerRadius,
  336. startAngle: degreesToRadius(180),
  337. endAngle: degreesToRadius(270),
  338. clockwise: true)
  339. }
  340. // 箭头向上时的箭头位置
  341. if isUpward {
  342. let upX = edgeAlignment && (isLeft || isRight) ? arrowPoint.x : arrowPoint.x - arrowWidth/2
  343. maskPath.addLine(to: CGPoint(x: upX, y: arrowHeight))
  344. if edgeAlignment && (isLeft || isRight) {
  345. maskPath.addLine(to: arrowPoint)
  346. maskPath.addLine(to: CGPoint(x: arrowPoint.x + (isLeft ? arrowWidth/2 : -arrowWidth/2), y: arrowHeight))
  347. }else{
  348. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowCornerRadius, y: arrowCornerRadius),
  349. controlPoint: CGPoint(x: arrowPoint.x - arrowWidth/2 + arrowBottomCornerRadius, y: arrowHeight))
  350. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowCornerRadius, y: arrowCornerRadius),
  351. controlPoint: arrowPoint)
  352. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowWidth/2, y: arrowHeight),
  353. controlPoint: CGPoint(x: arrowPoint.x + arrowWidth/2 - arrowBottomCornerRadius, y: arrowHeight))
  354. }
  355. }
  356. if edgeAlignment && isRight && isUpward {
  357. maskPath.addLine(to: CGPoint(x: currentW, y: maskTop))
  358. }else{
  359. //右上圆角
  360. maskPath.addLine(to: CGPoint(x: currentW - viewCornerRadius, y: maskTop))
  361. maskPath.addArc(withCenter: CGPoint(x: currentW - viewCornerRadius, y: viewCornerRadius + maskTop),
  362. radius: viewCornerRadius,
  363. startAngle: degreesToRadius(270),
  364. endAngle: degreesToRadius(0),
  365. clockwise: true)
  366. }
  367. if edgeAlignment && isRight && !isUpward {
  368. maskPath.addLine(to: CGPoint(x: currentW, y: currentH - arrowHeight))
  369. }else{
  370. //右下圆角
  371. maskPath.addLine(to: CGPoint(x: currentW, y: maskBottom - viewCornerRadius))
  372. maskPath.addArc(withCenter: CGPoint(x: currentW - viewCornerRadius, y: maskBottom - viewCornerRadius),
  373. radius: viewCornerRadius,
  374. startAngle: degreesToRadius(0),
  375. endAngle: degreesToRadius(90),
  376. clockwise: true)
  377. }
  378. // 箭头向下时的箭头位置
  379. if !isUpward {
  380. let downX = edgeAlignment && (isLeft || isRight) ? arrowPoint.x : arrowPoint.x + arrowWidth/2
  381. maskPath.addLine(to: CGPoint(x: downX, y: currentH - arrowHeight))
  382. if edgeAlignment && (isLeft || isRight) {
  383. maskPath.addLine(to: arrowPoint)
  384. maskPath.addLine(to: CGPoint(x: arrowPoint.x - (isLeft ? -arrowWidth/2 : arrowWidth/2), y: currentH - arrowHeight))
  385. }else{
  386. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x + arrowCornerRadius, y: currentH - arrowCornerRadius),
  387. controlPoint: CGPoint(x: arrowPoint.x + arrowWidth/2 - arrowBottomCornerRadius, y: currentH - arrowHeight))
  388. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowCornerRadius, y: currentH - arrowCornerRadius),
  389. controlPoint: arrowPoint)
  390. maskPath.addQuadCurve(to: CGPoint(x: arrowPoint.x - arrowWidth/2, y: currentH - arrowHeight),
  391. controlPoint: CGPoint(x: arrowPoint.x - arrowWidth/2 + arrowBottomCornerRadius, y: currentH - arrowHeight))
  392. }
  393. }
  394. if edgeAlignment && isLeft && !isUpward {
  395. maskPath.addLine(to: CGPoint(x: 0, y: currentH - arrowHeight))
  396. }else{
  397. //左下圆角
  398. maskPath.addLine(to: CGPoint(x: viewCornerRadius, y: maskBottom))
  399. maskPath.addArc(withCenter: CGPoint(x:viewCornerRadius, y: maskBottom - viewCornerRadius),
  400. radius: viewCornerRadius,
  401. startAngle: degreesToRadius(90),
  402. endAngle: degreesToRadius(180),
  403. clockwise: true)
  404. }
  405. maskPath.close()
  406. // 截取圆角和箭头
  407. let maskLayer = CAShapeLayer()
  408. maskLayer.frame = self.bounds
  409. maskLayer.path = maskPath.cgPath
  410. self.layer.mask = maskLayer
  411. keyWindow!.addSubview(self)
  412. //弹出动画
  413. let oldFrame = self.frame
  414. self.layer.anchorPoint = CGPoint(x: arrowPoint.x/currentW, y: isUpward ? 0 : 1)
  415. self.frame = oldFrame
  416. self.transform = CGAffineTransform(scaleX: settings.animation.scale.width, y: settings.animation.scale.height)
  417. UIView.animate(withDuration: settings.animation.duration, animations: {
  418. self.transform = CGAffineTransform.identity
  419. self.backgroundView.alpha = 1
  420. }) { (_) in
  421. if let completion = completion {
  422. completion()
  423. }
  424. }
  425. }
  426. private func degreesToRadius(_ angle: CGFloat) -> CGFloat {
  427. return angle * CGFloat.pi / 180
  428. }
  429. //MARK: - UITableViewDataSources
  430. func numberOfSections(in tableView: UITableView) -> Int {
  431. return numberOfSections()
  432. }
  433. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  434. return _sections[section].actions.count
  435. }
  436. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  437. let action = actionForIndexPath(indexPath)
  438. let cell = tableView.dequeueReusableCell(withIdentifier: ReusableViewIds.Cell.rawValue, for: indexPath) as? ActionViewType
  439. self.onConfigureCellForAction?(cell!, action!, indexPath)
  440. return cell!
  441. }
  442. func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  443. if section == 0 && hasHeader() {
  444. return nil
  445. }else{
  446. let reusableView = tableView.dequeueReusableHeaderFooterView(withIdentifier: ReusableViewIds.SectionHeader.rawValue) as? SectionHeaderViewType
  447. onConfigureSectionHeader?(reusableView!, sectionForIndex(section)!.data!)
  448. return reusableView
  449. }
  450. }
  451. func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  452. if section == 0 {
  453. if let headerData = headerData, let headerSpec = headerSpec {
  454. return headerSpec.height(headerData)
  455. }else if let sectionHeaderSpec = sectionHeaderSpec, let section = sectionForIndex(actionSectionIndexFor(section)), let sectionData = section.data {
  456. return sectionHeaderSpec.height(sectionData)
  457. }
  458. }else if let sectionHeaderSpec = sectionHeaderSpec, let section = sectionForIndex(actionSectionIndexFor(section)), let sectionData = section.data {
  459. return sectionHeaderSpec.height(sectionData)
  460. }
  461. return 0
  462. }
  463. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  464. let action = actionForIndexPath(indexPath)
  465. if let actionData = action?.data,
  466. let cellSpec = self.cellSpec {
  467. return cellSpec.height(actionData)
  468. }
  469. return 0
  470. }
  471. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  472. let action = actionForIndexPath(indexPath)!
  473. self.dismiss(isNeedAnimation: settings.animation.tapShouldAnimated) {
  474. action.handler?(action)
  475. }
  476. }
  477. }