2015-06-09 15 views
18

Ho il seguente layout nel mio controller di visualizzazione. Voglio essere in grado di scorrere verticalmente con l'intestazione che scorre fuori dalla vista e UISegmentedControl che si attacca alla parte superiore della vista, oltre al fatto che lo scroll rimanente deve essere gestito dalla Vista raccolta.iOS 7 - Consigli di layout

Tuttavia, sono un po 'confuso su quale sia l'approccio migliore per implementare questo layout.

Layout

ho provato un paio di implementazioni con risultati alterni:

  1. UIScrollView con UICollectionView come subviews: UIScrollView come vista primaria con l'intestazione, segmentato viste controllo e raccolta come controlli figlio . Il problema con questo approccio è che lo scorrimento nidificato non sembra funzionare correttamente. Per poter scorrere l'UIScrollView il rubinetto deve essere al di fuori dell'area CollectionView altrimenti solo i rotoli CollectionView e l'intestazione e il controllo segmentato non si spostano.

  2. Controllo intestazione e segmentato nella cella di intestazione: Ho provato un altro approccio utilizzando un singolo CollectionView. Ho aggiunto l'intestazione e il controllo segmentato come sottoview di una singola cella di intestazione della vista di raccolta. Quando il valore di controllo segmentato è stato modificato, cambio la proprietà dell'origine dati di CollectionView per ottenere le 3 viste richieste per la vista di raccolta. Visivamente tutto funziona perfettamente. L'unico problema qui è la condizione di gara quando si passa rapidamente tra prima, seconda e terza scheda. Carico i dati da un servizio web, se il servizio web richiede tempo e sta ancora caricando i dati e cambio rapidamente le schede quindi corro in bug in cui i dati restituiti sono per una vista collezione diversa da quella attualmente selezionata, molto di problemi di sincronizzazione non funzionante.

  3. Valore costante di aggiornamento per il vincolo di Autolayout: Un altro approccio che ho provato è quello di modificare il valore costante del vincolo di layout automatico applicato alla vista "Intestazione". Poi ho aggiunto un gesto alla vista del controller della vista per tracciare lo scroll, mentre l'utente scorre verticalmente aggiusto la costante del vincolo del layout automatico in modo che la cella "header" non venga visualizzata. Anche in questo caso non sembra funzionare bene, ma suppongo di poterlo modificare, ma sembra una specie di trucco.

Esiste un modo migliore per implementare questo layout?

+1

Si sta utilizzando AutoLayout come requisito? –

+1

sì, è necessario il layout automatico – newbie

+2

La possibile soluzione per il problema riscontrato nel 2o approccio (in realtà, penso che questo problema non dipenda dal layout scelto) può essere trovato nella prima parte di "Creazione di interfacce utente simultanee su iOS "[WWDC 2012] (https://developer.apple.com/videos/wwdc/2012/) sessione. – Anastasia

risposta

2

Penso che tu voglia la stessa funzionalità che ha la pagina di profilo pinterest. Per implementare tale funzionalità in modo semplice, è necessario eseguire le seguenti operazioni.

Passaggio 1: aggiungi UIView come tableHeader Visualizza quelli che si esibiscono durante lo scorrimento.

self.tableHeaderView = yourView 

Fase 2: Aggiungi UISegmentControl nella vista in sezione di intestazione.

- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section{ 
    return your_segmentcontrolView; 

} 

Fase 3: Aggiungere UICollectionView nella prima riga della prima sezione.

Implementando il seguente modo, è possibile ottenere la funzionalità desiderata.

Spero che questo ti aiuti.

+0

Sembra possibile, non sono sicuro di imbattermi nello stesso strano scroll all'interno di un caos di scroll, fondamentalmente è lo stesso della mia soluzione n. 1 invece di scrollview che stai suggerendo di poter usare TableView che usa stessa scrollview. Hai provato questo approccio in passato? – newbie

+0

@newbie, sì ho implementato in una delle app di maggio. mi è stato suggerito 'UITableView' perché l'intestazione della sezione mostra la sua auto aderente alla parte superiore dello schermo fino a quando la nuova sezione non raggiunge la cima. quindi non hai bisogno di giocare con offset. se vuoi giocare con UIScrollview, allora evento non ottieni un buon comportamento come UItableView fornito. –

+0

@newbie, grazie per il tuo apprezzamento. –

8

# 2 sembra una buona soluzione - i gesti di scorrimento saranno più coerenti con ciò che gli utenti si aspettano, poiché è tutto una singola vista di scorrimento. (Sono d'accordo sul fatto che il n. 3 suona come un trucco). Puoi rendere l'intestazione "appiccicosa" con qualche custom layout attributes.

L'unico problema qui è la condizione di gara quando si passa rapidamente tra prima, seconda e terza scheda.

Questo è un problema comune con il caricamento asincrono quando vengono rimosse le viste (specialmente quando si caricano dati in singole celle, che vengono riutilizzate mentre si scorre). È importante che, una volta ricevuti i dati, il numero controlli sempre se il ricevitore lo sta ancora aspettando; Ad esempio, è necessario controllare il valore di controllo segmentato prima di modificare l'origine dei dati di supporto.È inoltre possibile:

  • Utilizzare oggetti di origine dati separati per i diversi segmenti, ognuno dei quali gestisce i propri recuperi di dati in modo che non possano essere confusi.
  • Annulla le richieste in sospeso, se possibile, quando si passa rapidamente le schede, per evitare richieste di rete non necessarie.
  • Memorizza i dati nella cache per evitare di riprenderli ogni volta che si passa da una scheda all'altra.
-5

Tutto quello che posso dire è che è necessario sottoclasse UIView e renderlo un delegato di UIGestureRecognizerDelegate e UICollectionViewDelegate, poi nella sottoclasse UIView, effettuare le seguenti operazioni, non posso dare più informazioni su questo perché il codice , anche se di proprietà di me, è di proprietà fino al punto di probabilmente arrabbiare un bel paio di organizzazioni che ho usato questo per, quindi ecco l'ingrediente segreto:

CGPoint contentOffset = [scrollView contentOffset]; 
CGFloat newHeight = [_headerView maxHeight] - contentOffset.y; 
CGRect frame = [_headerView frame]; 

if (newHeight > [_headerView maxHeight]) { 
    frame.origin.y = contentOffset.y; 
    frame.size.height = [_headerView maxHeight]; 
    [_headerView setFrame:frame]; 
} else if (newHeight < [_headerView minHeight]) { 

    frame.origin.y = contentOffset.y; 
    frame.size.height = [_headerView minHeight]; 
    [_headerView setFrame:frame]; 
} else { 
    frame.origin.y = contentOffset.y; 
    frame.size.height = newHeight; 
    [_headerView setFrame:frame]; 
} 
if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) { 
    return [_delegate scrollViewDidScroll:scrollView]; 
} 

devi sottoclasse un'altra UIView che viene definito come il colpo di testa per questo UiCollectionView personalizzato. Quindi, è necessario dichiarare la vista di intestazione personalizzata UIView all'interno della sottoview personalizzata del delegato UIView/UICollectionView e quindi impostare l'intestazione di tale sottoview personalizzata all'interno di UICollctionViewdelegate. Dovresti quindi inserire questa sottoclasse composta di UIView/UIcollectionView nel tuo UIViewController. Oh sì, e nel layoutSubViews, assicurati di fare i calcoli di altezza che sono passati attraverso una sottoclasse a doppio strato. Quindi, si avrà i seguenti file:

  1. UIVew questo è il delegato del UICollectionView e quello che ho detto prima

  2. UIView questo è un UISCrollViewDelegate e questa è la vista intestazione

  3. UIViewController che inserisce la sottoclasse UIView nel numero 1

  4. UIView sottoclasse del numero 1 che inserisce il numero 2 e lo imposta come intestazione

Nella parte 4, assicuratevi di fare qualcosa di simile:

- (CGFloat)maxHeight 
{ 
    if (SCREEN_WIDTH == 414) 
    { 
     return 260; 

    }else if (SCREEN_WIDTH == 375) 
    { 
     return 325; 

    }else 
    { 
     return 290; 
    } 
} 

- (CGFloat)minHeight 
{ 
    if (SCREEN_WIDTH == 414) 
    { 

     return 90; 
    }else if (SCREEN_WIDTH == 375) 
    { 
     return 325; 
    }else 
    { 
     return 290; 
    } 
} 

Questo sarà quindi passare alla sottoclasse UIView che è una sottoclasse composto come ho già spiegato. L'idea è di catturare l'intestazione maxHeight nella sottoclasse di questa intestazione UIView (numero 2 sopra) e quindi passarla nella sottoclasse UIView principale che intercetta questi valori nello scrollViewDidScroll.

Ultimo boccaglio di informazioni, assicurati di configurare il layout delle immagini in tutti i modi per intercettare gli eventi di scorrimento. Per esempio nel precedente numero 1, il metodo layoutSubviews è questo:

- (void)layoutSubviews 
{ 
    CGRect frame = [_headerView frame]; 
    frame.size.width = [self frame].size.width; 
    [_headerView setFrame:frame]; 
    [super layoutSubviews]; 
} 

Questo è tutto quello che posso dare a voi, vorrei poter postare di più, ma questo dovrebbe darvi un'idea di come si fa in ambienti di produzione per le app di grande successo che vedi in natura.

Un'altra cosa da notare. Quando inizierai a intraprendere delle implementazioni intense come questa, non sorprenderti di sapere che, ad esempio, un controller di visualizzazione singola in un'app che funziona con metodi come quelli che ho spiegato avrà ovunque da 30-40 sottoclassi personalizzate che sono sottoclassi a loro volta o sottoclassi composte o sottoclassi delle mie sottoclassi o sottoclassi personali.Te lo sto dicendo per farti un'idea di quanto codice è necessario per farlo correttamente, non per spaventarti, ma per farti sapere che potrebbe volerci un po 'per avere ragione, e non prenderti a calci nel sedere se ci vuole un po 'per fare un lavoro. In bocca al lupo!!

1
  • Aggiungi vista intestazione, Vista corpo (Vista segmento di trattenimento & Vista raccolta) in vista di scorrimento.
  • Inizialmente impostare la proprietà userInteractionEnabled su "NO" per la vista raccolta.
  • Traccia sempre l'insetto della vista di scorrimento.
  • Se la coordinata y dell'insetto a scorrimento è maggiore dell'altezza della vista dell'intestazione, impostare la proprietà userInteractionEnabled su "SÌ" in modo che sia possibile scorrere la vista di raccolta successiva.
  • Se l'utente scorre fuori dalla vista di scorrimento e tenta di portare la vista dell'intestazione verso il basso, ovvero la vista di scorrimento dell'insetto a y è inferiore all'altezza della vista dell'intestazione, quindi modificare immediatamente la modalità di visualizzazione utente della vista raccolta e consentire all'utente di scorrere la vista di scorrimento fino alla cima.
2

Un approccio alternativo si potrebbe prendere in considerazione:

Utilizzare un UITableView per contenere l'interfaccia utente

  • Creare un UITableView, e impostare l'intestazione come headerView del UITableView.
  • Utilizzare una sectionHeader per contenere il segmentedControl.
  • Inserisci la tua raccoltaView all'interno di un singolo UITableViewCell. In alternativa, potresti essere in grado di utilizzare il footerView di UITableView per contenere gridView.

Usando il sectionHeader, questo dovrebbe permettere l'intestazione di scorrere fuori dal campo visivo, ma poi il sectionHeader si attacchi al di sotto della navigationBar o superiore del contentView fino un'altra sezione entra in vista (e nel tuo caso solo tu avere una sezione.)

1

Piuttosto che implementarlo a mano, è possibile utilizzare una libreria/cocoapod per configurarlo.Questo sembra una buona misura: https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout

Inoltre, il codice è open-source, quindi è sempre possibile modificare secondo necessità.

+0

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il link per riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. –

+0

Grazie per l'input @MayankJain. Il link rimanda a una libreria che fa ciò che la domanda sta ponendo riguardo (al contrario di un tutorial o qualcosa del genere), quindi aggiornerò la mia risposta per chiarire questo punto. – BevTheDev