Selecting multiple items with a two-finger pan gesture
At a glance
| Item | Summary |
|---|---|
| Purpose | Accelerate selection of multiple table rows or collection items with UIKit’s two-finger pan interaction. |
| App architecture | Two sibling controllers own small demo models and adapt UIKit’s table and collection multiple-selection callbacks into editing state. |
| Main patterns | Parallel view-controller implementations, Framework callback boundary, Controller-owned models |
| Project style | Seven Swift files; storyboard-backed table and collection examples share the same interaction concept without a common app abstraction. |
Project structure
Source bundle/
├── MultiselectGestureSample/
│ ├── AppDelegate.swift
│ ├── CollectionViewController.swift
│ ├── FillerModel.swift
│ ├── PhotoModel.swift
│ ├── SceneDelegate.swift
│ ├── TableViewController.swift
│ ├── CollectionViewCell.swift
│ └── Base.lproj/
│ ├── CollectionView.storyboard
│ └── LaunchScreen.storyboard
├── Configuration/
│ └── SampleCode.xcconfig
└── MultiselectGestureSample.xcodeproj/
├── .xcodesamplecode.plist
└── project.pbxproj
Structure observations
CollectionViewControllerandTableViewControllerare parallel feature roots rather than a call chain fromAppDelegate.- Model generation stays in
PhotoModelandFillerModel; selection policy stays beside each UIKit controller. - Framework data-source and delegate conformances are split into extensions for the collection example.
Overall architecture
flowchart LR
PM["PhotoModel items"] --> CVC["CollectionViewController"]
FM["FillerModel items"] --> TVC["TableViewController"]
CVC --> CSEL["UICollectionView multiselect callbacks"]
TVC --> TSEL["UITableView multiselect callbacks"]
CSEL --> EDIT["Editing and selected-item UI"]
TSEL --> EDIT
Reference code
MultiselectGestureSample/CollectionViewController.swift:104 — collection-view multiple-selection callback
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
// Returning `true` automatically sets `collectionView.isEditing`
// to `true`. The app sets it to `false` after the user taps the Done button.
return true
}
func collectionView(_ collectionView: UICollectionView, didBeginMultipleSelectionInteractionAt indexPath: IndexPath) {
setEditing(true, animated: true)
}Interpretation
The sample demonstrates one UIKit capability twice. Each controller is its own adapter: it supplies model identifiers/cells, accepts the framework callback, and updates editing UI. AppDelegate and SceneDelegate only provide lifecycle plumbing.
Ownership and state
classDiagram
CollectionViewController *-- PhotoModelArray : photos
TableViewController *-- FillerModelArray : items
CollectionViewController o-- UICollectionView : storyboard outlet
CollectionViewCell *-- Bool : showSelectionIcons
Ownership evidence
MultiselectGestureSample/CollectionViewController.swift:12 — collection controller state
@IBOutlet weak var collectionView: UICollectionView!
private let photos = PhotoModel.generatePhotosItems(count: 100)
private let sectionInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
private var isPad = false| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
CollectionViewController |
photos and layout/editing state |
Creates and retains private value state | The controller mutates editing and selection presentation |
TableViewController |
items |
Creates the generated filler-item array | The controller reads it for table data-source callbacks |
CollectionViewController |
collectionView |
Receives a storyboard-owned weak outlet | The controller changes selection, focus, layout, and editing flags |
CollectionViewCell |
showSelectionIcons |
Owns private cell presentation state | Only the cell updates its selection visuals |
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
MultiselectGestureSample/CollectionViewController.swift:99 — framework delegate conformance
extension CollectionViewController: UICollectionViewDelegate {
// MARK: - Multiple selection methods.
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
return true
}
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
CollectionViewController |
Configures collection selection/editing and supplies photo cells | UIViewController, UICollectionViewDataSource, UICollectionViewDelegate |
TableViewController |
Supplies rows and handles table multiselect callbacks | UITableViewController |
CollectionViewCell |
Maps a PhotoModel to image and selection visuals |
UICollectionViewCell |
PhotoModel / FillerModel |
Generate immutable demonstration data | Value types and bundled assets/text |
The source uses UIKit callback protocols, but defines no local substitutable protocol; this is callback-oriented, not an app-wide protocol-oriented architecture.
Access control
| Symbol | Access | Verified effect | Design reason |
|---|---|---|---|
photos, sectionInsets, isPad |
private |
Accessible only within CollectionViewController and same-file lexical extensions |
These values implement the controller’s layout and editing invariant. |
showSelectionIcons |
private |
Accessible only inside CollectionViewCell |
Prevents outside code from bypassing configureCell. |
| Controller and model types | implicit internal |
Visible inside the app module | Storyboard/runtime lookup and sibling source files need the types; external modules do not. |
Reference code
MultiselectGestureSample/CollectionViewCell.swift:19 — cell-local selection state
private var showSelectionIcons = false
override func awakeFromNib() {
super.awakeFromNib()
// Turn `imageViewSelected` into a circle to make its background
// color act as a border around the checkmark symbol.
}Declarations without an explicit modifier are module-internal. private is used for state and helpers that implement one type’s invariant; no public library surface is inferred for a single app target.
Logic ownership and placement
| Logic | Owning type or file | Why it lives there |
|---|---|---|
| Collection selection policy | CollectionViewController |
The collection delegate callbacks and editing UI change together. |
| Table selection policy | TableViewController |
The table subclass directly overrides the corresponding callbacks. |
| Demo data creation | PhotoModel and FillerModel |
Keeps generated content out of UIKit callback code. |
| Cell selection appearance | CollectionViewCell |
The reusable view owns its visual state. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Parallel view-controller implementations | MultiselectGestureSample/CollectionViewController.swift:104 and MultiselectGestureSample/TableViewController.swift:49 |
Shows the same UIKit interaction for two container APIs without forcing inheritance. |
| Framework callback boundary | MultiselectGestureSample/CollectionViewController.swift:99 |
UIKit drives selection lifecycle through delegate methods. |
| Controller-owned models | MultiselectGestureSample/CollectionViewController.swift:14 and MultiselectGestureSample/TableViewController.swift:15 |
Appropriate for a small sample; a larger app may inject a shared store. |
Naming conventions
- Types use UIKit role suffixes (
CollectionViewController,TableViewController,CollectionViewCell) and content suffixes (PhotoModel,FillerModel). - Callback names retain UIKit signatures; commands such as
toggleSelectionModeandupdateUserInterfacedescribe intent. - Private state names (
isPad,showSelectionIcons) use Boolean phrasing. - Files follow their primary type; collection data-source/delegate conformances stay in same-file extensions.
Architecture takeaways
- Treat the table and collection examples as siblings, not an
AppDelegate-to-model dependency chain. - Keep framework callback policy in the controller that owns the corresponding editing UI.
- Use private state to protect reusable-cell and layout invariants; no public API is needed.
Source map
| Source file | Architectural role |
|---|---|
MultiselectGestureSample/CollectionViewController.swift |
Collection selection, layout, data source, and delegate |
MultiselectGestureSample/TableViewController.swift |
Table data source and multiple-selection callbacks |
MultiselectGestureSample/CollectionViewCell.swift |
Photo-cell presentation |
MultiselectGestureSample/PhotoModel.swift |
Photo demonstration data |
MultiselectGestureSample/FillerModel.swift |
Table demonstration data |
MultiselectGestureSample/AppDelegate.swift / SceneDelegate.swift |
Application and scene lifecycle |