UIButtonsを備えたUIScrollview-スプリングボードを再作成する方法は?
-
19-08-2019 - |
質問
アプリ内でスプリングボードのようなインターフェイスを作成しようとしています。 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
に設定することで、これを防ぐことができます。したがって、スクロールビューは、ユーザーがスワイプジェスチャを実行していることが判明したときにキャンセルするのではなく、常にボタンにタッチを渡します。 YES
を<=>に設定してみてください。
構造的には、スクロールビューでホストされるすべてのビューを共通のコンテンツビューに追加し、その1つのビューのみをスクロールビューのサブビューとして使用することをお勧めします。
他のヒント
私のために働いた解決策は次のとおりです。
-
canCancelContentTouches
のUIScrollView
をYES
に設定します。 -
touchesShouldCancelInContentView:(UIView *)view
がview
の場合、UIButton
を拡張してtouchesShouldCancelInContentView
をオーバーライドし、NO
を返します。
ドキュメントによると、UIControl
は<!> quot; <=>を返し、さらに表示するタッチメッセージをキャンセルし、<=>はそれらのメッセージを引き続き表示します。ビューが<=>オブジェクトでない場合、デフォルトの戻り値は<=>です。そうでない場合は、<=>。<!> quot;
<=>は<=>であるため、スクロールを有効にするために<=>を有効にするには拡張子が必要です。
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内のボタンのテストを手動でヒットする必要があります。
OK、あなたの答えです:
サブクラスUIButton。 (注:各オーバーライドの開始時に[super ....]を呼び出します。
- プロパティを追加します。タイプBOOLの1つ (enableToRestoreと呼ばれる)
- プロパティを追加します。タイプCGPointの1つ (startTouchPositionと呼ばれる)
- awakeFromNibおよび initWithFrame、設定 isEnabledへのenableToRestore プロパティ)
- <!> quot; touchesBegan:withEvent:<!> quot;をオーバーライドします。 タッチの開始を保存する 位置。
- <!> quot; touchesMoved:withEvent:<!> quot;をオーバーライドします。に 水平があるかどうかを確認します 動き。
- 「はい」の場合、「有効」を「いいえ」に設定し、 NOに選択しました。
サンプルコード:
- (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 ....]を呼び出します。
- 両方の<!> quot; touchesEnded:withEvent:<!> quot;をオーバーライドします。および<!> quot; touchesCancelled:withEvent:<!> quot;
- オーバーライドで、すべてのサブビュー(およびそのサブビュー)有効フラグをリセットします。
- 注:カテゴリを使用して、メソッドをUIViewに追加します:
。
- (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(View Controllerまたはどこかで設定)は、回答4で最良の結果が得られるように設定されます。これにより、ボタンへの応答が非常に速くなります。 setDelaysContentTouches:YESを設定すると、ボタンへの応答時間に重大な影響(150ミリ秒)がかかり、軽く高速なタッチができなくなります。
別の方法:
1。単純なカスタムUIView
2でdeボタンを置き換えます。フラグ<!> quot; userInterationEnable = yes; <!> quot; initメソッド
3.ビューで、UIResponderメソッド<!> quot; touchesEnded <!> quot;をオーバーライドします。ここで、ボタンのように必要なアクションをトリガーできます。
私の経験では、最初の答え、つまり、単にdelaysContentTouches
をYES
に設定しても、問題に関して何も変わりません。ボタンは引き続き、スクロールビューに追跡結果を配信しません。 3番目の答えは、シンプルで非常に使いやすいです。ありがとうsieroaoj!
ただし、3番目の回答が機能するには、touchesEnded
を<=>に設定する必要もあります。それ以外の場合は、ビュー内で追跡するためにメソッド<=>も呼び出されます。したがって、次の方法で問題を解決できます。
- 単純なカスタムUIViewでdeボタンを置き換える
- フラグ<!> quot; userInterationEnable = yes; <!> quot; initメソッドで
- ビューでUIResponderメソッドをオーバーライドします<!> quot; touchesEnded <!> quot;ここに アクションをトリガーできます
4番目。 <=>を<=>
に設定します