UIKit Catalog: Creating and customizing views and controls
At a glance
| Item | Summary |
|---|---|
| Purpose | Organize many UIKit control demonstrations behind a data-driven outline and reusable table-case configuration model. |
| App architecture | OutlineViewController turns an OutlineItem hierarchy into a diffable sidebar and routes leaf storyboard names to feature controllers; most feature tables inherit BaseTableViewController and describe control setup with CaseElement closures. |
| Main patterns | Data-driven feature router, Template base controller, Typed configuration closure |
| Project style | Thirty-five Swift files; one catalog/router, a shared table template, and many focused UIKit feature controllers and storyboards. |
Project structure
Source bundle/
└── UIKitCatalog/
├── AlertControllerViewController.swift
├── AppDelegate.swift
├── MenuButtonViewController.swift
├── OutlineViewController.swift
├── PickerViewController.swift
├── PointerInteractionButtonViewController.swift
├── TextFieldViewController.swift
├── ActivityIndicatorViewController.swift
├── ButtonViewController.swift
├── ProgressViewController.swift
├── SegmentedControlViewController.swift
└── SliderViewController.swift
Structure observations
- The outline hierarchy is the navigation registry; app/scene delegates only establish lifecycle and initial UI.
- Feature controllers remain independent examples instead of a single monolithic switch over every control.
CaseElementpreserves each target view type at construction, then erases it to a uniformUIViewconfiguration closure for the base table.
Overall architecture
flowchart LR
ITEMS["OutlineItem hierarchy"] --> OUTLINE["OutlineViewController"]
OUTLINE --> SNAPSHOT["Diffable outline snapshot"]
SNAPSHOT --> SELECT["Selected leaf storyboardName"]
SELECT --> STORYBOARD["Feature storyboard controller"]
STORYBOARD --> BASE["BaseTableViewController"]
BASE --> CASES["CaseElement array"]
CASES --> CONTROL["Typed UIKit control configuration"]
Reference code
UIKitCatalog/OutlineViewController.swift:303 — leaf-to-feature routing
private func pushOrPresentStoryboard(storyboardName: String) {
let exampleStoryboard = UIStoryboard(name: storyboardName, bundle: nil)
if let exampleViewController = exampleStoryboard.instantiateInitialViewController() {
pushOrPresentViewController(viewController: exampleViewController)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let menuItem = dataSource.itemIdentifier(for: indexPath) else { return }
if let storyboardName = menuItem.storyboardName {
pushOrPresentStoryboard(storyboardName: storyboardName)
}
}Interpretation
Navigation and control configuration are both data-driven, at different scales. The outline routes to a feature, while each table feature supplies small typed configuration cases to shared table plumbing. This keeps catalog mechanics reusable without abstracting the individual UIKit APIs away.
Ownership and state
classDiagram
OutlineViewController *-- OutlineItemArray : menuItems
OutlineItem *-- OutlineItemArray : subitems
OutlineViewController *-- UICollectionView : creates
OutlineViewController *-- DiffableDataSource : creates
BaseTableViewController *-- CaseElementArray : testCells
CaseElement *-- ConfigurationClosure : configHandler
FeatureController --|> BaseTableViewController
Ownership evidence
UIKitCatalog/OutlineViewController.swift:185 — private navigation registry
private lazy var menuItems: [OutlineItem] = {
return [
controlsOutlineItem,
viewsOutlineItem,
pickersOutlineItem
]
}()
private func configureCollectionView() {
let collectionView = UICollectionView(
frame: view.bounds, collectionViewLayout: generateLayout())
self.outlineCollectionView = collectionView
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
OutlineViewController |
Menu hierarchy, collection view, diffable data source | Builds and retains all three | It creates snapshots and chooses push-versus-detail routing. |
Each OutlineItem |
Title, image name, optional storyboard, child items | Value owns its child array | It supplies stable identity and route metadata. |
BaseTableViewController |
testCells |
Subclasses populate the array inherited by the table template | The base class owns data-source rendering; feature subclasses own case selection. |
CaseElement |
Title, reuse ID, erased configuration closure | Stores the escaping closure value | The closure configures only the typed target view captured at initialization. |
Composition denotes source-visible construction and retained value state. Aggregation denotes a stored or injected collaborator whose exclusive lifetime the source does not prove.
Class and protocol design
UIKitCatalog/CaseElement.swift:10 — generic-to-erased case configuration
struct CaseElement {
typealias ConfigurationClosure = (UIView) -> Void
var configHandler: ConfigurationClosure
init<V: UIView>(title: String, cellID: String,
configHandler: @escaping (V) -> Void) {
self.title = title
self.cellID = cellID
self.configHandler = { view in
guard let view = view as? V else { fatalError("Impossible") }
configHandler(view)
}
}
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
OutlineViewController.OutlineItem |
Navigation node and stable diffable identifier | Hashable through explicit UUID identity |
OutlineViewController |
Builds outline snapshots and routes leaf selection | UIViewController, UICollectionViewDelegate |
BaseTableViewController |
Renders one CaseElement per table section |
UITableViewController template superclass |
CaseElement |
Carries feature-specific view configuration through a uniform base API | Generic initializer plus erased (UIView) -> Void closure |
| Feature view controllers | Demonstrate one UIKit control family | Usually inherit BaseTableViewController; some use specialized controllers |
The catalog relies on UIKit data-source/delegate callbacks and inheritance. It defines no local protocol abstraction; variability is represented by route values, subclasses, and configuration closures.
Access control
| Symbol | Access | Verified effect | Design reason |
|---|---|---|---|
OutlineViewController.menuItems |
private |
Only the outline implementation can read or replace the route registry | Prevents unrelated features from mutating navigation identity behind the diffable source. |
| Outline construction/routing helpers | private |
Callable only in the controller’s lexical scope | Snapshot and presentation policy stay behind selection callbacks. |
BaseTableViewController.testCells |
implicit internal |
Feature subclasses in other files can populate it | It is intentionally a subclass customization point inside the app module. |
CaseElement and feature controllers |
implicit internal |
Available across the application target | No external package needs the catalog’s teaching types. |
Reference code
UIKitCatalog/OutlineViewController.swift:199 — private outline implementation helpers
private func configureCollectionView() {
let collectionView = UICollectionView(
frame: view.bounds, collectionViewLayout: generateLayout())
view.addSubview(collectionView)
self.outlineCollectionView = collectionView
collectionView.delegate = self
}
private func configureDataSource() {
// Cell registrations and the diffable provider stay local.
}The shared base class leaves testCells internal because cross-file subclasses must set it. The outline’s registry and mechanics are private because they have no such collaborator. No public, open, or fileprivate API is present.
Logic ownership and placement
| Logic | Owning type or file | Why it lives there |
|---|---|---|
| Catalog hierarchy, snapshot, and route selection | OutlineViewController |
It owns both route metadata and adaptive split/navigation presentation. |
| Generic table-section rendering | BaseTableViewController |
All table-based examples share the same cell/header algorithm. |
| Control-specific configuration | Feature controllers via CaseElement closures |
The feature knows the exact UIKit type and desired API variants. |
| Typed closure adaptation | CaseElement |
The generic initializer validates the target once and exposes a uniform stored shape. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Data-driven feature router | UIKitCatalog/OutlineViewController.swift:245 |
Uses hierarchical stable values for both navigation UI and destination metadata. |
| Template base controller | UIKitCatalog/BaseTableViewController.swift:10 |
Shares table plumbing while feature subclasses provide case data. |
| Typed configuration closure | UIKitCatalog/CaseElement.swift:17 |
Retains type-safe authoring but erases closures for heterogeneous storage. |
Naming conventions
- Feature types name the demonstrated UIKit API (
ButtonViewController,SliderViewController,VisualEffectViewController). - Shared types name their catalog role (
OutlineItem,CaseElement,BaseTableViewController). - Routing metadata uses
storyboardName; commands use concrete navigation verbs (pushOrPresentStoryboard). - Most files follow one primary feature-controller type, with button configurations split into a focused extension file.
Architecture takeaways
- Represent a large sample catalog as route data, not an app-delegate dependency graph.
- Use a base controller only for truly repeated table mechanics; leave API-specific setup in feature controllers.
- A generic initializer plus erased closure can store heterogeneous UIKit configurations without a protocol hierarchy.
Source map
| Source file | Architectural role |
|---|---|
UIKitCatalog/OutlineViewController.swift |
Navigation hierarchy, diffable sidebar, and feature routing |
UIKitCatalog/BaseTableViewController.swift |
Shared table rendering template |
UIKitCatalog/CaseElement.swift |
Typed control-configuration case abstraction |
UIKitCatalog/ButtonViewController.swift / UIKitCatalog/ButtonViewController+Configs.swift |
Representative feature and configuration split |
UIKitCatalog/AlertControllerViewController.swift |
Representative specialized UIKit example |
UIKitCatalog/AppDelegate.swift / UIKitCatalog/SceneDelegate.swift |
Application and scene lifecycle only |