UIImageView
Touch Events
iOS Development
Swift
Gesture Detection

How can I detect the touch event of an UIImageView?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UIImageView is often used as a visual control in iOS apps, but it does not receive touch events by default. The reliable solution is to enable interaction explicitly, attach a gesture recognizer, and then verify that nothing higher in the view hierarchy is intercepting the event.

Enable User Interaction First

The most important detail is that UIImageView starts with isUserInteractionEnabled set to false. If you forget to change that, your tap handler will never fire no matter how correct the rest of the code looks.

swift
1import UIKit
2
3final class ProfileViewController: UIViewController {
4    private let avatarView = UIImageView(image: UIImage(named: "avatar"))
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        view.backgroundColor = .systemBackground
9        setupAvatar()
10    }
11
12    private func setupAvatar() {
13        avatarView.translatesAutoresizingMaskIntoConstraints = false
14        avatarView.isUserInteractionEnabled = true
15        avatarView.contentMode = .scaleAspectFill
16        avatarView.clipsToBounds = true
17
18        view.addSubview(avatarView)
19        NSLayoutConstraint.activate([
20            avatarView.widthAnchor.constraint(equalToConstant: 120),
21            avatarView.heightAnchor.constraint(equalToConstant: 120),
22            avatarView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
23            avatarView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
24        ])
25    }
26}

Without that one property change, gesture recognizers attached to the image view appear to do nothing.

Add a Tap Gesture Recognizer

For most cases, a tap recognizer is the simplest way to detect interaction.

swift
1private func setupAvatar() {
2    avatarView.translatesAutoresizingMaskIntoConstraints = false
3    avatarView.isUserInteractionEnabled = true
4    avatarView.contentMode = .scaleAspectFill
5    avatarView.clipsToBounds = true
6
7    let tap = UITapGestureRecognizer(target: self, action: #selector(didTapAvatar))
8    avatarView.addGestureRecognizer(tap)
9
10    view.addSubview(avatarView)
11}
12
13@objc private func didTapAvatar() {
14    print("Avatar tapped")
15}

This is the default pattern because it keeps touch logic separate from layout code and works well with Interface Builder or fully programmatic UI.

Support More Than One Gesture When Needed

If the image supports more than a simple tap, add additional recognizers rather than jumping immediately to low-level touch overrides.

swift
1let tap = UITapGestureRecognizer(target: self, action: #selector(didTapAvatar))
2let longPress = UILongPressGestureRecognizer(target: self, action: #selector(didLongPressAvatar(_:)))
3
4avatarView.addGestureRecognizer(tap)
5avatarView.addGestureRecognizer(longPress)
6
7@objc private func didLongPressAvatar(_ recognizer: UILongPressGestureRecognizer) {
8    if recognizer.state == .began {
9        print("Long press began")
10    }
11}

Gesture recognizers are easier to reason about and easier to combine than manual touchesBegan logic for this kind of UI.

Watch for Gesture Conflicts in Scrollable Containers

Image views often live inside table views, collection views, or scroll views. In those cases, the parent view may also have gestures that compete with your image recognizer. If taps feel unreliable, check whether another recognizer is winning first.

swift
1final class PhotoViewController: UIViewController, UIGestureRecognizerDelegate {
2    let imageView = UIImageView()
3
4    override func viewDidLoad() {
5        super.viewDidLoad()
6        imageView.isUserInteractionEnabled = true
7
8        let tap = UITapGestureRecognizer(target: self, action: #selector(onTap))
9        tap.delegate = self
10        imageView.addGestureRecognizer(tap)
11    }
12
13    func gestureRecognizer(
14        _ gestureRecognizer: UIGestureRecognizer,
15        shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
16    ) -> Bool {
17        return false
18    }
19
20    @objc private func onTap() {
21        print("Tapped")
22    }
23}

The right delegate behavior depends on the UX you want, but conflicts should be handled intentionally rather than discovered by accident.

Be Careful in Reusable Cells

If the image view lives inside a reusable collection or table view cell, avoid adding the same recognizer multiple times during reuse. That can lead to duplicate callbacks for one tap.

swift
1final class AvatarCell: UICollectionViewCell {
2    let imageView = UIImageView()
3
4    override init(frame: CGRect) {
5        super.init(frame: frame)
6        imageView.isUserInteractionEnabled = true
7
8        if imageView.gestureRecognizers?.isEmpty ?? true {
9            let tap = UITapGestureRecognizer(target: self, action: #selector(onImageTap))
10            imageView.addGestureRecognizer(tap)
11        }
12    }
13
14    required init?(coder: NSCoder) {
15        fatalError("init(coder:) has not been implemented")
16    }
17
18    @objc private func onImageTap() {
19        print("Cell image tapped")
20    }
21}

Reuse issues are subtle because the first few manual tests often look fine.

Treat Interactive Images as Controls

If the image behaves like a button, make that explicit for accessibility as well.

swift
avatarView.isAccessibilityElement = true
avatarView.accessibilityLabel = "Profile photo"
avatarView.accessibilityHint = "Opens photo actions"

That keeps VoiceOver behavior aligned with what sighted users experience on screen.

Common Pitfalls

The most common mistake is forgetting to set isUserInteractionEnabled to true. Another is attaching recognizers repeatedly in reusable cells and getting duplicate events. Developers also overlook transparent overlays or parent gestures that block the tap before it reaches the image view.

Summary

  • 'UIImageView does not receive touches until isUserInteractionEnabled is enabled.'
  • Use gesture recognizers as the default way to detect taps and long presses.
  • Check parent scroll views and overlays when touches do not arrive consistently.
  • Avoid adding duplicate recognizers in reusable cells.
  • Add accessibility metadata when the image acts like an interactive control.

Course illustration
Course illustration

All Rights Reserved.