You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
9.6 KiB

5 years ago
  1. //
  2. // TablePresenterViewController.swift
  3. // GME Remit
  4. //
  5. // Created by InKwon Devik Kim on 26/06/2019.
  6. //Copyright © 2019 Gobal Money Express Co. Ltd. All rights reserved.
  7. //
  8. import UIKit
  9. import IQKeyboardManagerSwift
  10. struct TablePresenterConfiguration {
  11. var presenterTitle: String
  12. var closeButtonTitle: String
  13. var notFoundTitle: String
  14. var searchBarPlaceHolder: String
  15. init(presenterTitle: String) {
  16. self.presenterTitle = presenterTitle
  17. closeButtonTitle = "penny_test_close_text".localized()
  18. notFoundTitle = "no_result_found_text".localized()
  19. searchBarPlaceHolder = "search_text".localized()
  20. }
  21. init(
  22. presenterTitle: String,
  23. closeButtonTitle: String,
  24. notFoundTitle: String,
  25. searchBarPlaceHolder: String
  26. ) {
  27. self.presenterTitle = presenterTitle
  28. self.closeButtonTitle = closeButtonTitle
  29. self.notFoundTitle = notFoundTitle
  30. self.searchBarPlaceHolder = searchBarPlaceHolder
  31. }
  32. }
  33. protocol TablePresenterDelegate: class {
  34. func tablePresenterView(
  35. _ viewController: TablePresenterViewController
  36. ) -> TablePresenterConfiguration
  37. func tablePresenterView(
  38. _ viewController: TablePresenterViewController,
  39. didSelectModel model: TablePresenterProtocol?
  40. )
  41. }
  42. class TablePresenterViewController: UIViewController {
  43. // MARK: Properties
  44. var presenter: TablePresenterModuleInterface?
  45. weak var delegate: TablePresenterDelegate?
  46. var type: TablePresenterType = .default
  47. private var dynamicHeight: CGFloat = 0
  48. private var cellHeight: CGFloat = 60
  49. private let impact = UISelectionFeedbackGenerator()
  50. private var isFirst = true
  51. private lazy var originPoint = CGPoint(x: 0, y: 0)
  52. private lazy var isDismissAnimation = false
  53. // MARK: Computed Properties
  54. var model: [TablePresenterProtocol]? {
  55. didSet {
  56. if model?.count == 0 {
  57. notFoundLabel.isHidden = false
  58. notFoundLabel.alpha = 1.0
  59. } else {
  60. notFoundLabel.isHidden = true
  61. notFoundLabel.alpha = 0.0
  62. }
  63. tableView.reloadData()
  64. if isFirst {
  65. switch type {
  66. case .branches:
  67. searchBarContainerView.isHidden = false
  68. dynamicHeight = view.frame.height * 0.8
  69. default :
  70. searchBarContainerView.isHidden = (model?.count ?? 0) < 1 ? true : false
  71. dynamicHeight = calculateHeight(count: model?.count ?? 0)
  72. }
  73. isFirst = false
  74. }
  75. }
  76. }
  77. // MARK: IBOutlets
  78. @IBOutlet private weak var titleLabel: UILabel!
  79. @IBOutlet private weak var searchBarContainerView: UIView!
  80. @IBOutlet private weak var searchBar: UISearchBar!
  81. @IBOutlet private weak var tableView: UITableView!
  82. @IBOutlet private weak var closeButton: UIButton!
  83. @IBOutlet private weak var mainView: UIView!
  84. @IBOutlet private weak var notFoundLabel: UILabel!
  85. @IBOutlet private weak var transparentView: UIView!
  86. @IBOutlet private weak var heightConstraint: NSLayoutConstraint!
  87. // MARK: VC's Life cycle
  88. override func viewDidLoad() {
  89. super.viewDidLoad()
  90. setup()
  91. }
  92. override func viewWillAppear(_ animated: Bool) {
  93. super.viewWillAppear(true)
  94. searchBar.keyboardType = .asciiCapable
  95. setConfiguration()
  96. mainView.bottomToOrigin(duration: 0.1) {[weak self] in
  97. guard let `self` = self else { return }
  98. if !self.searchBarContainerView.isHidden {
  99. if self.searchBar.canBecomeFirstResponder {
  100. self.searchBar.becomeFirstResponder()
  101. }
  102. }
  103. }
  104. IQKeyboardManager.shared.enable = false
  105. heightConstraint.constant = (dynamicHeight == 0) ? view.frame.height * 0.8 : dynamicHeight
  106. impact.selectionChanged()
  107. }
  108. override func viewDidAppear(_ animated: Bool) {
  109. super.viewDidAppear(animated)
  110. originPoint = mainView.frame.origin
  111. }
  112. override func viewWillDisappear(_ animated: Bool) {
  113. super.viewWillDisappear(animated)
  114. IQKeyboardManager.shared.enable = true
  115. view.endEditing(true)
  116. }
  117. // MARK: IBActions
  118. @IBAction func touchCloseButton(_ sender: UIButton) {
  119. presentingViewController?.view.endEditing(true)
  120. mainView.originToBottom {
  121. // self.delegate?.tablePresenterView(self, didSelectModel: nil)
  122. self.dismiss(animated: true)
  123. }
  124. }
  125. private func calculateHeight(count: Int) -> CGFloat {
  126. let calculatedHeight = CGFloat(count) * cellHeight + (view.frame.height - 200)
  127. return min(calculatedHeight, view.frame.height * 0.8)
  128. }
  129. }
  130. // MARK: TablePresenterViewInterface
  131. extension TablePresenterViewController: TablePresenterViewInterface {
  132. func setModel(with model: [TablePresenterProtocol]?) {
  133. self.model = model
  134. }
  135. func setError(with error: Error) {
  136. // alert(message: error.localizedDescription)
  137. notFoundLabel.text = error.localizedDescription
  138. self.model?.removeAll()
  139. }
  140. }
  141. // MARK: Other Functions
  142. extension TablePresenterViewController {
  143. private func setup() {
  144. mainView.layer.cornerRadius = 5
  145. searchBar.delegate = self
  146. tableView.delegate = self
  147. tableView.dataSource = self
  148. let tapGestureRecognizer = UITapGestureRecognizer()
  149. tapGestureRecognizer.addTarget(self, action: #selector(tapGesture(_:)))
  150. tapGestureRecognizer.delegate = self
  151. transparentView.addGestureRecognizer(tapGestureRecognizer)
  152. presenter?.fetchModel(type: type)
  153. transparentView.backgroundColor = .themeBackgroundGray
  154. // let panGesture = UIPanGestureRecognizer(target: self,action: #selector(onDrag(_:)))
  155. // panGesture.delegate = self
  156. //
  157. // tableView.bounces = false
  158. // tableView.addGestureRecognizer(panGesture)
  159. }
  160. private func setConfiguration() {
  161. let configuration = delegate?.tablePresenterView(self)
  162. titleLabel.text = configuration?.presenterTitle
  163. notFoundLabel.text = configuration?.notFoundTitle
  164. searchBar.placeholder = configuration?.presenterTitle
  165. // searchBar.isHidden = !(configuration?.isUseSearchBar ?? true)
  166. }
  167. @objc func onDrag(_ sender: UIPanGestureRecognizer) {
  168. if tableView.contentOffset.y == 0 && sender.velocity(in: tableView).y > 0 {
  169. isDismissAnimation = true
  170. let percentThreshold: CGFloat = 0.5
  171. let translation = sender.translation(in: mainView)
  172. let newY = ensureRange(
  173. value: mainView.frame.minY + translation.y,
  174. minimum: 0,
  175. maximum: mainView.frame.maxY
  176. )
  177. let progress = progressAlongAxis(newY, mainView.bounds.height)
  178. mainView.frame.origin.y = newY
  179. if sender.state == .ended {
  180. let velocity = sender.velocity(in: mainView)
  181. if velocity.y >= 200 || progress > percentThreshold {
  182. self.touchCloseButton(UIButton())
  183. } else {
  184. UIView.animate(withDuration: 0.2, animations: {
  185. self.mainView.frame.origin.y = self.originPoint.y
  186. })
  187. }
  188. }
  189. sender.setTranslation(.zero, in: mainView)
  190. } else if isDismissAnimation {
  191. isDismissAnimation = false
  192. UIView.animate(withDuration: 0.2, animations: {
  193. self.mainView.frame.origin.y = self.originPoint.y
  194. })
  195. }
  196. }
  197. func progressAlongAxis(_ pointOnAxis: CGFloat, _ axisLength: CGFloat) -> CGFloat {
  198. let movementOnAxis = pointOnAxis / axisLength
  199. let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0)
  200. let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0)
  201. return CGFloat(positiveMovementOnAxisPercent)
  202. }
  203. func ensureRange<T>(value: T, minimum: T, maximum: T) -> T where T : Comparable {
  204. return min(max(value, minimum), maximum)
  205. }
  206. @objc private func tapGesture(_ sender: UITapGestureRecognizer) {
  207. touchCloseButton(UIButton())
  208. }
  209. }
  210. // MARK: - UITableViewDataSource
  211. extension TablePresenterViewController: UITableViewDataSource {
  212. func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  213. return cellHeight
  214. }
  215. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  216. return model?.count ?? 0
  217. }
  218. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  219. guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell") as? TableCell else {
  220. return UITableViewCell()
  221. }
  222. cell.setModel(model?[indexPath.row])
  223. return cell
  224. }
  225. }
  226. // MARK: - UITableViewDelegate
  227. extension TablePresenterViewController: UITableViewDelegate {
  228. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  229. mainView.originToBottom {[weak self] in guard let `self` = self else { return }
  230. self.delegate?.tablePresenterView(self, didSelectModel: self.model?[indexPath.row])
  231. self.dismiss(animated: true)
  232. }
  233. }
  234. func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
  235. view.endEditing(true)
  236. }
  237. }
  238. // MARK: - UIGestureRecognizerDelegate
  239. extension TablePresenterViewController: UIGestureRecognizerDelegate {
  240. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
  241. guard let tapRecognizer = gestureRecognizer as? UITapGestureRecognizer else {
  242. return true
  243. }
  244. return touch.view == tapRecognizer.view
  245. }
  246. func gestureRecognizer(
  247. _ gestureRecognizer: UIGestureRecognizer,
  248. shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
  249. ) -> Bool {
  250. return true
  251. }
  252. }
  253. extension TablePresenterViewController: UISearchBarDelegate {
  254. func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
  255. print("\(searchText)")
  256. guard let filteredSearchText = searchText.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
  257. presenter?.filterModel(word: filteredSearchText, type: type)
  258. }
  259. }