アプリ内に踏み台のようなインターフェースを作成しようとしています。 UIScrollViewに追加されたUIButtonを使用しようとしています。私が実行している問題は、ボタンがUIScrollViewにタッチを渡さないことです-フリック/スライドしようとしてボタンを押した場合、UIScrollViewに登録されませんが、その間のスペースをフリックするとボタンが機能します。ボタンをタッチすると、クリック/機能します。
ボタンがタッチイベントを親(スーパービュー)に送信するように強制するプロパティまたは設定はありますか? UIScrollViewを追加する前に、ボタンを他のものに追加する必要がありますか?
これが私のコードです:
//init scrolling area
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 480, 480)];
scrollView.contentSize = CGSizeMake(480, 1000);
scrollView.bounces = NO;
scrollView.delaysContentTouches = NO;
//create background image
UIImageView *rowsBackground = [[UIImageView alloc] initWithImage:[self scaleAndRotateImage:[UIImage imageNamed:@"mylongbackground.png"]]];
rowsBackground.userInteractionEnabled = YES;
//create button
UIButton *btn = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
btn.frame = CGRectMake(100, 850, 150, 150);
btn.bounds = CGRectMake(0, 0, 150.0, 150.0);
[btn setImage:[self scaleAndRotateImage:[UIImage imageNamed:@"basicbutton.png"]] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];
//add "stuff" to scrolling area
[scrollView addSubview:rowsBackground];
[scrollView addSubview:btn];
//add scrolling area to cocos2d
//this is just a UIWindow
[[[Director sharedDirector] openGLView] addSubview:scrollView];
//mem-mgmt
[rowsBackground release];
[btn release];
[scrollView release];
UIScrollView
がコンテンツビューに到達するクリックと、スワイプまたはピンチに変わるタッチの違いを判断するには、タッチを遅らせて指が動いたかどうかを確認する必要がありますその遅延の間。上記の例でdelaysContentTouches
をNO
に設定することで、これを防ぐことができます。したがって、スクロールビューでは、ユーザーがスワイプジェスチャを実行していることがわかったときに、ビューをキャンセルするのではなく、常にボタンにタッチを渡します。 delaysContentTouches
をYES
に設定してみてください。
構造的には、スクロールビューでホストされるすべてのビューを共通のコンテンツビューに追加し、その1つのビューのみをスクロールビューのサブビューとして使用することもお勧めします。
私のために働いた解決策は次のとおりです:
canCancelContentTouches
のUIScrollView
をYES
に設定します。UIScrollView
がYES
の場合、view
を拡張してtouchesShouldCancelInContentView:(UIView *)view
をオーバーライドしてUIButton
を返すように拡張します。ドキュメントによると、touchesShouldCancelInContentView
は "YES
"を返し、ビューへのタッチメッセージをキャンセルします。NO
は、ビューがそれらのメッセージを引き続き受信するようにします。ビューがYES
オブジェクトでない場合、デフォルトの戻り値はUIControl
です。それ以外の場合は、NO
を返します。 "
UIButton
はUIControl
であるため、canCancelContentTouches
を有効にしてスクロールを有効にするには、拡張機能が必要です。
UIScrollViewに多数のボタンがあるという同様のケースがあり、これらのボタンをスクロールしたいと思います。最初に、UIScrollViewとUIButtonの両方をサブクラス化しました。ただし、サブクラス化されたUIScrollViewがtouchesEndedイベントを受信しないことに気付いたため、サブクラスのUIButtonのみに変更しました。
@interface MyPhotoButton : UIButton {
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
@end
@implementation MyPhotoButton
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self setEnabled:NO];
[self setSelected:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self setEnabled:YES];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
[self setEnabled:YES];
}
@end
UIScrollViewは、多くのイベント自体を処理します。 touchesDidEndを処理し、UIScrollView内のボタンのテストを手動でヒットする必要があります。
ここにあなたの答えがあります:
UIButtonのサブクラス。 (注:各オーバーライドの開始時に[super ....]を呼び出します。
サンプルコード:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
[super touchesBegan:touches withEvent:event];
[self setStartTouchPosition:[touch locationInView:self]];
}
//
// Helper Function
//
- (BOOL)isTouchMovingHorizontally:(UITouch *)touch
{
CGPoint currentTouchPosition = [touch locationInView:self];
BOOL rValue = NO;
if (fabsf([self startTouchPosition].x - currentTouchPosition.x) >= 2.0)
{
rValue = YES;
}
return (rValue);
}
//
// This is called when the finger is moved. If the result is a left or right
// movement, the button will disable resulting in the UIScrollView being the
// next responder. The parrent ScrollView will then re-enable the button
// when the finger event is ended of cancelled.
//
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if ([self isTouchMovingHorizontally:[touches anyObject]])
{
[self setEnabled:NO];
[self setSelected:NO];
}
}
これにより、UIScrollViewがアクティブになります。
UIScrollViewをサブクラス化します。 (注:各オーバーライドの開始時に[super ....]を呼び出します。
。
- (void) restoreAllEnables
{
NSArray *views = [self subviews];
for (UIView *aView in views)
{
if ([aView respondsToSelector:@selector(restoreEnable)])
{
[aView restoreEnable];
}
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self restoreAllEnables];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self restoreAllEnables];
}
。
-(void) restoreEnable
{
NSArray *views = [self subviews];
if ([self respondsToSelector:@selector(enableToRestore)])
{
[self setEnabled:[self enableToRestore]];
}
for (UIView *aView in views)
{
if ([aView respondsToSelector:@selector(restoreEnable)])
{
[aView restoreEnable];
}
}
}
編集注:私は答え3を機能させることができませんでした。同様に:setDelaysContentTouches:NO(ビューコントローラーまたはどこかで設定)を設定すると、回答4で最良の結果が得られます。これにより、ボタンに対する応答が非常に速くなります。 setDelaysContentTouches:YESを設定すると、ボタンへの応答時間に重大な影響(150ms)がかかり、軽くて速いタッチが不可能になります。
私の経験では、最初の答え、つまりdelaysContentTouches
をYES
に設定するだけでは、問題に関して何も変更されません。それでもボタンは追跡結果をスクロールビューに配信しません。 3番目の答えは単純であり、非常に使いやすいです。ありがとうsieroaoj!
ただし、3番目の回答が機能するためには、delaysContentTouches
をYES
に設定する必要もあります。そうでなければ、メソッドtouchesEnded
もビュー内で追跡するために呼び出されます。したがって、私は次の方法で問題を解決できます:
- ボタンを単純なカスタムUIViewに置き換える
- フラグ「userInterationEnable = yes;」を設定しますinitメソッド
- ビューでUIResponderメソッド「touchesEnded」をオーバーライドして、アクションをトリガーできます
第4。 delaysContentTouches
をYES
に設定
別の方法は:
1。ボタンを単純なカスタムUIViewに置き換える
2。フラグ「userInterationEnable = yes;」を設定しますinitメソッド
3。ここでは、UIResponderメソッド「touchesEnded」をオーバーライドするビューで、ボタンのように必要なアクションをトリガーできます。