UIScrollview con UIButtons – come ricreare il trampolino di lancio?

Sto cercando di creare un’interfaccia simile a un trampolino di lancio all’interno della mia app. Sto cercando di utilizzare UIButtons aggiunti a un UIScrollView. Il problema che sto eseguendo è con i pulsanti che non passano alcun touch a UIScrollView: se provo a sfogliare / scorrere e succede di premere il pulsante, non si registra per UIScrollView, ma se faccio scorrere lo spazio tra pulsanti funzionerà. I pulsanti fanno clic / funzionano se li touch.

Esiste una proprietà o un’impostazione che obbliga il pulsante a inviare gli eventi di touch al genitore (superview)? I pulsanti devono essere aggiunti a qualcos’altro prima di essere aggiunti a UIScrollView?

Ecco il mio codice:

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

Per consentire a UIScrollView di determinare la differenza tra un clic che passa attraverso le sue visualizzazioni di contenuto e un touch che si trasforma in un colpo o un pizzico, è necessario ritardare il touch e vedere se il dito si è spostato durante quel ritardo. Impostando delaysContentTouches su NO nell’esempio sopra, stai impedendo che ciò accada. Pertanto, la vista di scorrimento passa sempre il touch al pulsante, invece di annullarla quando si scopre che l’utente sta eseguendo un gesto di scorrimento. Prova a impostare delaysContentTouches su YES .

Potrebbe anche essere una buona idea, strutturalmente, aggiungere tutte le viste da ospitare nella vista di scorrimento a una vista di contenuto comune e utilizzare solo quella vista come sottoscheda della vista di scorrimento.

La soluzione che ha funzionato per me includeva:

  1. Impostazione canCancelContentTouches in UIScrollView su YES .
  2. Estensione di UIScrollView per sovrascrivere i touchesShouldCancelInContentView:(UIView *)view per restituire YES quando view è un UIButton .

Secondo la documentazione touchesShouldCancelInContentView restituisce ” YES per cancellare ulteriori messaggi di touch da visualizzare, NO per avere la vista continua a ricevere quei messaggi.Il valore restituito predefinito è YES se la vista non è un object UIControl , altrimenti restituisce NO .”

Poiché UIButton è un UIControl l’estensione è necessaria per ottenere canCancelContentTouches affinché abbia effetto che consenta lo scorrimento.

Ho un caso simile a un certo numero di pulsanti su un UIScrollView e voglio scorrere questi pulsanti. All’inizio, ho creato sottoclassi sia UIScrollView che UIButton. Tuttavia, ho notato che la mia sottoclass UIScrollView non ha ricevuto alcun evento touchEnded, quindi ho cambiato solo sottoclass 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 gestisce un sacco di eventi. È necessario gestire i touchDidEnd e premere manualmente i pulsanti all’interno di UIScrollView.

OK ecco la tua risposta:

Sottoclass UIButton. (NOTA: chiamare [super ….] all’inizio di ogni override.

  • Aggiungi una proprietà. Uno di tipo BOOL (chiamato enableToRestore)
  • Aggiungi una proprietà. Uno di tipo CGPoint (chiamato startTouchPosition)
  • in awakeFromNib e initWithFrame, imposta enableToRestore sulla proprietà isEnabled)
  • Sostituisci “tocca Began: withEvent:” per memorizzare l’inizio della posizione di touch.
  • Sostituisci “tocca Sposta: conEvento:” per controllare se c’è movimento orizzontale.
  • Se SÌ, impostare abilitato su NO e selezionato su NO.

Codice d’esempio:

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

Questo attiverà UIScrollView.

Sottoclassi UIScrollView. (NOTA: chiamare [super ….] all’inizio di ogni override.

  • Sostituisci sia “touchesEnded: withEvent:” e “touchesCancelled: withEvent:”
  • Nell’override, reimposta il flag abilitato per tutte le sottoview (e relative sottoview).
  • NOTA: utilizzare una categoria e aggiungere il metodo a 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]; } 
  • Nella categoria:

.

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

EDIT Nota: non ho mai avuto risposta 3 per funzionare. Allo stesso modo: setDelaysContentTouches: NO (impostato nel controller di visualizzazione o in un luogo) deve essere impostato per i migliori risultati in risposta 4. Ciò fornisce una risposta molto veloce ai pulsanti. Impostazione setDelaysContentTouches: YES attribuisce un impatto notevole (150 ms) sul tempo di risposta ai pulsanti e rende la luce, il touch veloce non ansible.

Un altro modo è:
1. Sostituisci il pulsante con un semplice UIView personalizzato
2. Metti il ​​flag “userInterationEnable = yes;” sul metodo init
3. Nella vista sovrascrivi il metodo UIResponder “touchesEnded” qui puoi triggersre l’azione di cui hai bisogno come un pulsante.

Nella mia esperienza, la prima risposta, ovvero la semplice impostazione di delaysContentTouches su YES , non cambia nulla in relazione al problema. I pulsanti continueranno a non fornire risultati di tracciamento alla visualizzazione di scorrimento. La terza risposta è sia semplice che molto utilizzabile. Grazie sieroaoj!

Tuttavia, per il funzionamento della terza risposta è necessario anche delaysContentTouches impostato su YES . In caso contrario, verrà touchesEnded anche il metodo touchesEnded per il tracciamento all’interno della vista. Quindi potrei risolvere il problema con:

  1. Sostituisci il pulsante con un semplice UIView personalizzato
  2. Metti il ​​flag “userInterationEnable = yes;” sul metodo init
  3. Nella vista sovrascrivi il metodo UIResponder “touchesEnded” qui puoi triggersre l’azione tu

Il quarto. imposta delaysContentTouches su YES