Passing parameters to addTargetactionforControlEvents
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
In iOS development, user interface controls like buttons, sliders, and switches respond to user interaction through the target-action pattern. The addTarget(_:action:for:) method (called addTarget:action:forControlEvents: in Objective-C) connects a control event to a method on a target object. A common question is how to pass custom parameters to the action method, since the method signature is constrained by UIKit. This article explains how the mechanism works and covers practical techniques for passing data.
How the Target-Action Pattern Works
When a user taps a button or interacts with a control, UIKit sends a predefined action message to the target object. You do not call the action method yourself — the system calls it for you. The action method can have one of three signatures:
You wire the action to a button like this:
The key constraint is that UIKit determines which arguments are passed to the action method. You cannot add arbitrary custom parameters to the selector. This means you need alternative approaches to pass extra data.
Technique 1: Use the Tag Property
Every UIView has an integer tag property. You can set it to identify which control triggered the action:
This approach is simple but limited to integer values and becomes hard to maintain as the number of controls grows.
Technique 2: Subclass the Control
For richer data, you can create a custom subclass of UIButton (or any UIControl) and add properties to it:
This is clean and type-safe. The action method receives the custom subclass as the sender, giving you direct access to the extra properties.
Technique 3: Use Associated Objects (Objective-C Runtime)
If subclassing is not practical, you can attach arbitrary data to any object using Objective-C associated objects:
Technique 4: Use Closures (Modern Swift Approach)
Starting with iOS 14, UIAction lets you use closures directly, avoiding selectors entirely:
The closure captures variables from its surrounding scope, which is the most natural way to pass parameters in modern Swift. For projects targeting iOS 14 and later, this is the recommended approach.
Objective-C Equivalent
In Objective-C, the wiring looks similar, but you use selectors and method declarations:
Common Pitfalls
- Trying to add extra parameters to the selector: The selector passed to
addTargetmust match one of the three supported signatures (no params, sender, or sender + event). Adding custom parameters causes an unrecognized selector crash at runtime. - Forgetting the
@objcattribute in Swift: Selectors require Objective-C runtime dispatch. Omitting@objcon the action method causes a compile-time error or a runtime crash. - Using
tagfor complex data: Thetagproperty only holds anInt. Encoding complex information into an integer leads to fragile, unreadable code. Use subclassing or closures instead. - Retain cycles with closures: When using
UIActionclosures that captureself, failing to use[weak self]can create retain cycles that leak memory. - Not matching the sender type in the action method: If you subclass
UIButtonbut declare the action parameter asUIButtoninstead of your subclass, you need to cast the sender, which defeats the purpose of subclassing.
Summary
- The target-action pattern restricts action methods to zero, one (sender), or two (sender + event) parameters — you cannot pass arbitrary arguments through the selector.
- Use the
tagproperty for simple integer-based identification of controls. - Subclass
UIButtonor use associated objects to attach rich custom data to controls. - On iOS 14 and later, prefer
UIActionclosures, which capture surrounding variables naturally and eliminate the need for selectors. - Always mark action methods with
@objcin Swift to enable Objective-C runtime dispatch required by the target-action mechanism.

