You can simply adjust the content inset of the button to get your desired size. In code, it will look like this:
button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
//Or if you specifically want to adjust around the image, instead use button.imageEdgeInsets
You can set the button EdgeInsets in storyboard or via code. The size of button should be bigger in height and width than image set to button.
Note: After Xcode8, setting content inset is available in size inspecor
Or you can also use image view with tap gesture on it for action while taping on image view. Make sure to tick User Interaction Enabled for imageview on storyboard for gesture to work.
Make image view bigger than image to set on it and set image on it.
Now set the mode of image view image to center on storyboard/interface builder.
import UIKit
@IBDesignable
class GRCustomButton: UIButton {
@IBInspectable var margin:CGFloat = 20.0
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
//increase touch area for control in all directions by 20
let area = self.bounds.insetBy(dx: -margin, dy: -margin)
return area.contains(point)
}
}
When using auto layout combined with content edge insets you may need to change your constraints.
Say you have a 10x10 image and you want to make it 30x30 for a larger hit area:
Set your auto layout constraints to the desired larger area. If you
build right now this would stretch the image.
Using the content edge insets to shrink the space available to the
image so it matches the correct size. In this Example that would 10
10 10 10. Leaving the image with a 10x10 space to draw itself in.
The way I'd approach this is to give the button some extra room around a small image using contentEdgeInsets (which act like a margin outside the button content), but also override the alignmentRect property with the same insets, which bring the rect that autolayout uses back in to the image. This ensures that autolayout calculates its constraints using the smaller image, rather than the full tappable extent of the button.
class HIGTargetButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setImage(_ image: UIImage?, for state: UIControl.State) {
super.setImage(image, for: state)
guard let image = image else { return }
let verticalMarginToAdd = max(0, (targetSize.height - image.size.height) / 2)
let horizontalMarginToAdd = max(0, (targetSize.width - image.size.width) / 2)
let insets = UIEdgeInsets(top: verticalMarginToAdd,
left: horizontalMarginToAdd,
bottom: verticalMarginToAdd,
right: horizontalMarginToAdd)
contentEdgeInsets = insets
}
override var alignmentRectInsets: UIEdgeInsets {
contentEdgeInsets
}
private let targetSize = CGSize(width: 44.0, height: 44.0)
}
The pink button has a bigger tappable target (shown pink here, but could be .clear) and a smaller image - its leading edge is aligned with the green view's leading edge based on the icon, not the whole button.
An alternative to subclassing would be extending UIControl, adding a touchAreaInsets property to it - by leveraging the objC runtime - and swizzling pointInside:withEvent.
Both solutions presented here do work ... under the right circumstances it is. But here are some gotchas you might run into.
First something not completely obvious:
tapping has to be WITHIN the button, touching the button bounds slightly does NOT work. If a button is very small, there is a good chance most of your finger will be outside of the button and the tap won't work.
Specific to the solutions above:
SOLUTION 1 @Travis:
Use contentEdgeInsets to increase the button size without increasing the icon/text size, similar to adding padding