I have a UIButton
in my UITableViewCell
I made a subclass of UIButton
override the pointInside
function:
var touchMargin:CGFloat = 20.0
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let extendedArea = CGRectInset(self.bounds, -touchMargin, -touchMargin)
return CGRectContainsPoint(extendedArea, point)
}
However, the touch area does not get increased.
I get a touch on the table cell if I touch slightly outside of the UIButton
.
Does this code not work due to the Button being placed in a cell?
How can I fix?
Swift 3
//
// ViewController.swift
// test
//
// Created by David Seek on 9/29/16.
// Copyright © 2016 David Seek. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = MyButton(frame: CGRect(x: 200, y: 200, width: 100, height: 100))
button.backgroundColor = UIColor.white
button.addedTouchArea = 50 // any value you want
button.addTarget(self, action:#selector(self.action), for: .touchUpInside)
self.view.addSubview(button)
}
func action() {
print("touched")
}
}
class MyButton: UIButton {
var addedTouchArea = CGFloat(0)
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let newBound = CGRect(
x: self.bounds.origin.x - addedTouchArea,
y: self.bounds.origin.y - addedTouchArea,
width: self.bounds.width + 2 * addedTouchArea,
height: self.bounds.width + 2 * addedTouchArea
)
return newBound.contains(point)
}
}
You are creating a UIButton
in the size of 100x100, and with .addedTouchArea
, you have a UIButton - still optical size 100x100, but with a touch area of 150x150.
Swift 2.X
class MyButton: UIButton {
var addedTouchArea = CGFloat(0)
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let newBound = CGRect(
x: self.bounds.origin.x - addedTouchArea,
y: self.bounds.origin.y - addedTouchArea,
width: self.bounds.width + 2 * addedTouchArea,
height: self.bounds.width + 2 * addedTouchArea
)
return newBound.contains(point)
}
}
InterfaceBuilder
If you did set the Button with the InterfaceBuilder
, apply our subclass of UIButton
to your button.
Then set an outlet to the button, f.e. named buttonXY. And set buttonXY.addedTouchArea = 50
within viewDidLoad
, f.e.
UITableViewCell
Since you're asking for UITableViewCell
. It works exactly the same.
In your ViewController class:
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell") as! TableViewCell
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.myButton.addedTouchArea = 50 // any value you want
tableView.rowHeight = 200
return cell
}
}
In your Cell class:
class TableViewCell: UITableViewCell {
@IBOutlet weak var myButton: MyButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
@IBAction func myButtonAction(_ sender: AnyObject) {
print("touched")
}
}
The only thing you should beware of: The outlet got set as a UIButton
outlet, even tho I had declared it as subclass of MyButton within the InterfaceBuilder
. I had to change the outlet manually to MyButton
! @IBOutlet weak var myButton: MyButton!
you should override hitTest on the view that you want to enlarge.
e.g.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
CGRect touchRect = CGRectInset(self.bounds, -10, -10);
if (CGRectContainsPoint(touchRect, point)) {
//TODO!? check supviews
return self;
}
return nil;
}
First you need to create UIButton subclass:
class ButtonWithTouchSize: UIButton {
var touchAreaPadding: UIEdgeInsets? = nil
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if let inset = touchAreaPadding {
let origin = CGPoint(x: 0 - inset.left, y: 0 - inset.top)
let size = CGSize(width: inset.left + bounds.width + inset.right,
height: inset.top + bounds.height + inset.bottom)
let rect = CGRect(origin: origin, size: size)
return rect.contains(point)
} else {
return super.point(inside: point, with: event)
}
}
}
If you put an instance of ButtonWithTouchSize directly into UITableViewCell contentView, you'll get it. But if you wrap this instance in some container wich is in UITableViewCell contentView, your should also override its point(inside:with) method:
class ContainerView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return btn.point(inside: point, with: event)
}
override func layoutSubviews() {
super.layoutSubviews()
btn.frame = self.bounds
}
private let btn: ButtonWithTouchSize = ButtonWithTouchSize()
}
So, to sum up... you should override point(inside:with) for each superview, which size is lessThanOrEqual to button size
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With