Domanda

Ho un grafico disegnato all'interno di un UIScrollView . È un grande UIView che utilizza una sottoclasse personalizzata di CATiledLayer come livello.

Quando ingrandisco e rimpicciolisco il UIScrollView , voglio che il grafico si ridimensioni in modo dinamico come quando restituisco il grafico da viewForZoomingInScrollView . Tuttavia, il grafico si ridisegna al nuovo livello di zoom e voglio reimpostare la scala di trasformazione su 1x1 in modo che la volta successiva che l'utente ingrandisce, la trasformazione inizia dalla vista corrente. Se ripristino la trasformazione su Identità in scrollViewDidEndZooming , funziona nel simulatore, ma genera un EXC_BAD_ACCSES sul dispositivo.

Questo non risolve nemmeno il problema interamente sul simulatore, perché la prossima volta che l'utente ingrandisce, la trasformazione si reimposta a qualsiasi livello di zoom si trovasse, e quindi sembra, se fossi ingrandito a 2x, per esempio, è improvvisamente a 4x. Quando finisco lo zoom, finisce con la scala corretta, ma l'atto effettivo di zoom sembra male.

Quindi prima: come posso consentire al grafico di ridisegnare se stesso alla scala standard di 1x1 dopo lo zoom e come posso avere uno zoom uniforme in tutto?

Modifica: Nuovi risultati L'errore sembra essere " [CALayer retainCount]: messaggio inviato all'istanza deallocata "

Non sto mai deallocando alcun livello da solo. Prima non stavo nemmeno cancellando nessuna vista o altro. Questo errore veniva generato dallo zoom e anche dalla rotazione. Se elimino l'oggetto prima della rotazione e lo aggiungo successivamente, non genera l'eccezione. Questa non è un'opzione per lo zoom.

È stato utile?

Soluzione

Non posso aiutarti con l'arresto anomalo, oltre a dirti di controllare e assicurarti di non rilasciare inavvertitamente una vista o un livello in qualche punto all'interno del tuo codice. Ho visto il simulatore gestire i tempi dei rilasci automatici in modo diverso rispetto al dispositivo (il più delle volte quando sono coinvolti i thread).

Il ridimensionamento della vista è un problema con UIScrollView in cui mi sono imbattuto, però. Durante un evento di ingrandimento del pizzico, UIScrollView acquisirà la vista specificata nel metodo delegato viewForZoomingInScrollView: e applicherà una trasformazione ad esso. Questa trasformazione fornisce un ridimensionamento uniforme della vista senza dover ridisegnarla per ogni fotogramma. Al termine dell'operazione di zoom, verrà chiamato il tuo metodo delegato scrollViewDidEndZooming: withView: atScale: e ti darà la possibilità di eseguire un rendering più di alta qualità della tua vista con il nuovo fattore di scala. In genere, si consiglia di reimpostare la trasformazione sulla vista su CGAffineTransformIdentity e di ridisegnare manualmente la vista nella nuova scala di dimensioni.

Tuttavia, ciò causa un problema perché UIScrollView non sembra monitorare la trasformazione della visualizzazione del contenuto, quindi alla successiva operazione di zoom imposta la trasformazione della visualizzazione del contenuto su qualunque sia il fattore di scala generale . Dato che hai ridisegnato manualmente la vista all'ultimo fattore di scala, aumenta il ridimensionamento, che è quello che stai vedendo.

Come soluzione alternativa, utilizzo una sottoclasse UIView per la mia visualizzazione del contenuto con i seguenti metodi definiti:

- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
    [super setTransform:newTransform];
}

- (void)setTransform:(CGAffineTransform)newValue;
{
    [super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}

dove previousScale è una variabile di istanza float della vista. Quindi implemento il metodo di zoom delegato come segue:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
    [contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
    contentView.previousScale = scale;
    scrollView.contentSize = contentView.frame.size;
}

In questo modo, le trasformazioni inviate alla vista del contenuto vengono regolate in base alla scala in cui la vista è stata ridisegnata l'ultima volta. Al termine dello zoom con pizzico, la trasformazione viene reimpostata su una scala di 1,0 ignorando la regolazione nel normale metodo setTransform: . Questo sembra fornire il corretto comportamento di ridimensionamento mentre ti consente di disegnare una visione nitida al completamento di uno zoom.

AGGIORNAMENTO (23/07/2010): iPhone OS 3.2 e versioni successive hanno modificato il comportamento delle visualizzazioni di scorrimento per quanto riguarda lo zoom. Ora, un UIScrollView rispetterà la trasformazione dell'identità che applichi a una visualizzazione del contenuto e fornirà solo il fattore di scala relativo in -scrollViewDidEndZooming: withView: atScale: . Pertanto, il codice sopra riportato per una sottoclasse UIView è necessario solo per i dispositivi che eseguono versioni del sistema operativo iPhone precedenti alla 3.2.

Altri suggerimenti

Grazie a tutte le risposte precedenti, ed ecco la mia soluzione.
Implementa i metodi UIScrollViewDelegate :

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return tmv;
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    CGPoint contentOffset = [tmvScrollView contentOffset];   
    CGSize  contentSize   = [tmvScrollView contentSize];
     CGSize  containerSize = [tmv frame].size;

    tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
    tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;

    previousScale *= scale;

    [tmvScrollView setZoomScale:1.0f];

    [tmvScrollView setContentOffset:contentOffset];
    [tmvScrollView setContentSize:contentSize];
    [tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];  

    [tmv reloadData];
}
  • tmv è la mia sottoclasse di UIView
  • tmvScrollView & # 8212; uscita su UIScrollView
  • imposta maximumZoomScale e minimumZoomScale prima
  • crea scala precedente e imposta il suo valore su 1.0f

Funziona perfettamente per me.

BR, Eugene.

Stavo solo cercando di ripristinare al caricamento la scala di zoom predefinita, perché quando lo ingrandisco, scrollview ha la stessa scala di zoom per la prossima volta fino a quando non viene deallocata.

-(void) viewWillAppear:(BOOL)animated {
      [self.scrollView setZoomScale:1.0f];
}

Modificato il gioco.

Ho una discussione dettagliata di come (e perché) lo zoom UIScrollView funziona su github.com/andreyvit/ScrollingMadness/ .

(Il collegamento contiene anche una descrizione di come ingrandire UIScrollView a livello di programmazione, come emulare paging + zoom + scorrimento in stile Photo Library, un progetto di esempio e la classe ZoomScrollView che incapsula parte della magia dello zoom.)

Quote:

UIScrollView non ha una nozione di & # 8220; livello di zoom corrente & # 8221; poiché ogni sottoview che contiene può avere il proprio livello di zoom corrente. Si noti che in UIScrollView non è presente alcun campo per mantenere l'attuale livello di zoom. Tuttavia sappiamo che qualcuno memorizza quel livello di zoom, perché se esegui il pizzico-zoom di una sottoview, reimposta la sua trasformazione in CGAffineTransformIdentity e poi pizzichi di nuovo, noterai che il precedente livello di zoom della subview è stato ripristinato.

In effetti, se si osserva lo smontaggio, è UIView che memorizza il proprio livello di zoom (all'interno dell'oggetto UIGestureInfo a cui punta il campo _gestureInfo). Ha anche una serie di simpatici metodi non documentati come zoomScale e setZoomScale: animato: . (Intendiamoci, ha anche un sacco di metodi relativi alla rotazione, forse un giorno avremo presto il supporto per i gesti di rotazione.)

Tuttavia, se creiamo una nuova UIView solo per lo zoom e aggiungiamo la nostra visualizzazione zoomabile reale come figlio, inizieremo sempre con il livello di zoom 1.0. La mia implementazione dello zoom programmatico si basa su questo trucco.

Un approccio abbastanza affidabile, appropriato per iOS 3.2 e 4.0 e versioni successive, è il seguente. È necessario essere pronti a fornire la vista scalabile (la sottoview principale della vista a scorrimento) in qualsiasi scala richiesta. Quindi in scrollViewDidEndZooming: rimuoverai la versione sfocata trasformata in scala di questa vista e la sostituirai con una nuova vista disegnata sulla nuova scala.

Dovrai:

  • Un ivar per mantenere la scala precedente; chiamiamolo oldScale

  • Un modo per identificare la vista scalabile, sia per viewForZoomingInScrollView: che per scrollViewDidEndZooming: ; qui, applicherò un tag (999)

  • Un metodo che crea la vista scalabile, la contrassegna e la inserisce nella vista di scorrimento (qui la chiamerò addNewScalableViewAtScale: ). Ricorda, deve fare tutto in base alla scala: ridimensiona la vista e le sue visualizzazioni secondarie o disegno e ridimensiona il contenuto della vista di scorrimento.

  • Costanti per minimumZoomScale e maximumZoomScale. Questi sono necessari perché quando sostituiamo la vista in scala con la versione più grande dettagliata, il sistema penserà che la scala corrente sia 1, quindi dobbiamo ingannarla per consentire all'utente di ridimensionare la vista.

Molto bene allora. Quando si crea la vista di scorrimento, si inserisce la vista scalabile alla scala 1.0. Questo potrebbe essere nel tuo viewDidLoad , ad esempio ( sv è la vista di scorrimento):

[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;

Quindi nel tuo scrollViewDidEndZooming: fai la stessa cosa, ma compensando il cambio di scala:

UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
sv.maximumZoomScale = MAX / newscale;

Nota che la soluzione fornita da Brad ha un problema però: se continui a ingrandire a livello di codice (diciamo in caso di doppio tocco) e consenti all'utente di ridurre (rimpicciolendo) manualmente, dopo qualche tempo la differenza tra la scala reale e la scala che UIScrollView sta monitorando diventerà troppo grande, quindi il precedenteScale a un certo punto cadrà dalla precisione del float che alla fine si tradurrà in un comportamento imprevedibile

Swift 4. * e Xcode 9.3

Impostando la scala dello zoom scrollView su 0, ScrollView tornerà al suo stato iniziale.

self.scrollView.setZoomScale(0.0, animated: true)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top