UIButtonとそのヒット領域を扱う質問があります。インターフェイスビルダーの[情報の暗闇]ボタンを使用していますが、ヒット領域が一部の人の指に十分な大きさでないことがわかりました。
InfoButtonグラフィックのサイズを変更せずに、プログラムで、またはInterface Builderでボタンのヒット領域を増やす方法はありますか?
背景画像を使用しているため、これらのソリューションはどれもうまくいきませんでした。これは、いくつかの楽しいObjective-Cマジックを実行し、最小限のコードでソリューションのドロップを提供するソリューションです。
まず、UIButton
にカテゴリを追加して、ヒットテストをオーバーライドし、ヒットテストフレームを展開するためのプロパティも追加します。
UIButton + Extensions.h
@interface UIButton (Extensions)
@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end
UIButton + Extensions.m
#import "UIButton+Extensions.h"
#import <objc/runtime.h>
@implementation UIButton (Extensions)
@dynamic hitTestEdgeInsets;
static const NSString *KEY_HIT_TEST_Edge_INSETS = @"HitTestEdgeInsets";
-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)];
objc_setAssociatedObject(self, &KEY_HIT_TEST_Edge_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_Edge_INSETS);
if(value) {
UIEdgeInsets edgeInsets; [value getValue:&edgeInsets]; return edgeInsets;
}else {
return UIEdgeInsetsZero;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
return [super pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
@end
このクラスを追加したら、ボタンのエッジインセットを設定するだけです。インセットを追加することにしましたので、ヒット領域を大きくしたい場合は、負の数を使用する必要があります。
[button setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)];
注:クラスに必ずカテゴリ(#import "UIButton+Extensions.h"
)をインポートしてください。
インターフェースビルダーで画像のエッジインセット値を設定するだけです。
Swiftの拡張機能を使用したエレガントなソリューションを次に示します。 Appleのヒューマンインターフェイスガイドライン( https://developer.Apple.com/ios/human-interface-guidelines/visual-design/layout/ に従って、すべてのUIButtonに少なくとも44x44ポイントのヒット領域を与えます=)
スイフト2:
private let minimumHitArea = CGSizeMake(44, 44)
extension UIButton {
public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// if the button is hidden/disabled/transparent it can't be hit
if self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 { return nil }
// increase the hit frame to be at least as big as `minimumHitArea`
let buttonSize = self.bounds.size
let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
let largerFrame = CGRectInset(self.bounds, -widthToAdd / 2, -heightToAdd / 2)
// perform hit test on larger frame
return (CGRectContainsPoint(largerFrame, point)) ? self : nil
}
}
Swift 3:
fileprivate let minimumHitArea = CGSize(width: 100, height: 100)
extension UIButton {
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// if the button is hidden/disabled/transparent it can't be hit
if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }
// increase the hit frame to be at least as big as `minimumHitArea`
let buttonSize = self.bounds.size
let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)
// perform hit test on larger frame
return (largerFrame.contains(point)) ? self : nil
}
}
UIButton
またはカスタムUIView
をサブクラス化し、point(inside:with:)
を次のようなものでオーバーライドすることもできます。
Swift
override func point(inside point: CGPoint, with _: UIEvent?) -> Bool {
let margin: CGFloat = 5
let area = self.bounds.insetBy(dx: -margin, dy: -margin)
return area.contains(point)
}
Objective-C
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGFloat margin = 5.0;
CGRect area = CGRectInset(self.bounds, -margin, -margin);
return CGRectContainsPoint(area, point);
}
Swift 3.0のChaseのUIButton + Extensionsは次のとおりです。
import UIKit
private var pTouchAreaEdgeInsets: UIEdgeInsets = .zero
extension UIButton {
var touchAreaEdgeInsets: UIEdgeInsets {
get {
if let value = objc_getAssociatedObject(self, &pTouchAreaEdgeInsets) as? NSValue {
var edgeInsets: UIEdgeInsets = .zero
value.getValue(&edgeInsets)
return edgeInsets
}
else {
return .zero
}
}
set(newValue) {
var newValueCopy = newValue
let objCType = NSValue(uiEdgeInsets: .zero).objCType
let value = NSValue(&newValueCopy, withObjCType: objCType)
objc_setAssociatedObject(self, &pTouchAreaEdgeInsets, value, .OBJC_ASSOCIATION_RETAIN)
}
}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if UIEdgeInsetsEqualToEdgeInsets(self.touchAreaEdgeInsets, .zero) || !self.isEnabled || self.isHidden {
return super.point(inside: point, with: event)
}
let relativeFrame = self.bounds
let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.touchAreaEdgeInsets)
return hitFrame.contains(point)
}
}
それを使用するには、次のことができます。
button.touchAreaEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
画像にbackgroundImage
プロパティを設定しないで、imageView
プロパティを設定してください。また、imageView.contentMode
がUIViewContentModeCenter
に設定されていることを確認してください。
情報ボタンの上にカスタムタイプのUIButtonを配置することをお勧めします。カスタムボタンのサイズをヒットエリアのサイズに変更します。そこから2つのオプションがあります。
カスタムボタンの[ハイライト時にタッチを表示]オプションをオンにします。情報ボタンの上に白い輝きが表示されますが、ほとんどの場合、ユーザーの指がこれを覆い、表示されるのは外側の周りの輝きだけです。
情報ボタンにIBOutletを、カスタムボタンに2つのIBActionsを設定します。1つは「Touch Down」用、もう1つは「Touch Up Inside」用です。次に、Xcodeで、touchdownイベントで情報ボタンの強調表示されたプロパティをYESに設定し、touchupinsideイベントで強調表示されたプロパティをNOに設定します。
Swift 3に関する私のソリューション:
class MyButton: UIButton {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let relativeFrame = self.bounds
let hitTestEdgeInsets = UIEdgeInsetsMake(-25, -25, -25, -25)
let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets)
return hitFrame.contains(point)
}
}
提示された回答に問題はありません。ただし、 jlarjlarの答え を拡張したかったのは、他のコントロール(SearchBarなど)で同じ問題に価値を追加できる驚くべき可能性があるためです。これは、pointInsideがUIViewにアタッチされているため、サブクラスany controlを使用してタッチ領域を改善できるためです。この回答は、完全なソリューションを実装する方法の完全なサンプルも示しています。
ボタン(または任意のコントロール)の新しいサブクラスを作成します
#import <UIKit/UIKit.h>
@interface MNGButton : UIButton
@end
次に、サブクラスの実装でpointInsideメソッドをオーバーライドします
@implementation MNGButton
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
//increase touch area for control in all directions by 20
CGFloat margin = 20.0;
CGRect area = CGRectInset(self.bounds, -margin, -margin);
return CGRectContainsPoint(area, point);
}
@end
ストーリーボード/ xibファイルで、問題のコントロールを選択し、IDインスペクターを開いて、カスタムクラスの名前を入力します。
ボタンを含むシーンのUIViewControllerクラスで、ボタンのクラスタイプをサブクラスの名前に変更します。
@property (weak, nonatomic) IBOutlet MNGButton *helpButton;
ストーリーボード/ xibボタンをプロパティIBOutletにリンクすると、タッチ領域がサブクラスで定義された領域に合わせて拡張されます。
pointInside method を CGRectInset および CGRectContainsPoint メソッドとともにオーバーライドすることに加えて、 CGGeometry を調べるのに時間がかかるはずです。 = UIViewサブクラスの長方形のタッチ領域を拡張します。 NSHipster でCGGeometryのユースケースに関する素敵なヒントを見つけることもできます。
たとえば、上記の方法を使用してタッチ領域を不規則にしたり、幅のタッチ領域を水平のタッチ領域の2倍にしたりすることができます。
CGRect area = CGRectInset(self.bounds, -(2*margin), -margin);
NB:UIクラスコントロールを置き換えると、異なるコントロール(またはUIImageViewなどのUIViewサブクラス)のタッチ領域を拡張する際に同様の結果が得られます。
これは私のために働く:
UIButton *button = [UIButton buttonWithType: UIButtonTypeCustom];
// set the image (here with a size of 32 x 32)
[button setImage: [UIImage imageNamed: @"myimage.png"] forState: UIControlStateNormal];
// just set the frame of the button (here 64 x 64)
[button setFrame: CGRectMake(xPositionOfMyButton, yPositionOfMyButton, 64, 64)];
UIButtonの動作を変更しないでください。
@interface ExtendedHitButton: UIButton
+ (instancetype) extendedHitButton;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
@end
@implementation ExtendedHitButton
+ (instancetype) extendedHitButton {
return (ExtendedHitButton *) [ExtendedHitButton buttonWithType:UIButtonTypeCustom];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGRect relativeFrame = self.bounds;
UIEdgeInsets hitTestEdgeInsets = UIEdgeInsetsMake(-44, -44, -44, -44);
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
@end
-[UIView pointInside:withEvent:]
をスウィズルすることにより、より一般的なアプローチを使用しています。これにより、UIView
だけでなく、UIButton
のヒットテスト動作を変更できます。
多くの場合、ボタンはコンテナビュー内に配置され、ヒットテストも制限します。たとえば、ボタンがコンテナビューの上部にあり、タッチターゲットを上方に拡張する場合、コンテナビューのタッチターゲットも拡張する必要があります。
@interface UIView(Additions)
@property(nonatomic) UIEdgeInsets hitTestEdgeInsets;
@end
@implementation UIView(Additions)
+ (void)load {
Swizzle(self, @selector(pointInside:withEvent:), @selector(myPointInside:withEvent:));
}
- (BOOL)myPointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || self.hidden ||
([self isKindOfClass:UIControl.class] && !((UIControl*)self).enabled))
{
return [self myPointInside:point withEvent:event]; // original implementation
}
CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets);
hitFrame.size.width = MAX(hitFrame.size.width, 0); // don't allow negative sizes
hitFrame.size.height = MAX(hitFrame.size.height, 0);
return CGRectContainsPoint(hitFrame, point);
}
static char hitTestEdgeInsetsKey;
- (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
objc_setAssociatedObject(self, &hitTestEdgeInsetsKey, [NSValue valueWithUIEdgeInsets:hitTestEdgeInsets], OBJC_ASSOCIATION_RETAIN);
}
- (UIEdgeInsets)hitTestEdgeInsets {
return [objc_getAssociatedObject(self, &hitTestEdgeInsetsKey) UIEdgeInsetsValue];
}
void Swizzle(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
@end
このアプローチの良い点は、ユーザー定義のランタイム属性を追加することにより、ストーリーボードでもこれを使用できることです。残念ながら、UIEdgeInsets
はそこで直接型として使用できませんが、CGRect
も4つのCGFloat
を持つ構造体で構成されているため、[Rect]を選択して次のように値を入力することで問題なく動作します: {{top, left}, {bottom, right}}
。
さて、透明でわずかに大きいUIView内にUIButtonを配置し、UIButtonのようにUIViewインスタンスでタッチイベントをキャッチできます。そうすれば、ボタンは引き続き使用できますが、タッチ領域は大きくなります。ユーザーがボタンではなくビューにタッチした場合、ボタンで選択および強調表示された状態を手動で処理する必要があります。
他の可能性には、UIButtonの代わりにUIImageを使用することが含まれます。
これは私のSwift 3ソリューションです(このブログ投稿に基づいて: http://bdunagan.com/2010/03/01/iphone- tip-larger-hit-for-for-uibutton / )
class ExtendedHitAreaButton: UIButton {
@IBInspectable var hitAreaExtensionSize: CGSize = CGSize(width: -10, height: -10)
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let extendedFrame: CGRect = bounds.insetBy(dx: hitAreaExtensionSize.width, dy: hitAreaExtensionSize.height)
return extendedFrame.contains(point) ? self : nil
}
}
Interface Builderプロパティを有効にしてマージンを調整するために、Swiftで次のクラスを使用しています。
@IBDesignable
class ALExtendedButton: UIButton {
@IBInspectable var touchMargin:CGFloat = 20.0
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
var extendedArea = CGRectInset(self.bounds, -touchMargin, -touchMargin)
return CGRectContainsPoint(extendedArea, point)
}
}
情報ボタンのヒット領域をプログラムで増やすことができました。 「i」グラフィックはスケールを変更せず、新しいボタンフレームの中央に残ります。
情報ボタンのサイズは、Interface Builderで18x19 [*]に固定されているようです。それをIBOutletに接続することで、コードのフレームサイズを問題なく変更することができました。
static void _resizeButton( UIButton *button )
{
const CGRect oldFrame = infoButton.frame;
const CGFloat desiredWidth = 44.f;
const CGFloat margin =
( desiredWidth - CGRectGetWidth( oldFrame ) ) / 2.f;
infoButton.frame = CGRectInset( oldFrame, -margin, -margin );
}
[*]:iOSの以降のバージョンでは、情報ボタンのヒット領域が増加しているようです。
オーバーライド継承UIButtonによる実装。
Swift 2.2:
// don't forget that negative values are for outset
_button.hitOffset = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
...
class UICustomButton: UIButton {
var hitOffset = UIEdgeInsets()
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
guard hitOffset != UIEdgeInsetsZero && enabled && !hidden else {
return super.pointInside(point, withEvent: event)
}
return UIEdgeInsetsInsetRect(bounds, hitOffset).contains(point)
}
}
UIButtonのサブクラスとして実装されたChaseのカスタムヒットテスト。 Objective-Cで書かれています。
init
とbuttonWithType:
の両方のコンストラクターで機能するようです。私のニーズには完璧ですが、サブクラス化UIButton
は難しいので、誰かがそれに欠陥があるかどうか知りたいです。
#import <UIKit/UIKit.h>
@interface CustomHitAreaButton : UIButton
- (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets;
@end
#import "CustomHitAreaButton.h"
@interface CustomHitAreaButton()
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end
@implementation CustomHitAreaButton
- (instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame]) {
self.hitTestEdgeInsets = UIEdgeInsetsZero;
}
return self;
}
-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
self->_hitTestEdgeInsets = hitTestEdgeInsets;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
return [super pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
@end
WJBackgroundInsetButton.h
#import <UIKit/UIKit.h>
@interface WJBackgroundInsetButton : UIButton {
UIEdgeInsets backgroundEdgeInsets_;
}
@property (nonatomic) UIEdgeInsets backgroundEdgeInsets;
@end
WJBackgroundInsetButton.m
#import "WJBackgroundInsetButton.h"
@implementation WJBackgroundInsetButton
@synthesize backgroundEdgeInsets = backgroundEdgeInsets_;
-(CGRect) backgroundRectForBounds:(CGRect)bounds {
CGRect sup = [super backgroundRectForBounds:bounds];
UIEdgeInsets insets = self.backgroundEdgeInsets;
CGRect r = UIEdgeInsetsInsetRect(sup, insets);
return r;
}
@end
カテゴリ内のメソッドをオーバーライドしないでください。ボタンをサブクラス化し、- pointInside:withEvent:
をオーバーライドします。たとえば、ボタンの横が44ピクセル(最小のタップ可能領域として推奨)よりも小さい場合、これを使用します。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return (ABS(point.x - CGRectGetMidX(self.bounds)) <= MAX(CGRectGetMidX(self.bounds), 22)) && (ABS(point.y - CGRectGetMidY(self.bounds)) <= MAX(CGRectGetMidY(self.bounds), 22));
}
上記のgiasetの回答(最もエレガントなソリューションを見つけました)に基づいて、ここにSwift 3バージョンがあります。
import UIKit
fileprivate let minimumHitArea = CGSize(width: 44, height: 44)
extension UIButton {
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// if the button is hidden/disabled/transparent it can't be hit
if isHidden || !isUserInteractionEnabled || alpha < 0.01 { return nil }
// increase the hit frame to be at least as big as `minimumHitArea`
let buttonSize = bounds.size
let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
let largerFrame = bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)
// perform hit test on larger frame
return (largerFrame.contains(point)) ? self : nil
}
}
この目的のために library を作成しました。
UIView
カテゴリーを使用することを選択できますサブクラス化は不要です:
@interface UIView (KGHitTesting)
- (void)setMinimumHitTestWidth:(CGFloat)width height:(CGFloat)height;
@end
または、UIViewまたはUIButtonをサブクラス化し、minimumHitTestWidth
および/またはminimumHitTestHeight
を設定できます。ボタンヒットテスト領域は、これら2つの値で表されます。
他のソリューションと同様に、- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
メソッドを使用します。このメソッドは、iOSがヒットテストを実行するときに呼び出されます。 This ブログの投稿には、iOSのヒットテストの仕組みに関する説明があります。
https://github.com/kgaidis/KGHitTestingViews
@interface KGHitTestingButton : UIButton <KGHitTesting>
@property (nonatomic) CGFloat minimumHitTestHeight;
@property (nonatomic) CGFloat minimumHitTestWidth;
@end
コードを記述せずに、サブクラスを作成してInterface Builderを使用することもできます。
Swift 4用にフォーマットされた@antoineの回答
class ExtendedHitButton: UIButton
{
override func point( inside point: CGPoint, with event: UIEvent? ) -> Bool
{
let relativeFrame = self.bounds
let hitTestEdgeInsets = UIEdgeInsetsMake( -44, -44, -44, -44 ) // Apple recommended hit target
let hitFrame = UIEdgeInsetsInsetRect( relativeFrame, hitTestEdgeInsets )
return hitFrame.contains( point );
}
}
@Chaseの移植を行いました solution in Swift 2.2
import Foundation
import ObjectiveC
private var hitTestEdgeInsetsKey: UIEdgeInsets = UIEdgeInsetsZero
extension UIButton {
var hitTestEdgeInsets:UIEdgeInsets {
get {
let inset = objc_getAssociatedObject(self, &hitTestEdgeInsetsKey) as? NSValue ?? NSValue(UIEdgeInsets: UIEdgeInsetsZero)
return inset.UIEdgeInsetsValue()
}
set {
let inset = NSValue(UIEdgeInsets: newValue)
objc_setAssociatedObject(self, &hitTestEdgeInsetsKey, inset, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
guard !UIEdgeInsetsEqualToEdgeInsets(hitTestEdgeInsets, UIEdgeInsetsZero) && self.enabled == true && self.hidden == false else {
return super.pointInside(point, withEvent: event)
}
let relativeFrame = self.bounds
let hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitTestEdgeInsets)
return CGRectContainsPoint(hitFrame, point)
}
}
このように使用できます
button.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10)
その他の参照については、 https://stackoverflow.com/a/13067285/1728552 を参照してください
Uitableviewcell.accessoryビュー内のボタンにこのトリックを使用して、タッチ領域を拡大します
#pragma mark - Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGRect accessoryViewTouchRect = CGRectInset(self.accessoryView.frame, -15, -15);
if(!CGRectContainsPoint(accessoryViewTouchRect, location))
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGRect accessoryViewTouchRect = CGRectInset(self.accessoryView.frame, -15, -15);
if(CGRectContainsPoint(accessoryViewTouchRect, location) && [self.accessoryView isKindOfClass:[UIButton class]])
{
[(UIButton *)self.accessoryView sendActionsForControlEvents:UIControlEventTouchUpInside];
}
else
[super touchesEnded:touches withEvent:event];
}
このSwiftバージョンでは、すべてのUIButtonの最小ヒットサイズを定義できます。重要なのは、UIButtonが非表示の場合も処理することであり、多くの回答はこれを無視しています。
extension UIButton {
public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// Ignore if button hidden
if self.hidden {
return nil
}
// If here, button visible so expand hit area
let hitSize = CGFloat(56.0)
let buttonSize = self.frame.size
let widthToAdd = (hitSize - buttonSize.width > 0) ? hitSize - buttonSize.width : 0
let heightToAdd = (hitSize - buttonSize.height > 0) ? hitSize - buttonSize.height : 0
let largerFrame = CGRect(x: 0-(widthToAdd/2), y: 0-(heightToAdd/2), width: buttonSize.width+widthToAdd, height: buttonSize.height+heightToAdd)
return (CGRectContainsPoint(largerFrame, point)) ? self : nil
}
}
上記の@jlajlarの回答は、素晴らしく簡単なように見えましたが、Xamarin.iOSとは一致しないため、Xamarinに変換しました。 Xamarin iOSでソリューションを探している場合は、ここにあります:
public override bool PointInside (CoreGraphics.CGPoint point, UIEvent uievent)
{
var margin = -10f;
var area = this.Bounds;
var expandedArea = area.Inset(margin, margin);
return expandedArea.Contains(point);
}
UIViewまたはUIImageViewをオーバーライドするクラスにこのメソッドを追加できます。これはうまくいきました:)
私はチェイスの応答を追跡しましたが、ボタンが選択解除されるゾーンよりも大きいエリアを作成する場合、ゾーンが大きくない場合、UIControlEventTouchUpInsideイベントのセレクターを呼び出しません。 。
どんな方向やそのようなものでも、サイズは200を超えると思います。
私はこのゲームにとても遅れていますが、あなたの問題を解決するかもしれない簡単なテクニックに重きを置きたかったのです。これが私にとって典型的なプログラムによるUIButtonスニペットです。
UIImage *arrowImage = [UIImage imageNamed:@"leftarrow"];
arrowButton = [[UIButton alloc] initWithFrame:CGRectMake(15.0, self.frame.size.height-35.0, arrowImage.size.width/2, arrowImage.size.height/2)];
[arrowButton setBackgroundImage:arrowImage forState:UIControlStateNormal];
[arrowButton addTarget:self action:@selector(onTouchUp:) forControlEvents:UIControlEventTouchUpOutside];
[arrowButton addTarget:self action:@selector(onTouchDown:) forControlEvents:UIControlEventTouchDown];
[arrowButton addTarget:self action:@selector(onTap:) forControlEvents:UIControlEventTouchUpInside];
[arrowButton addTarget:self action:@selector(onTouchUp:) forControlEvents:UIControlEventTouchDragExit];
[arrowButton setUserInteractionEnabled:TRUE];
[arrowButton setAdjustsImageWhenHighlighted:NO];
[arrowButton setTag:1];
[self addSubview:arrowButton];
ボタンに透明なPNG画像をロードし、背景画像を設定しています。 UIImageに基づいてフレームを設定し、網膜に対して50%スケーリングします。 OK、多分あなたは上記に同意するかどうか、しかし、もしあなたがヒットエリアをより大きくして頭痛を省いたければ:
私がしていることは、Photoshopで画像を開き、キャンバスサイズを120%に増やして保存するだけです。事実上、透明ピクセルを使用して画像を大きくしただけです。
ただ一つのアプローチ。
ボタンには背景画像とタイトルを使用しているため、答えはどれも私には最適ではありません。さらに、画面サイズが変更されると、ボタンのサイズが変更されます。
代わりに、pngの透過領域を大きくすることにより、タップ領域を拡大します。
Zhanserikに似ていますが、変数の拡張子があり、Swift 4.2用に更新されています。
class ButtonWithExtendedHitArea: UIButton {
var extention: CGFloat
required init(extendBy: CGFloat) {
extention = extendBy
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let relativeFrame = self.bounds
let hitTestEdgeInsets = UIEdgeInsets(top: -extention, left: -extention, bottom: -extention, right: -extention)
let hitFrame = relativeFrame.inset(by: hitTestEdgeInsets)
return hitFrame.contains(point)
}
}