UIButton
iOS Development
User Interface
Tap Area
Mobile App Design

How can I increase the Tap Area for UIButton?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In iOS development, optimizing the tap area for UIButton elements is important for both usability and accessibility. Buttons that are too small frustrate users and cause misses, especially on smaller screens. Apple's Human Interface Guidelines recommend a minimum tap target of 44x44 points for all interactive elements. This article covers multiple techniques to increase the tap area of a UIButton while keeping its visual appearance unchanged.

Why 44x44 Points Matters

The 44x44 point minimum is not arbitrary. It comes from Apple's accessibility research on comfortable touch targets. Users with motor impairments, larger fingers, or reduced precision all benefit from adequately sized tap areas. If your button's visual design calls for a smaller appearance (for example, a small icon button), you still need to ensure the tappable region meets this minimum.

Technique 1: Content Edge Insets

The simplest approach is to add padding around the button's content using contentEdgeInsets. This increases the button's overall frame while keeping the visible content the same size.

swift
let button = UIButton(type: .system)
button.setTitle("Save", for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 20, bottom: 12, right: 20)

This makes the tappable area larger because the button's frame expands to accommodate the insets. The downside is that the button's layout footprint also grows, which may affect surrounding constraints.

Note that contentEdgeInsets is deprecated in iOS 15 and later. Use the UIButton.Configuration API instead.

swift
1var config = UIButton.Configuration.plain()
2config.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20)
3
4let button = UIButton(configuration: config)
5button.setTitle("Save", for: .normal)

Technique 2: Override point(inside:with:)

For cases where you want to expand the tap area without changing the button's visual frame, override the point(inside:with:) method in a UIButton subclass. This method determines whether a touch point falls within the button's bounds.

swift
1class ExpandedTapButton: UIButton {
2    var tapAreaInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
3
4    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
5        let expandedBounds = bounds.inset(by: tapAreaInsets)
6        return expandedBounds.contains(point)
7    }
8}

Negative inset values expand the bounds outward. With tapAreaInsets set to -10 on all sides, the tappable area extends 10 points beyond the visible button in every direction. The visual appearance stays exactly the same because you are only changing the hit-test logic, not the rendering.

Usage:

swift
1let button = ExpandedTapButton(type: .system)
2button.setImage(UIImage(systemName: "xmark"), for: .normal)
3button.frame = CGRect(x: 100, y: 100, width: 24, height: 24)
4// Visual size is 24x24, but tappable area is 44x44

Technique 3: Override hitTest(_:with:)

An alternative to point(inside:with:) is overriding hitTest(_:with:), which gives you even more control over which view receives the touch.

swift
1class HitTestButton: UIButton {
2    var extraHitArea: CGFloat = 10
3
4    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
5        let expandedBounds = bounds.insetBy(dx: -extraHitArea, dy: -extraHitArea)
6        if expandedBounds.contains(point) {
7            return self
8        }
9        return nil
10    }
11}

This approach is useful when the button overlaps with other interactive elements and you need fine-grained control over which view captures the touch.

Technique 4: Wrap in a Larger Container View

If you prefer not to subclass, you can wrap the button inside a larger transparent UIView and attach a tap gesture recognizer to the container.

swift
1let container = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
2container.backgroundColor = .clear
3
4let smallButton = UIButton(type: .system)
5smallButton.setImage(UIImage(systemName: "heart"), for: .normal)
6smallButton.frame = CGRect(x: 10, y: 10, width: 24, height: 24)
7smallButton.isUserInteractionEnabled = false
8
9container.addSubview(smallButton)
10container.addGestureRecognizer(
11    UITapGestureRecognizer(target: self, action: #selector(buttonTapped))
12)

The button itself has user interaction disabled, so touches pass through to the container's gesture recognizer. This works well when you cannot or do not want to subclass the button.

Technique 5: SwiftUI Approach

In SwiftUI, increasing the tap area is straightforward with the .contentShape() modifier.

swift
1import SwiftUI
2
3struct TapAreaButton: View {
4    var body: some View {
5        Button(action: {
6            print("Tapped")
7        }) {
8            Image(systemName: "xmark")
9                .font(.system(size: 16))
10        }
11        .frame(width: 44, height: 44)
12        .contentShape(Rectangle())
13    }
14}

The .frame modifier sets the overall size, and .contentShape(Rectangle()) tells SwiftUI that the entire frame is tappable, not just the visible content. Without contentShape, only the area covered by the image would respond to taps.

Debugging Tap Areas

When your expanded tap area is not working as expected, these techniques help debug the issue:

  • Temporarily add a background color to the button or container to see its actual frame.
  • Use Xcode's View Debugger (Debug > View Debugging > Capture View Hierarchy) to inspect view frames and identify overlapping views that might intercept touches.
  • Check clipsToBounds: If a parent view has clipsToBounds = true, touches outside the parent's bounds are ignored regardless of the child's hit-test area.

Common Pitfalls

  • Overlapping touch areas: If you expand the tap area of adjacent buttons, their touch regions may overlap. The view hierarchy determines which button receives the touch, which can lead to the wrong button being triggered.
  • clipsToBounds on parent views: A parent view with clipsToBounds = true clips not just the rendering but also the touch delivery. Even if your point(inside:with:) override returns true, the parent will discard the touch before it reaches the child.
  • Forgetting Auto Layout: When using contentEdgeInsets, the button's intrinsic content size changes. Make sure your constraints account for this larger size.
  • Deprecated APIs: contentEdgeInsets, titleEdgeInsets, and imageEdgeInsets are deprecated in iOS 15. Migrate to UIButton.Configuration for forward compatibility.

Summary

There are several ways to increase the tap area for UIButton. Use contentEdgeInsets (or UIButton.Configuration in iOS 15+) for the simplest approach. Override point(inside:with:) for invisible expansion that does not affect layout. Use a container view with a gesture recognizer when subclassing is not an option. In SwiftUI, combine .frame with .contentShape(Rectangle()). Whichever method you choose, always verify that the effective tap area meets Apple's 44x44 point minimum for accessibility.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.