web-dev-qa-db-ja.com

UIButtonsを備えたUIScrollview-Springboardを再作成する方法?

アプリ内に踏み台のようなインターフェースを作成しようとしています。 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];
34
Patrick

UIScrollViewがコンテンツビューに到達するクリックと、スワイプまたはピンチに変わるタッチの違いを判断するには、タッチを遅らせて指が動いたかどうかを確認する必要がありますその遅延の間。上記の例でdelaysContentTouchesNOに設定することで、これを防ぐことができます。したがって、スクロールビューでは、ユーザーがスワイプジェスチャを実行していることがわかったときに、ビューをキャンセルするのではなく、常にボタンにタッチを渡します。 delaysContentTouchesYESに設定してみてください。

構造的には、スクロールビューでホストされるすべてのビューを共通のコンテンツビューに追加し、その1つのビューのみをスクロールビューのサブビューとして使用することもお勧めします。

28
Brad Larson

私のために働いた解決策は次のとおりです:

  1. canCancelContentTouchesUIScrollViewYESに設定します。
  2. UIScrollViewYESの場合、viewを拡張してtouchesShouldCancelInContentView:(UIView *)viewをオーバーライドしてUIButtonを返すように拡張します。

ドキュメントによると、touchesShouldCancelInContentViewは "YES"を返し、ビューへのタッチメッセージをキャンセルします。NOは、ビューがそれらのメッセージを引き続き受信するようにします。ビューがYESオブジェクトでない場合、デフォルトの戻り値はUIControlです。それ以外の場合は、NOを返します。 "

UIButtonUIControlであるため、canCancelContentTouchesを有効にしてスクロールを有効にするには、拡張機能が必要です。

44

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
5
Richard Chen

UIScrollViewは、多くのイベント自体を処理します。 touchesDidEndを処理し、UIScrollView内のボタンのテストを手動でヒットする必要があります。

1
Rog

ここにあなたの答えがあります:

UIButtonのサブクラス。 (注:各オーバーライドの開始時に[super ....]を呼び出します。

  • プロパティを追加します。タイプBOOL(enableToRestoreと呼ばれる)の1つ
  • プロパティを追加します。タイプCGPointの1つ(startTouchPositionと呼ばれる)
  • awakeFromNibおよびinitWithFrameで、enableToRestoreをisEnabledプロパティに設定します)
  • 「touchesBegan:withEvent:」をオーバーライドして、タッチ位置の開始を保存します。
  • 「touchesMoved:withEvent:」をオーバーライドして、水平方向の動きがあるかどうかを確認します。
  • YESの場合、有効をNOに、選択を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 ....]を呼び出します。

  • 「touchesEnded:withEvent:」と「touchesCancelled:withEvent:」の両方をオーバーライドします
  • オーバーライドで、すべてのサブビュー(およびそのサブビュー)有効フラグをリセットします。
  • 注:カテゴリを使用して、メソッドを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(ビューコントローラーまたはどこかで設定)を設定すると、回答4で最良の結果が得られます。これにより、ボタンに対する応答が非常に速くなります。 setDelaysContentTouches:YESを設定すると、ボタンへの応答時間に重大な影響(150ms)がかかり、軽くて速いタッチが不可能になります。

1
Steven Noyes

私の経験では、最初の答え、つまりdelaysContentTouchesYESに設定するだけでは、問題に関して何も変更されません。それでもボタンは追跡結果をスクロールビューに配信しません。 3番目の答えは単純であり、非常に使いやすいです。ありがとうsieroaoj!

ただし、3番目の回答が機能するためには、delaysContentTouchesYESに設定する必要もあります。そうでなければ、メソッドtouchesEndedもビュー内で追跡するために呼び出されます。したがって、私は次の方法で問題を解決できます:

  1. ボタンを単純なカスタムUIViewに置き換える
  2. フラグ「userInterationEnable = yes;」を設定しますinitメソッド
  3. ビューでUIResponderメソッド「touchesEnded」をオーバーライドして、アクションをトリガーできます

第4。 delaysContentTouchesYESに設定

0
Captain Fim

別の方法は:
1。ボタンを単純なカスタムUIViewに置き換える
2。フラグ「userInterationEnable = yes;」を設定しますinitメソッド
3。ここでは、UIResponderメソッド「touchesEnded」をオーバーライドするビューで、ボタンのように必要なアクションをトリガーできます。

0
user41806