2011-01-05 3 views
13

Ho un UISlider come parte di una vista che viene caricata in un UIScrollView con il paging abilitato. Ho notato un comportamento inaspettato. Se l'utente tenta di utilizzare rapidamente il dispositivo di scorrimento (ad esempio, premere e spostare), "attiva" la vista di scorrimento, provocando il passaggio della pagina. Tuttavia, se si tiene premuto per un secondo, il dispositivo di scorrimento si "attiva" e si può quindi regolare il valore del cursore. Questo comportamento è indesiderabile.UISlider e UIScrollView

Qual è il modo migliore per rendere rispondente il UISlider quando caricato in un UIScrollView? Ho pensato di aggiungere una vista "bloccante" che semplicemente mangia eventi di tocco posizionati sotto il cursore, ma non sono sicuro che questo sia il modo migliore per farlo.

risposta

3

Fare in modo che la vista di scorrimento tocchi la regione occupata dal cursore (ignorare hitTest:withEvent:, potrebbe essere necessario creare una sottoclasse di UIScrollView). Se viene rilevato un tocco su detta regione, dire alla vista di scorrimento di passare immediatamente il tocco al cursore.

+0

Grazie, ho finito per andare con quello. Tuttavia, un'altra stranezza ... questo comportamento sembrava solo manifestarsi sul simulatore. Una volta ottenuto sul dispositivo ha funzionato bene senza la correzione. – MarkPowell

+0

Interessante, un'altra ragione per non fare affidamento solo sul simulatore per testare ... – BoltClock

+2

FWIW, sto vedendo esattamente lo stesso comportamento (come descritto nella domanda originale) sia su simulatore (iOS 6) che su dispositivo (iOS 5.1). Devo toccare e tenere premuto un attimo prima di trascinare il cursore, altrimenti otterrò il trascinamento della vista di scorrimento. –

40

non è necessario eseguire un hit test sul lato UIScrollView. poiché il ritardo è impostato dallo UIScrollView stesso. la sottoclasse e l'implementazione di una custom hitTest:withEvent: non sono d'aiuto poiché sono ancora attivate con ritardo.

ho cercato ore per una soluzione elegante a questo, dal momento che volevo simulare il volumelider di Apple nel commutatore di applicazioni ios.

il trucco:

yourScrollView.delaysContentTouches = NO; 

purtroppo questo disabilita gli eventi lungo la pista UISliders, quindi per questa parte il vostro UIScrollView non dà luogo ad alcuna touchevents perché sono catturati dal cursore prima.

passare touchevents diversi da quelli che si trovano nella UISliders pollice rect bisogna sottoclasse UISlider e aggiungere il seguente:

// 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

Grazie Sebastian, è esattamente quello che stavo cercando! –

+0

prego, alex. –

+0

Stavo cercando esattamente la stessa cosa, grazie! –

0

È possibile sottoclasse UIScrollView e l'override del metodo - (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view come segue:

- (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; 
} 

Impostare anche la proprietà yourScrollView.delaysContentTouches = NO; per la visualizzazione a scorrimento.

1

Durante la creazione di un player audio che ho avuto il problema esatto con un sliderView e viene aggiunto a una scrollView, e ogni volta che ho toccato la sliderView, il scrollView utilizzato per ottenere il tatto e al posto del sliderView, il scrollView spostato. Per evitare ciò, ho disabilitato l'aggancio dello scrollView quando l'utente ha toccato lo sliderView e in caso contrario lo scorrimento è abilitato.

- (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

Il mio problema era un superset di questo problema - ho UISliders all'interno UITableViewCells e tutta l'UITableView è una pagina all'interno di un UIScrollView. I cursori stavano devastando le interazioni con gli altri due e le soluzioni di sottoclassi non funzionavano. Ecco che cosa è venuto fuori che funziona benissimo: invia notifiche quando i cursori si muovono e UITableView e UIScrollView disabilitano il cronometraggio durante questo periodo. Nota nell'immagine qui sotto: i miei cursori sono orizzontali, il mio tableview è verticale e il mio UIScrollView ha pagine orizzontali.

UISlider in a UITableView in a UIScrollView

L'UITableViewCell raccoglie gli eventi per il cursore di programmazione creato:

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]; 

Ai fini di questo tutorial, ci interessa solo touchdown e Up:

- (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]; 
} 

Ora rileviamo queste notifiche sia in UITableView (si noti che il tableview è in un VC ma sono sicuro che funzionerebbe se si sottomette 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; 
} 

e UIScrollView (come sopra, racchiusa in un VC):

- (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; 
} 

Mi piacerebbe sentire una soluzione più elegante, ma questo certamente ottiene il lavoro fatto . Quando si utilizza un dispositivo di scorrimento, la tabella e le visualizzazioni di scorrimento rimangono ferme e quando si fa clic all'esterno dei cursori, la vista tabella e le visualizzazioni di scorrimento si spostano come previsto. Inoltre, nota che potrei usare istanze non sottoclasse di tutti e 3 i componenti in questa soluzione. Spero che questo aiuti qualcuno!

1

Ho voluto postare questo commento come commento, ma il mio account non ha rappresentanti, quindi ho dovuto postare una risposta completa. La risposta dell'utente762391, compresa la hitTest override, funziona perfettamente. Voglio solo aggiungere che, invece di rilevante hitTest, si potrebbe invece ignorare pointInside: withEvent: in questo modo (mantenendo il metodo thumbRect):

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

o in Swift:

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

codice commento più upvoted in Swift 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) 
     } 
    } 
}