Skip to content

Instantly share code, notes, and snippets.

@lalkrishna
Created January 17, 2023 07:50
Show Gist options
  • Save lalkrishna/05388a71ccecec7757a16469a7895403 to your computer and use it in GitHub Desktop.
Save lalkrishna/05388a71ccecec7757a16469a7895403 to your computer and use it in GitHub Desktop.
MenuBuilder
class MenuBuilder {
var menu = [MenuItem]()
@discardableResult
func separator() -> Self {
menu.append(MenuItemSeparator()) // a different type of cell, contains a separator.
return self
}
@discardableResult
func items(_ items: MenuItemModel...) -> Self {
menu.append(contentsOf: items)
return self
}
@discardableResult
func item(iconNamed: String?, title: String, instance: ViewControllerConvertable) -> Self {
let item = MenuItemModel(
icon: iconNamed != nil ? UIImage(named: iconNamed!)?.withRenderingMode(.alwaysTemplate) : nil,
title: title,
instance: instance
)
menu.append(item)
return self
}
@discardableResult
func item(iconImage: UIImage?, title: String, instance: ViewControllerConvertable) -> Self {
let item = MenuItemModel(
icon: iconImage?.withRenderingMode(.alwaysTemplate),
title: title,
instance: instance
)
menu.append(item)
return self
}
}
protocol MenuItem { }
struct MenuItemSeparator: MenuItem { }
struct MenuItemModel: MenuItem {
let icon: UIImage?
let title: String
// var instance: Instantiatable.Type
fileprivate var instance: ViewControllerConvertable
var viewController: UIViewController {
instance.viewController
}
}
class MenuViewController: UIViewController, Instantiatable {
@IBOutlet weak var tableView: UITableView!
var onSelectItem: ((MenuItemModel) -> Void)?
private var items = [MenuItem]() {
didSet { tableView.reloadData() }
}
private func buildMenu() {
let builder = MenuBuilder()
builder
.item(iconNamed: "first", title: "First Menu", instance: ViewControllerInstantiator(FirstMenuViewController.self))
.separator()
// OR
builder.item(iconNamed: "menu-survey", title: "Survey", instance: ViewControllerInitialized {
let viewModel = SurveyListViewModel()
let sample = SurveyListViewController(viewModel: viewModel)
return sample
})
// Generate menu items.
items = builder.menu
}
}
// MARK: - Delegate, Datasource.
extension MenuViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
switch item {
case _ as MenuItemSeparator:
let cell: MenuItemSeparatorTableViewCell = tableView.reusableCell(for: indexPath)
return cell
case let item as MenuItemModel:
let cell: MenuItemTableViewCell = tableView.reusableCell(for: indexPath)
cell.configure(item)
return cell
default: fatalError()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
guard let menuItem = item as? MenuItemModel else { return }
onSelectItem?(menuItem)
}
}
protocol ViewControllerConvertable {
var viewController: UIViewController { get }
}
struct ViewControllerInstantiator: ViewControllerConvertable {
let instantiatableType: Instantiatable.Type // Refer: https://gist.github.com/lalkrishna/3770ce3933feaf4d7c0d3f1323e7fd72
internal init(_ instantiatableType: Instantiatable.Type) {
self.instantiatableType = instantiatableType
}
var viewController: UIViewController {
instantiatableType.instantiate() as! UIViewController // swiftlint:disable:this force_cast
}
}
struct ViewControllerInitialized: ViewControllerConvertable {
private let `internal`: () -> UIViewController
internal init(_ internal: @escaping () -> UIViewController) {
self.internal = `internal`
}
var viewController: UIViewController {
`internal`()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment