Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

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

  • CollectionViewController and TableViewController are parallel feature roots rather than a call chain from AppDelegate.
  • Model generation stays in PhotoModel and FillerModel; selection policy stays beside each UIKit controller.
  • Framework data-source and delegate conformances are split into extensions for the collection example.

Overall architecture

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

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 toggleSelectionMode and updateUserInterface describe 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