import UIKit @MainActor final class NavigationSettingsViewController: UITableViewController { private let context: AppContext private var selected: [PrimaryDestination] init(context: AppContext) { self.context = context selected = context.primaryDestinations super.init(style: .insetGrouped) title = "Primary Navigation" navigationItem.rightBarButtonItem = editButtonItem } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") } private var available: [PrimaryDestination] { PrimaryDestination.allCases.filter { !selected.contains($0) } } override func numberOfSections(in tableView: UITableView) -> Int { 2 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { section == 0 ? selected.count : available.count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { section == 0 ? "Shown After Home" : "Available on Home" } override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { section == 0 ? "Choose up to four destinations. Tap Edit to reorder them. Home is always first." : "Destinations not in the tab bar remain available as buttons on Home." } override func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell { let destination = indexPath.section == 0 ? selected[indexPath.row] : available[indexPath.row] let cell = UITableViewCell(style: .default, reuseIdentifier: nil) var content = cell.defaultContentConfiguration() content.text = destination.title content.image = UIImage(systemName: destination.symbolName) content.imageProperties.tintColor = .tintColor if indexPath.section == 1 && selected.count == 4 { content.textProperties.color = .secondaryLabel content.imageProperties.tintColor = .tertiaryLabel cell.selectionStyle = .none } else if indexPath.section == 1 { let add = UIImageView(image: UIImage(systemName: "plus.circle.fill")) add.tintColor = .tintColor cell.accessoryView = add cell.accessibilityHint = "Adds this destination to the tab bar" } cell.contentConfiguration = content return cell } override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { indexPath.section == 0 } override func tableView( _ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath ) { guard sourceIndexPath.section == 0, destinationIndexPath.section == 0 else { tableView.reloadData() return } let destination = selected.remove(at: sourceIndexPath.row) selected.insert(destination, at: destinationIndexPath.row) save() } override func tableView( _ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath ) -> IndexPath { guard proposedDestinationIndexPath.section == 0 else { return IndexPath(row: max(selected.count - 1, 0), section: 0) } return proposedDestinationIndexPath } override func tableView( _ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath ) -> UITableViewCell.EditingStyle { indexPath.section == 0 ? .delete : .none } override func tableView( _ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath ) { guard editingStyle == .delete, indexPath.section == 0 else { return } selected.remove(at: indexPath.row) save() tableView.reloadData() } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) guard indexPath.section == 1, selected.count < 4 else { return } selected.append(available[indexPath.row]) save() tableView.reloadData() } private func save() { do { try context.core.setPrimaryDestinations(destinations: selected) context.applyPrimaryDestinations() } catch { selected = context.primaryDestinations tableView.reloadData() show(error: error) } } }