// // StaticTableView.swift // Private Analytics // // Created by Oleg Dreyman on 21.08.2020. // Copyright © 2020 Confirmed, Inc. All rights reserved. // import UIKit final class StaticTableView: UITableView { // Resizing UITableView to fit content override var contentSize: CGSize { didSet { invalidateIntrinsicContentSize() } } override var intrinsicContentSize: CGSize { layoutIfNeeded() return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } var rows: [SelectableTableViewCell] = [] var deselectsCellsAutomatically: Bool = false override init(frame: CGRect, style: UITableView.Style) { super.init(frame: frame, style: .insetGrouped) setup() } func setup() { dataSource = self delegate = self separatorStyle = .none } enum Insert { case last case dontInsert } @discardableResult func addRow(insert: Insert = .last, _ configure: (UIView) -> ()) -> SelectableTableViewCell { let cell = SelectableTableViewCell() cell.selectionStyle = .none cell.backgroundColor = nil configure(cell.contentView) self.insert(cell: cell, insert: insert) return cell } @discardableResult func addRowCell(insert: Insert = .last, _ configure: (UITableViewCell) -> ()) -> SelectableTableViewCell { let cell = SelectableTableViewCell() cell.selectionStyle = .none configure(cell) self.insert(cell: cell, insert: insert) return cell } @discardableResult func addCell(insert: Insert = .last, _ cell: SelectableTableViewCell) -> SelectableTableViewCell { self.insert(cell: cell, insert: insert) return cell } @discardableResult func addRow(insert: Insert = .last, view: UIView, insets: UIEdgeInsets = .zero) -> SelectableTableViewCell { return addRow(insert: insert) { (row) in row.addSubview(view) view.anchors.edges.pin(axis: .vertical) view.anchors.edges.marginsPin(insets: insets, axis: .horizontal) } } private func insert(cell: SelectableTableViewCell, insert: Insert) { switch insert { case .dontInsert: break case .last: rows.append(cell) } } func clear() { rows = [] } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } class SelectableTableViewCell: UITableViewCell { var selectionCallback: () -> () = { } var deletionCallback: (() -> ())? enum Action { case toggleCheckmark } @discardableResult func onSelect(callback: @escaping () -> ()) -> Self { selectionStyle = .default selectionCallback = callback return self } @discardableResult func onSwipeToDelete(callback: @escaping () -> ()) -> Self { deletionCallback = callback return self } @discardableResult func onSelect(_ action: Action, callback: @escaping () -> () = { }) -> Self { selectionCallback = { [unowned self] in switch action { case .toggleCheckmark: if self.accessoryType == .checkmark { self.accessoryType = .none } else { self.accessoryType = .checkmark } } callback() } return self } } extension StaticTableView: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 0 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rows.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return rows[indexPath.row] } func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { let cell = rows[indexPath.row] return cell.deletionCallback != nil } func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { return .delete } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { let cell = rows[indexPath.row] guard cell.deletionCallback != nil else { return } cell.deletionCallback?() self.rows.removeAll(where: { $0 === cell }) self.deleteRows(at: [indexPath], with: .fade) } } extension StaticTableView: UITableViewDelegate { func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { if let cell = tableView.cellForRow(at: indexPath) as? SelectableTableViewCell { return cell.selectionStyle != .none } else { return false } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath) as? SelectableTableViewCell { cell.selectionCallback() if deselectsCellsAutomatically { tableView.deselectRow(at: indexPath, animated: true) } } } } extension UIViewController { func addTableView(_ tableView: UITableView, layout: (UIView) -> ()) { // adding UITableView as UITableViewController will enable // UIKit's own "scroll to text field when keyboard appears" let tableViewController = StaticTableViewController() tableViewController.tableView = tableView view.addSubview(tableViewController.view) layout(tableView) addChild(tableViewController) tableViewController.didMove(toParent: self) } } final class StaticTableViewController: UITableViewController { }