はじめに
なぜやるのか
iOSアプリのボタン、デザインは良いけど、タップ領域が小さすぎて押しづらい
という事、ありませんか?
例えば、下記のサンプルのボタン。ボタンのデフォルトサイズで、System Imageを設定したよくある情報ボタンです。
しっかしこれがくせ者。サイズは20x20px
とデザイン上はよくあるのですが、タップするには小さく認識に苦労します。

確かに、Appleの公開している、Apple Interface Guideline によると、ボタンサイズは少なくとも 44 x 44 px
以上とする事が推奨されています。
しかし44 x 44 px
は案外大きく、ボタンのサイズを物理的に大きくするだけでは、不都合が生じる事があります。
そこで今回は、ボタンの大きさはそのままに、タップ範囲のみを拡張して、かつ Interface Builder上でプレビューしながら作れる様にUIButtonをカスタムしていきたいと思います。
UIButtonをカスタムする
ボタンの大きさはそのままに、タップ範囲のみを拡張して、かつ Interface Builder上でプレビュー可能なカスタムボタンTapAreaExpandableButton
を作成します。
import UIKit
// @IBDesignableを付与する事で、Storyboard上に設定した値が反映される
@IBDesignable
class TapAreaExpandableButton: UIButton {
private var _minTapArea = CGSize(width: 0, height: 0)
private var _previewArea = false
// @IBInspectableを付与した変数は、Interface Builder側で編集できる
@IBInspectable var minTapArea: CGSize {
get { return _minTapArea }
set { _minTapArea = newValue }
}
@IBInspectable var previewArea: Bool {
get { return _previewArea }
set { _previewArea = newValue }
}
open override func point(inside point: CGPoint, with _: UIEvent?) -> Bool {
if isHidden || !isUserInteractionEnabled { return false }
return expandedArea.contains(point)
}
private var expandedArea: CGRect {
let buttonSize = self.bounds.size
let widthToAdd = max(_minTapArea.width - buttonSize.width, 0)
let heightToAdd = max(_minTapArea.height - buttonSize.height, 0)
return self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
if previewArea { setupPreviewArea() }
}
private func setupPreviewArea() {
let tapGuideView = UIView(frame: expandedArea)
let previewLineColor = Asset.Color.themeMidGray.color.cgColor
let lineLayer = CAShapeLayer()
lineLayer.frame = tapGuideView.bounds
lineLayer.strokeColor = previewLineColor
lineLayer.lineWidth = 1
lineLayer.lineDashPattern = [2, 2]
lineLayer.fillColor = nil
lineLayer.path = UIBezierPath(rect: lineLayer.frame).cgPath
tapGuideView.isUserInteractionEnabled = false
tapGuideView.backgroundColor = UIColor.clear
tapGuideView.isHidden = false
tapGuideView.layer.addSublayer(lineLayer)
addSubview(tapGuideView)
}
}
使い方
先に出てきた、情報ボタンが1個だけ存在するViewを例とします。このボタンは20 x 20px
と小さくタップが難しいです。
そこで TapAreaExpandableButton
をカスタムクラスとして指定します。
するとプロパティ設定に TapAreaExpandableButton
の項目が出現しますので、
Min Tap Areaの項目に44 x 44px
と設定して、Preview Areaの項目をON
にします。
すると、下図の様に設定したMin Tap AreaがStoryBoard上でプレビューされます。
※もしプレビューが表示されない場合は、XcodeのEditor -> Automatically Refresh Designable View
のチェックをONにしてお試しください。

おわりに
タップ可能領域をプレビューしながら設定できるため、ボタンの大きさを微調整する手間を大きく減らす事ができると思います。本記事が少しでも参考になったのであれば幸いです。
参考