// // TablePresenterViewController.swift // GME Remit // // Created by InKwon Devik Kim on 26/06/2019. //Copyright © 2019 Gobal Money Express Co. Ltd. All rights reserved. // import UIKit import IQKeyboardManagerSwift struct TablePresenterConfiguration { var presenterTitle: String var closeButtonTitle: String var notFoundTitle: String var searchBarPlaceHolder: String init(presenterTitle: String) { self.presenterTitle = presenterTitle closeButtonTitle = "penny_test_close_text".localized() notFoundTitle = "no_result_found_text".localized() searchBarPlaceHolder = "search_text".localized() } init( presenterTitle: String, closeButtonTitle: String, notFoundTitle: String, searchBarPlaceHolder: String ) { self.presenterTitle = presenterTitle self.closeButtonTitle = closeButtonTitle self.notFoundTitle = notFoundTitle self.searchBarPlaceHolder = searchBarPlaceHolder } } protocol TablePresenterDelegate: class { func tablePresenterView( _ viewController: TablePresenterViewController ) -> TablePresenterConfiguration func tablePresenterView( _ viewController: TablePresenterViewController, didSelectModel model: TablePresenterProtocol? ) } class TablePresenterViewController: UIViewController { // MARK: Properties var presenter: TablePresenterModuleInterface? weak var delegate: TablePresenterDelegate? var type: TablePresenterType = .default private var dynamicHeight: CGFloat = 0 private var cellHeight: CGFloat = 60 private let impact = UISelectionFeedbackGenerator() private var isFirst = true private lazy var originPoint = CGPoint(x: 0, y: 0) private lazy var isDismissAnimation = false // MARK: Computed Properties var model: [TablePresenterProtocol]? { didSet { if model?.count == 0 { notFoundLabel.isHidden = false notFoundLabel.alpha = 1.0 } else { notFoundLabel.isHidden = true notFoundLabel.alpha = 0.0 } tableView.reloadData() if isFirst { switch type { case .branches: searchBarContainerView.isHidden = false dynamicHeight = view.frame.height * 0.8 default : searchBarContainerView.isHidden = (model?.count ?? 0) < 1 ? true : false dynamicHeight = calculateHeight(count: model?.count ?? 0) } isFirst = false } } } // MARK: IBOutlets @IBOutlet private weak var titleLabel: UILabel! @IBOutlet private weak var searchBarContainerView: UIView! @IBOutlet private weak var searchBar: UISearchBar! @IBOutlet private weak var tableView: UITableView! @IBOutlet private weak var closeButton: UIButton! @IBOutlet private weak var mainView: UIView! @IBOutlet private weak var notFoundLabel: UILabel! @IBOutlet private weak var transparentView: UIView! @IBOutlet private weak var heightConstraint: NSLayoutConstraint! // MARK: VC's Life cycle override func viewDidLoad() { super.viewDidLoad() setup() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) searchBar.keyboardType = .asciiCapable setConfiguration() mainView.bottomToOrigin(duration: 0.1) {[weak self] in guard let `self` = self else { return } if !self.searchBarContainerView.isHidden { if self.searchBar.canBecomeFirstResponder { self.searchBar.becomeFirstResponder() } } } IQKeyboardManager.shared.enable = false heightConstraint.constant = (dynamicHeight == 0) ? view.frame.height * 0.8 : dynamicHeight impact.selectionChanged() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) originPoint = mainView.frame.origin } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) IQKeyboardManager.shared.enable = true view.endEditing(true) } // MARK: IBActions @IBAction func touchCloseButton(_ sender: UIButton) { presentingViewController?.view.endEditing(true) mainView.originToBottom { // self.delegate?.tablePresenterView(self, didSelectModel: nil) self.dismiss(animated: true) } } private func calculateHeight(count: Int) -> CGFloat { let calculatedHeight = CGFloat(count) * cellHeight + (view.frame.height - 200) return min(calculatedHeight, view.frame.height * 0.8) } } // MARK: TablePresenterViewInterface extension TablePresenterViewController: TablePresenterViewInterface { func setModel(with model: [TablePresenterProtocol]?) { self.model = model } func setError(with error: Error) { // alert(message: error.localizedDescription) notFoundLabel.text = error.localizedDescription self.model?.removeAll() } } // MARK: Other Functions extension TablePresenterViewController { private func setup() { mainView.layer.cornerRadius = 5 searchBar.delegate = self tableView.delegate = self tableView.dataSource = self let tapGestureRecognizer = UITapGestureRecognizer() tapGestureRecognizer.addTarget(self, action: #selector(tapGesture(_:))) tapGestureRecognizer.delegate = self transparentView.addGestureRecognizer(tapGestureRecognizer) presenter?.fetchModel(type: type) transparentView.backgroundColor = .themeBackgroundGray // let panGesture = UIPanGestureRecognizer(target: self,action: #selector(onDrag(_:))) // panGesture.delegate = self // // tableView.bounces = false // tableView.addGestureRecognizer(panGesture) } private func setConfiguration() { let configuration = delegate?.tablePresenterView(self) titleLabel.text = configuration?.presenterTitle notFoundLabel.text = configuration?.notFoundTitle searchBar.placeholder = configuration?.presenterTitle // searchBar.isHidden = !(configuration?.isUseSearchBar ?? true) } @objc func onDrag(_ sender: UIPanGestureRecognizer) { if tableView.contentOffset.y == 0 && sender.velocity(in: tableView).y > 0 { isDismissAnimation = true let percentThreshold: CGFloat = 0.5 let translation = sender.translation(in: mainView) let newY = ensureRange( value: mainView.frame.minY + translation.y, minimum: 0, maximum: mainView.frame.maxY ) let progress = progressAlongAxis(newY, mainView.bounds.height) mainView.frame.origin.y = newY if sender.state == .ended { let velocity = sender.velocity(in: mainView) if velocity.y >= 200 || progress > percentThreshold { self.touchCloseButton(UIButton()) } else { UIView.animate(withDuration: 0.2, animations: { self.mainView.frame.origin.y = self.originPoint.y }) } } sender.setTranslation(.zero, in: mainView) } else if isDismissAnimation { isDismissAnimation = false UIView.animate(withDuration: 0.2, animations: { self.mainView.frame.origin.y = self.originPoint.y }) } } func progressAlongAxis(_ pointOnAxis: CGFloat, _ axisLength: CGFloat) -> CGFloat { let movementOnAxis = pointOnAxis / axisLength let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0) let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0) return CGFloat(positiveMovementOnAxisPercent) } func ensureRange(value: T, minimum: T, maximum: T) -> T where T : Comparable { return min(max(value, minimum), maximum) } @objc private func tapGesture(_ sender: UITapGestureRecognizer) { touchCloseButton(UIButton()) } } // MARK: - UITableViewDataSource extension TablePresenterViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return cellHeight } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return model?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell") as? TableCell else { return UITableViewCell() } cell.setModel(model?[indexPath.row]) return cell } } // MARK: - UITableViewDelegate extension TablePresenterViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { mainView.originToBottom {[weak self] in guard let `self` = self else { return } self.delegate?.tablePresenterView(self, didSelectModel: self.model?[indexPath.row]) self.dismiss(animated: true) } } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { view.endEditing(true) } } // MARK: - UIGestureRecognizerDelegate extension TablePresenterViewController: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { guard let tapRecognizer = gestureRecognizer as? UITapGestureRecognizer else { return true } return touch.view == tapRecognizer.view } func gestureRecognizer( _ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer ) -> Bool { return true } } extension TablePresenterViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { print("\(searchText)") guard let filteredSearchText = searchText.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return } presenter?.filterModel(word: filteredSearchText, type: type) } }