Changing the appearance of selected and highlighted cells
At a glance
| Item |
Summary |
| Purpose |
Demonstrates separate visual feedback for the transient highlighted state and persistent selected state of collection-view cells. |
| App architecture |
A storyboard connects a collection view to one controller acting as data source/delegate; a custom cell owns reusable visual-state presentation. |
| Main patterns |
Framework-managed cell state, delegate callbacks, and reusable-cell encapsulation. |
| Project style |
Deliberately small UIKit sample: four Swift lifecycle/UI files and no domain/service layer. |
Project structure
CollectionViewManagingVisualStates/
├── AppDelegate.swift
├── SceneDelegate.swift
├── ViewController.swift
├── CustomCollectionViewCell.swift
└── Base.lproj/
├── Main.storyboard
└── LaunchScreen.storyboard
Structure observations
ViewController contains collection callbacks but no stored feature state.
CustomCollectionViewCell contains the visual implementation reused by all 25 cells.
- The storyboard supplies collection layout, reuse registration, outlets, and delegate/data-source connections.
Overall architecture
flowchart LR
Storyboard["Main.storyboard"] --> Controller["ViewController"]
Controller -->|"data source: dequeue"| Collection["UICollectionView"]
Collection --> Cell["CustomCollectionViewCell"]
Touch["Touch input"] --> Collection
Collection -->|"highlight callbacks"| Controller
Controller -->|"content background"| Cell
Collection -->|"selection callbacks"| Controller
Controller -->|"show / hide icon"| Cell
Collection -->|"isSelected"| SelectedBG["selectedBackgroundView"]
Reference code
CollectionViewManagingVisualStates/ViewController.swift:34 — the collection delegate treats highlight and selection as different event streams and applies a different visual response to each.
func collectionView(
_ collectionView: UICollectionView,
didHighlightItemAt indexPath: IndexPath
) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.contentView.backgroundColor =
#colorLiteral(red: 1, green: 0.4932718873,
blue: 0.4739984274, alpha: 1)
}
}
func collectionView(
_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath
) {
if let cell = collectionView.cellForItem(at: indexPath)
as? CustomCollectionViewCell {
cell.showIcon()
}
}
Interpretation
UIKit owns the state machine and sends callbacks. The controller reacts to touch phases, while the cell owns how the persistent selected state looks. There is no app-level selection model because the sample’s subject is cell presentation.
Ownership and state
classDiagram
SceneDelegate o-- UIWindow : storyboard-provided
ViewController ..|> UICollectionViewDataSource
ViewController ..|> UICollectionViewDelegate
UICollectionView *-- CustomCollectionViewCell : reuse lifecycle
CustomCollectionViewCell o-- UIImageView : IBOutlet
CustomCollectionViewCell *-- UIView : backgroundView
CustomCollectionViewCell *-- UIView : selectedBackgroundView
UICollectionView --> CustomCollectionViewCell : selected/highlighted state
Ownership evidence
CollectionViewManagingVisualStates/CustomCollectionViewCell.swift:13 — each cell configures its two framework-defined background slots and retains the icon outlet used for selected-state detail.
static public let reuseID = "CustomCollectionViewCell"
@IBOutlet var iconView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
let redView = UIView(frame: bounds)
redView.backgroundColor =
#colorLiteral(red: 1, green: 0, blue: 0, alpha: 1)
backgroundView = redView
let blueView = UIView(frame: bounds)
blueView.backgroundColor =
#colorLiteral(red: 0, green: 0, blue: 1, alpha: 1)
selectedBackgroundView = blueView
}
| Owner |
Object or state |
Relationship |
Mutation authority |
| Storyboard / collection view |
Cell registration, collection hierarchy, delegate/data-source wiring |
Constructs and reuses cells |
UIKit/storyboard lifecycle |
ViewController |
No stored selection data |
Coordinates callbacks only |
Collection data-source/delegate methods |
UICollectionView |
Highlighted and selected index-path state |
Framework-owned interaction state |
Touch/selection machinery |
CustomCollectionViewCell |
Default/selected background views and icon outlet |
Owns reusable presentation objects |
awakeFromNib, showIcon, hideIcon |
SceneDelegate |
Storyboard-created window reference |
Receives/stores scene window |
UIKit scene lifecycle |
Class and protocol design
| Type or protocol |
Responsibility |
Depends on or conforms to |
ViewController |
Supply 25 cells and react to highlight/select transitions |
UICollectionViewDataSource, UICollectionViewDelegate |
CustomCollectionViewCell |
Configure persistent background states and icon visibility |
UICollectionViewCell |
SceneDelegate |
Hold the storyboard-created window |
UIWindowSceneDelegate |
AppDelegate |
Configure scene sessions |
UIApplicationDelegate |
There is no app-defined protocol or protocol-oriented domain layer. The only protocols are UIKit callback contracts, which is appropriate for this focused visual-state sample.
Access control
| Symbol |
Access |
Verified effect |
Why it fits this design |
| All app classes |
implicit internal |
Types are visible only inside the app module. |
The sample exposes no reusable framework API. |
CustomCollectionViewCell.reuseID |
Declared public static let |
The declaration is public, but effective external reach is limited by the internal cell class. |
Gives controller/storyboard code a named reuse identifier; internal would also suffice here. |
iconView outlet |
implicit internal, strong |
App-module code could access it; the cell and view hierarchy retain the image view. |
Storyboard connection is simple, although only cell methods need it. |
showIcon / hideIcon |
implicit internal |
The controller can request semantic visual changes without touching the outlet. |
Keeps call sites descriptive even without private properties. |
| Delegate/data-source methods |
implicit internal |
Available for UIKit protocol dispatch within the module. |
Required callback surface. |
private / fileprivate / private(set) |
Not used |
No finer lexical boundary is enforced. |
The sample prioritizes brevity; production code could make iconView private where outlet tooling permits. |
Reference code
CollectionViewManagingVisualStates/CustomCollectionViewCell.swift:28 — controller-facing methods express the visual intent while the cell performs the outlet mutation.
func showIcon() {
iconView.alpha = 1.0
}
func hideIcon() {
iconView.alpha = 0.0
}
Logic ownership and placement
| Logic |
Owning type or file |
Placement rationale |
| Item count and cell dequeue |
ViewController data-source extension |
Requires collection-view context, not cell internals. |
| Highlight begin/end response |
ViewController delegate extension |
Maps UIKit touch callbacks to transient content feedback. |
| Selection begin/end response |
ViewController delegate extension |
Requests semantic icon changes for the selected cell. |
| Default/selected backgrounds |
CustomCollectionViewCell |
Reusable presentation belongs with the reusable view. |
| Icon visibility mutation |
CustomCollectionViewCell |
Hides outlet details from the controller call site. |
| Selection/highlight state machine |
UICollectionView / UICollectionViewCell |
UIKit already owns interaction and cell-state propagation. |
Design patterns
| Pattern |
Source evidence |
Purpose or tradeoff |
| Framework-managed visual state |
CollectionViewManagingVisualStates/CustomCollectionViewCell.swift:16 |
Uses backgroundView and selectedBackgroundView instead of an app state machine. |
| Delegate callbacks |
CollectionViewManagingVisualStates/ViewController.swift:32 |
Separates transient highlight and persistent selection responses. |
| Data source |
CollectionViewManagingVisualStates/ViewController.swift:19 |
Keeps cell creation/dequeue at the collection owner. |
| Reusable cell encapsulation |
CollectionViewManagingVisualStates/CustomCollectionViewCell.swift:11 |
Centralizes visual setup and icon changes across reused instances. |
Naming conventions
CustomCollectionViewCell names the customized UIKit role; the generic ViewController reflects the sample’s intentionally tiny scope.
reuseID names framework identity, while iconView names the outlet by visual role.
showIcon and hideIcon use direct command verbs.
- UIKit callback names distinguish
didHighlight/didUnhighlight from didSelect/didDeselect.
Architecture takeaways
- Let
UICollectionView own selection and highlight state when no domain-level selection model is required.
- Put reusable appearance in the cell and event interpretation in the collection delegate.
- Treat highlight and selection as distinct lifetimes with distinct visuals.
- Expose semantic cell commands rather than mutating its outlet from the controller.
- This sample’s broad module-local visibility is concise, but production cells can narrow implementation properties.
Source map
| Source file |
Relevant symbols |
CollectionViewManagingVisualStates/ViewController.swift |
Data source and highlight/selection callbacks |
CollectionViewManagingVisualStates/CustomCollectionViewCell.swift |
Background slots, reuse ID, icon presentation |
CollectionViewManagingVisualStates/SceneDelegate.swift |
Storyboard window lifecycle |
CollectionViewManagingVisualStates/AppDelegate.swift |
Scene configuration |
CollectionViewManagingVisualStates/Base.lproj/Main.storyboard |
Collection wiring and cell registration |