2011-01-05 7 views
13

私はUISliderを、ページングを有効にしてUIScrollViewにロードされたビューの一部として持っています。予期せぬ動作に気づいた。ユーザがスライダをすばやく使用しようとすると(すなわち、押して移動する)、スクロールビューが「アクティブ」になり、ページが切り替わる。しかし、1秒間押し続けるとスライダーが「作動する」とスライダーの値を調整できます。この動作は望ましくありません。UISliderとUIScrollView

UIScrollViewにロードしたときにUISliderが応答するようにするにはどうすればよいですか?スライダーの下に置かれたタッチイベントを食べるだけの「ブロッカー」ビューを追加することを考えましたが、これが最善の方法かどうかはわかりません。

答えて

3

スライダーが占める領域にスクロールビューの検出を検出させます(hitTest:withEvent:をオーバーライドし、UIScrollViewをサブクラス化する必要があります)。その領域をタッチしたことが検出された場合は、スクロールビューにそのスライダにタッチをすぐに渡すよう指示します。

+0

ありがとうございました。しかし、別の奇妙なことに...この振る舞いはシミュレータ上でのみ現れたようです。私はデバイス上でそれを得た後、それは修正なしで正常に働いた。 – MarkPowell

+0

面白いのは、テスト用のシミュレータだけに頼らない別の理由... – BoltClock

+2

FWIW、私はシミュレータ(iOS 6)とデバイス(iOS 5.1)の両方で同じ問題が発生していることを確認しています。私はスライダーをドラッグする前に瞬間をタップして保持する必要があります。そうでなければ、スクロールビューのドラッグを取得します。 –

40

UIScrollView側でヒットテストする必要はありません。遅延はUIScrollViewによって設定されるためです。カスタムをサブクラス化して実装することはまだ遅れて起動されているので役に立ちません。

iosアプリケーションスイッチャでapple自身のボリュームライダーをシミュレートしたいので、これをうまく解決するために何時間も検索しました。

トリック:

yourScrollView.delaysContentTouches = NO; 

が、残念ながらこれはUISlidersトラックに沿ってイベントを無効にしますので、それらが第1のスライダによって捕捉されているので、この部分のためにあなたのUIScrollViewはどのtoucheventsをトリガしません。あなたは以下のUISliderをサブクラス化して追加する必要がRECT

UISliders親指にされているもの以外のtoucheventsを渡す:

// get the location of the thumb 
- (CGRect)thumbRect 
{ 
    CGRect trackRect = [self trackRectForBounds:self.bounds]; 
    CGRect thumbRect = [self thumbRectForBounds:self.bounds 
            trackRect:trackRect 
             value:self.value]; 
    return thumbRect; 
} 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGRect thumbFrame = [self thumbRect]; 

     // check if the point is within the thumb 
    if (CGRectContainsPoint(thumbFrame, point)) 
    { 
     // if so trigger the method of the super class 
     NSLog(@"inside thumb"); 
     return [super hitTest:point withEvent:event]; 
    } 
    else 
    { 
     // if not just pass the event on to your superview 
     NSLog(@"outside thumb"); 
     return [[self superview] hitTest:point withEvent:event]; 
    } 
} 
+0

ありがとうSebastian、それは私が探していたものです! –

+0

あなたは歓迎です、アレックス。 –

+0

私はまったく同じことを探していました、ありがとう! –

0

あなたはUIScrollViewのをサブクラス化し、次のように- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)viewメソッドをオーバーライドすることができます

- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view { 
    if ([view isKindOfClass:[UISlider class]]) { 
     UITouch *touch = [[event allTouches] anyObject]; 
     CGPoint location = [touch locationInView:view]; 
     CGRect thumbRect; 
     UISlider *mySlide = (UISlider*) view; 
     CGRect trackRect = [mySlide trackRectForBounds:mySlide.bounds]; 
     thumbRect = [mySlide thumbRectForBounds:mySlide.bounds trackRect:trackRect value:mySlide.value]; 
     if (CGRectContainsPoint(thumbRect, location)) 
      return YES; 
    } 
    return NO; 
} 

また、スクロールビューのプロパティをyourScrollView.delaysContentTouches = NO;に設定します。

1

私はsliderViewと正確な問題を抱えていたし、それがscrollViewに追加され、私はsliderViewに触れたときに、scrollViewがタッチを取得するために使用し、代わりにsliderViewのオーディオプレーヤーを作成している間、scrollViewは移動しました。これを避けるために、ユーザーがsliderViewに触れたときにscrollViewのsrcollingを無効にしました。それ以外の場合、スクロールが有効になりました。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 

    UIView *result = [super hitTest:point withEvent:event] ; 
    if (result == sliderView) 
    { 
     scrollView.scrollEnabled = NO ; 
    } 
    else 
    { 
     scrollView.scrollEnabled = YES ; 
    } 

    return result ; 

} 
1

私の問題は、この問題のスーパーセットだった - 私はUITableViewCells内UISlidersを持っているし、全体のUITableViewはUIScrollViewの内部ページです。スライダーは他の2つとのやりとりに混乱を招き、サブクラス化の解決策は機能しませんでした。ここで私が思いついたのは素晴らしいことです:スライダが移動し、この間にUITableViewとUIScrollViewのdisableScrollingをオンにしたときに通知を送信します。下の図では、スライダは水平、テーブルビューは垂直、UIScrollViewは水平ページを示しています。

のUITableViewCellは、プログラムが作成したスライダーのイベントを拾っUISlider in a UITableView in a UIScrollView

:このチュートリアルの目的のために

self.numberSlider = [[UISlider alloc] init]; 
[self.numberSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchDown:) forControlEvents:UIControlEventTouchDown]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside]; 
[self.numberSlider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpOutside]; 

は、我々は唯一のタッチダウンとアップを気:

- (void)sliderTouchDown:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
} 

- (void)sliderTouchUp:(UISlider *)sender 
{ 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

ここでは、これらの通知を両方のUITableViewで捕捉しています(tableviewはVC内にありますが、サブクラassed):

- (void)viewDidLoad 
{ 
    // other stuff 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchDown:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sliderTouchUp:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)sliderTouchDown:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = NO; 
} 

- (void)sliderTouchUp:(NSNotification *)notify 
{ 
    self.treatmentTableView.scrollEnabled = YES; 
} 

とVCで囲まれた上記と同じUIScrollViewの(、):

- (void)viewDidLoad 
{ 
    // other stuff 

    // Register for slider notifications 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disableScrolling:) name:NOTIFY_SLIDER_TOUCH_BEGAN object:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enableScrolling:) name:NOTIFY_SLIDER_TOUCH_ENDED object:nil]; 
} 

- (void)disableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = NO; 
} 

- (void)enableScrolling:(NSNotification *)notify 
{ 
    self.scrollView.scrollEnabled = YES; 
} 

私はもっとエレガントな解決策を聞いてみたいが、これは間違いなく仕事を取得します。スライダを使用すると、テーブルとスクロールビューは停止し、スライダの外側をクリックすると、テーブルビューとスクロールビューが期待どおりに移動します。また、このソリューションでは、3つのコンポーネントすべてのサブクラス化されていないインスタンスを使用できます。これが誰かを助けることを願って!

1

私はコメントとして投稿したいと思っていましたが、私のアカウントには担当者がいないので、全回答を投稿しなければなりませんでした。 hitTestのオーバーライドを含むuser762391の答えは完全に機能します。私はちょうどあなたの代わりにpointInsideをオーバーライドすることができ、代わりにhitTestをオーバーライドすることを追加したい:withEvent:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent * _Nullable)event { 
    return CGRectContainsPoint([self thumbRect], point) 
} 

またはスウィフト中:

var thumbRect: CGRect { 
    return thumbRectForBounds(bounds, trackRect: trackRectForBounds(bounds), value: value) 
} 

override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { 
    return thumbRect.contains(point) 
} 
1

最もupvotedコメントコードこの(thumbRect方法を保つ)などをスウィフト3で

import Foundation 

class UISliderForScrollView:UISlider { 
    var thumbRect:CGRect { 
     let trackRect = self.trackRect(forBounds: bounds) 
     return thumbRect(forBounds: bounds, trackRect: trackRect, value: value) 
    } 

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 
     if thumbRect.contains(point) { 
      return super.hitTest(point, with: event) 
     } else { 
      return superview?.hitTest(point, with: event) 
     } 
    } 
}