Showing help tags for views and controls using tooltip interactions
At a glance
| Item | Summary |
|---|---|
| Purpose | Attach static, dynamic, and region-specific tooltips to UIKit views and controls. |
| App architecture | ViewController composes tooltip-enabled views; UIToolTipInteraction delegates either remain on the controller or move into custom view subclasses that calculate their own configuration. |
| Main patterns | Interaction-object composition, Delegate-configured behavior, Self-contained custom views |
| Project style | Six Swift files; one storyboard controller plus small custom view/delegate types. |
Project structure
Source bundle/
├── TooltipSample/
│ ├── AppDelegate.swift
│ ├── SceneDelegate.swift
│ ├── ViewController.swift
│ ├── TextViewWithTooltip.swift
│ ├── ViewWithBackgroundColorTooltip.swift
│ ├── ViewWithTooltipRegion.swift
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ └── Info.plist
├── Configuration/
│ └── SampleCode.xcconfig
└── TooltipSample.xcodeproj/
├── .xcodesamplecode.plist
└── project.pbxproj
Structure observations
- The feature is composed in
ViewController, not in the app delegate. - Default tooltips need only an interaction or
toolTipvalue; dynamic and region-aware tooltips use delegate callbacks. - Custom view files colocate geometry/color knowledge with the delegate that uses it.
Overall architecture
flowchart LR
VC["ViewController"] --> VIEWS["Labels, buttons, text and custom views"]
VIEWS --> INTERACTIONS["UIToolTipInteraction objects"]
INTERACTIONS --> STATIC["Static tooltip text"]
INTERACTIONS --> DELEGATES["Delegate configuration callbacks"]
DELEGATES --> DYNAMIC["Cart count, text range, color, or hover region"]
Reference code
TooltipSample/ViewController.swift:99 — tooltip composition
override func viewDidLoad() {
super.viewDidLoad()
containerStackView.addArrangedSubview(viewWithDefaultTooltip)
containerStackView.addArrangedSubview(labelWithTooltip)
containerStackView.addArrangedSubview(buttonWithTooltip)
containerStackView.addArrangedSubview(viewWithBackgroundColorTooltip)
containerStackView.addArrangedSubview(shoppingCartButtonWithTooltip)
containerStackView.addArrangedSubview(viewWithTooltipRegion)
}Interpretation
The controller is a composition showcase. Each tooltip style is implemented at the narrowest useful boundary: static strings on views/interactions, controller state for the cart count, and view-local delegate logic when geometry or appearance belongs to a custom view.
Ownership and state
classDiagram
ViewController *-- UILabel : labelWithTooltip
ViewController *-- UIButton : buttonWithTooltip
ViewController *-- ViewWithTooltipRegion : lazy view
UIView *-- UIToolTipInteraction : addInteraction
ViewWithTooltipRegion ..|> UIToolTipInteractionDelegate
Ownership evidence
TooltipSample/ViewController.swift:74 — custom view and interaction construction
lazy var viewWithBackgroundColorTooltip: UIView = {
let view = ViewWithBackgroundColorTooltip()
view.backgroundColor = UIColor.systemYellow
let tooltipInteraction = UIToolTipInteraction()
tooltipInteraction.delegate = view
view.addInteraction(tooltipInteraction)
return view
}()| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
ViewController |
Lazy tooltip demonstration views | Creates them and inserts them into its stack view | The controller chooses the examples and mutates cartItemCount. |
Each UIView |
Attached UIToolTipInteraction |
UIKit retains interactions added with addInteraction |
The view owns attachment lifetime; the assigned delegate supplies behavior. |
ViewWithTooltipRegion |
Region geometry | Computes from its own bounds |
The custom view alone decides which regions return a tooltip. |
TextViewWithTooltip |
Text-range tooltip behavior | Receives the interaction delegate role | The text view maps hover positions to text-specific content. |
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
TooltipSample/ViewWithTooltipRegion.swift:11 — region-specific tooltip delegate
class ViewWithTooltipRegion: UIView, UIToolTipInteractionDelegate {
func toolTipInteraction(_ interaction: UIToolTipInteraction,
configurationAt point: CGPoint) -> UIToolTipConfiguration? {
var topRect = self.bounds
var bottomRect = self.bounds
let partHeight = self.bounds.size.height / 3
topRect.size.height = partHeight
}
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
ViewController |
Builds and presents the tooltip variants | UIViewController, UIToolTipInteractionDelegate |
TextViewWithTooltip |
Returns tooltip content for text positions | UITextView, UIToolTipInteractionDelegate |
ViewWithBackgroundColorTooltip |
Derives tooltip text from view appearance | UIView, UIToolTipInteractionDelegate |
ViewWithTooltipRegion |
Restricts tooltip activation to top/bottom regions | UIView, UIToolTipInteractionDelegate |
All capability protocols here are UIKit-defined. The separate conforming view types are useful delegation seams, but the sample does not define a local protocol abstraction.
Access control
| Symbol | Access | Verified effect | Design reason |
|---|---|---|---|
| Tooltip view/controller types | implicit internal |
Visible throughout the app module | Storyboard and sibling source files can construct them; external modules cannot. |
Lazy demo views and cartItemCount |
implicit internal |
Other code in the module could access them | The sample favors inspectable teaching code over a hardened library surface. |
| Tooltip delegate callbacks | implicit internal |
Satisfy UIKit conformance inside the module | They must be visible to the conformance witness, but need not be public. |
Reference code
TooltipSample/ViewController.swift:10 — module-internal feature root
class ViewController: UIViewController {
// ...
}This sample has no meaningful public, open, or fileprivate feature boundary. Its unmodified declarations are internal, which fits a single application target; production code could make demo properties private if no tests or collaborators require them.
Logic ownership and placement
| Logic | Owning type or file | Why it lives there |
|---|---|---|
| Example composition | ViewController |
It owns the stack and demonstrates all supported tooltip styles. |
| Dynamic cart tooltip | ViewController |
Tooltip text depends on controller-owned cartItemCount. |
| Region hit testing | ViewWithTooltipRegion |
The view has the bounds needed to calculate hover regions. |
| Text/color-specific tooltip text | TextViewWithTooltip and ViewWithBackgroundColorTooltip |
Behavior stays beside the UI state it interprets. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Interaction-object composition | TooltipSample/ViewController.swift:21 and TooltipSample/ViewController.swift:68 |
Behavior is attached to existing views without subclassing every static case. |
| Delegate-configured behavior | TooltipSample/ViewController.swift:116 |
Dynamic content is requested only when UIKit needs a tooltip. |
| Self-contained custom views | TooltipSample/ViewWithTooltipRegion.swift:11 |
Geometry-dependent rules do not leak into the composition controller. |
Naming conventions
- Custom views state both subject and behavior (
TextViewWithTooltip,ViewWithTooltipRegion). - Delegate callbacks keep UIKit’s
toolTipInteraction(_:configurationAt:)vocabulary. - Lazy property names describe each demonstrated variant instead of generic
view1/view2labels. - Files follow their primary custom view type.
Architecture takeaways
- Use an interaction object for attachable behavior and a delegate only when content is dynamic.
- Put geometry- or appearance-dependent tooltip rules on the view that owns that knowledge.
- Treat the controller as a demo composer; app lifecycle delegates are not part of the feature architecture.
Source map
| Source file | Architectural role |
|---|---|
TooltipSample/ViewController.swift |
Example composition and dynamic cart tooltip |
TooltipSample/TextViewWithTooltip.swift |
Text-aware tooltip delegate |
TooltipSample/ViewWithBackgroundColorTooltip.swift |
Appearance-aware tooltip delegate |
TooltipSample/ViewWithTooltipRegion.swift |
Region-aware tooltip delegate |
TooltipSample/AppDelegate.swift / SceneDelegate.swift |
Application and scene lifecycle |