2012-10-10 55 views
23

¿Alguien ha implementado una vista de decoración para iOS 6 UICollectionView? Es imposible para encontrar cualquier tutorial sobre la implementación de una vista de decoración en la web. Básicamente en mi aplicación tengo varias secciones, y solo quería mostrar una vista de decoración detrás de cada sección. Esto debería ser simple de implementar, pero no estoy teniendo suerte. Esto me está volviendo loco ... Gracias.UICollectionView Decoration View

+0

Has marcado este tutorial http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12. No estoy seguro de si ya lo han hecho. Pero vale la pena verificarlo allí. – iDev

+0

lo siento, pero ¿puedes explicar a qué te refieres con la vista de decoración? – geraldWilliam

+0

@geraldWilliam, mira el enlace de arriba en mi comentario. Tienen un buen tutorial que explica. – iDev

Respuesta

22

Tengo esta trabajando con un diseño personalizado con lo siguiente:

crear una subclase de UICollectionReusableView y, por ejemplo, añadir una UIImageView a ella:

@implementation AULYFloorPlanDecorationViewCell 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     UIImage *backgroundImage = [UIImage imageNamed:@"Layout.png"]; 
     UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; 
     imageView.image = backgroundImage; 
     [self addSubview:imageView]; 
    } 
    return self; 
} 

@end 

Luego, en su controlador en viewDidLoad registrar esta subclase con el siguiente código (código sustituir con su diseño personalizado)

AULYAutomationObjectLayout *automationLayout = (AULYAutomationObjectLayout *)self.collectionView.collectionViewLayout; 
[automationLayout registerClass:[AULYFloorPlanDecorationViewCell class] forDecorationViewOfKind:@"FloorPlan"]; 

En su diseño personalizado a continuación, poner en práctica el followin métodos g (o similares):

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath 
{ 
    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath]; 
    layoutAttributes.frame = CGRectMake(0.0, 0.0, self.collectionViewContentSize.width, self.collectionViewContentSize.height); 
    layoutAttributes.zIndex = -1; 
    return layoutAttributes; 
} 

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
{ 
    NSMutableArray *allAttributes = [[NSMutableArray alloc] initWithCapacity:4]; 

    [allAttributes addObject:[self layoutAttributesForDecorationViewOfKind:@"FloorPlan" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]]; 

    for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) 
    { 
     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 
     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath]; 
     [allAttributes addObject:layoutAttributes]; 
    } 
    return allAttributes; 
} 

No parece haber ninguna documentación para ello, pero el siguiente documento me consiguió en el camino correcto: Collection View Programming Guide for iOS

ACTUALIZACIÓN: Es probable que sea mejor subclase UICollectionReusableView para una vista la decoración en lugar de UICollectionViewCell

+0

Gracias, esto me puso en el camino correcto. Parece que solo está mostrando una única vista de decoración para la sección 0. Mi vista de colección tiene varias secciones. Todavía necesito descubrir cómo mostrar la misma vista de decoración para cada sección con diferente cantidad de elementos. – vtruong

+0

Sí, solo visualizo una vista de decoración, pero podría mostrar más vistas de decoración agregando más líneas en el layoutAttributesForElementsInRect: '[allAttributes addObject: [self layoutAttributesForDecorationViewOfKind: @" FloorPlan "atIndexPath: [NSIndexPath indexPathForItem: 0 inSection: 1]] ]; '(y puede agregar más para la sección 2, 3, 4, etc. Luego debe asegurarse de establecer los atributos de diseño de modo que los límites sean correctos para esa sección. Para averiguar la cantidad de secciones y cuántas Los elementos que contienen deben poder acceder a 'self.collectionView.dataSource' – dominikk

+1

¿Debo subir la subclase 'UICollectionViewLayout' para usar Decoration Views? ¿Se puede hacer desde FlowLayout? –

5

Así es como hacerlo en MonoTouch:

public class DecorationView : UICollectionReusableView 
{ 
    private static NSString classId = new NSString ("DecorationView"); 
    public static NSString ClassId { get { return classId; } } 

    UIImageView blueMarble; 

    [Export("initWithFrame:")] 
    public DecorationView (RectangleF frame) : base(frame) 
    { 
     blueMarble = new UIImageView (UIImage.FromBundle ("bluemarble.png")); 

     AddSubview (blueMarble);  
    } 
} 


public class SimpleCollectionViewController : UICollectionViewController 
{ 
    public override void ViewDidLoad() 
    { 
     base.ViewDidLoad(); 
     //Register the cell class (code for AnimalCell snipped) 
     CollectionView.RegisterClassForCell (typeof(AnimalCell), AnimalCell.ClassId); 
     //Register the supplementary view class (code for SideSupplement snipped) 
     CollectionView.RegisterClassForSupplementaryView (typeof(SideSupplement), UICollectionElementKindSection.Header, SideSupplement.ClassId); 

     //Register the decoration view 
     CollectionView.CollectionViewLayout.RegisterClassForDecorationView (typeof(DecorationView), DecorationView.ClassId); 
    } 

//...snip... 
} 

public class LineLayout : UICollectionViewFlowLayout 
{ 
    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (RectangleF rect) 
    { 
     var array = base.LayoutAttributesForElementsInRect (rect); 
     /* 
     ...snip content relating to cell layout... 
     */ 
     //Add decoration view 
     var attributesWithDecoration = new List<UICollectionViewLayoutAttributes> (array.Length + 1); 
     attributesWithDecoration.AddRange (array); 
     var decorationIndexPath = NSIndexPath.FromIndex (0); 
     var decorationAttributes = LayoutAttributesForDecorationView (DecorationView.ClassId, decorationIndexPath); 
     attributesWithDecoration.Add (decorationAttributes); 

     var extended = attributesWithDecoration.ToArray<UICollectionViewLayoutAttributes>(); 

     return extended; 
    } 

    public override UICollectionViewLayoutAttributes LayoutAttributesForDecorationView (NSString kind, NSIndexPath indexPath) 
    { 
     var layoutAttributes = UICollectionViewLayoutAttributes.CreateForDecorationView (kind, indexPath); 
     layoutAttributes.Frame = new RectangleF (0, 0, CollectionView.ContentSize.Width, CollectionView.ContentSize.Height); 
     layoutAttributes.ZIndex = -1; 
     return layoutAttributes; 
    } 
//...snip... 
} 

con un resul final T similar a: enter image description here

-5

si desea agregar decorationview (encabezado/pie de página) en vistacoleccion

compruebe este enlace en esta cabecera & pie de página tanto agregado como DecorationView

http://www.appcoda.com/ios-collection-view-tutorial/

+1

Esas son vistas suplementarias, por ejemplo, encabezado y pie de página de sección, no vistas de decoración. – nirvana

35

Aquí una vista de diseño de vista vista de decoración vista tutorial en Swift (esto es Swift 3, Xcode 8 semilla 6).


Las vistas de decoración no son una característica UICollectionView; pertenecen esencialmente al UICollectionViewLayout. Ningún método UICollectionView (o métodos delegados o fuente de datos) menciona vistas de decoración. UICollectionView no sabe nada de ellos; simplemente hace lo que se le dice.

Para suministrar cualquier vista de decoración, necesitará una subclase UICollectionViewLayout; esta subclase puede definir libremente sus propias propiedades y delegar métodos de protocolo que personalicen cómo se configuran sus vistas de decoración, pero eso depende completamente de usted.

Para ilustrar, voy a subclase UICollectionViewFlowLayout para imponer una etiqueta de título en la parte superior del rectángulo de contenido de la vista de colección. Este es probablemente un uso tonto de una vista de decoración, pero ilustra perfectamente los principios básicos. Para simplificar, comenzaré por codificar todo, sin darle al cliente la posibilidad de personalizar ningún aspecto de esta vista.

Hay cuatro pasos para implementar una vista de la decoración en una subclase diseño:

  1. definir una subclase UICollectionReusableView.

  2. Registre el subclase UICollectionReusableView con el diseño (no la vista de colección), llamando register(_:forDecorationViewOfKind:). El inicializador del diseño es un buen lugar para hacer esto.

  3. Implemente layoutAttributesForDecorationView(ofKind:at:) para devolver los atributos de diseño que posicionan UICollectionReusableView. Para construir los atributos de diseño, llame al init(forDecorationViewOfKind:with:) y configure los atributos.

  4. Anule layoutAttributesForElements(in:) de modo que el resultado de layoutAttributesForDecorationView(ofKind:at:) se incluya en la matriz devuelta.

El último paso es lo que hace que la vista de decoración aparezca en la vista de colección. Cuando la vista de colección llama al layoutAttributesForElements(in:), descubre que la matriz resultante incluye atributos de diseño para una vista de decoración de un tipo especificado. La vista de colección no sabe nada acerca de las vistas de decoración, por lo que vuelve al diseño, solicitando una instancia real de este tipo de vista de decoración. Ha registrado este tipo de vista de decoración para que se corresponda con su subclase UICollectionReusableView, por lo que se crea una instancia de su subclase UICollectionReusableView y se devuelve esa instancia, y la vista de colección la ubica de acuerdo con los atributos de diseño.

Así que sigamos los pasos. Definir la subclase UICollectionReusableView:

class MyTitleView : UICollectionReusableView { 
    weak var lab : UILabel! 
    override init(frame: CGRect) { 
     super.init(frame:frame) 
     let lab = UILabel(frame:self.bounds) 
     self.addSubview(lab) 
     lab.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
     lab.font = UIFont(name: "GillSans-Bold", size: 40) 
     lab.text = "Testing" 
     self.lab = lab 
    } 
    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 
} 

Pasamos ahora a nuestra subclase UICollectionViewLayout, que llamaré a MyFlowLayout. Registramos MyTitleView en el inicializador del diseño; También me he definido algunas propiedades privadas que necesitará para el resto de pasos:

private let titleKind = "title" 
private let titleHeight : CGFloat = 50 
private var titleRect : CGRect { 
    return CGRect(10,0,200,self.titleHeight) 
} 
override init() { 
    super.init() 
    self.register(MyTitleView.self, forDecorationViewOfKind:self.titleKind) 
} 

Implementar layoutAttributesForDecorationView(ofKind:at:):

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) 
    -> UICollectionViewLayoutAttributes? { 
     if elementKind == self.titleKind { 
      let atts = UICollectionViewLayoutAttributes(
       forDecorationViewOfKind:self.titleKind, with:indexPath) 
      atts.frame = self.titleRect 
      return atts 
     } 
     return nil 
} 

Anulación layoutAttributesForElements(in:); la ruta de índice aquí es arbitraria (lo ignoré en el código anterior):

override func layoutAttributesForElements(in rect: CGRect) 
    -> [UICollectionViewLayoutAttributes]? { 
     var arr = super.layoutAttributesForElements(in: rect)! 
     if let decatts = self.layoutAttributesForDecorationView(
      ofKind:self.titleKind, at: IndexPath(item: 0, section: 0)) { 
       if rect.contains(decatts.frame) { 
        arr.append(decatts) 
       } 
     } 
     return arr 
} 

¡Esto funciona! Aparecerá una etiqueta de título que dice `` Prueba '' en la parte superior de la vista de colección.


Ahora le mostraré cómo hacer que la etiqueta se pueda personalizar. En lugar del título "Prueba", le permitiremos al cliente establecer una propiedad que determine el título. Voy a dar mi diseño subclase un público title propiedad:

class MyFlowLayout : UICollectionViewFlowLayout { 
    var title = "" 
    // ... 
} 

Quien utiliza esta disposición debe establecer esta propiedad. Por ejemplo, supongamos que este punto de vista la colección está mostrando los 50 estados de Estados Unidos:

func setUpFlowLayout(_ flow:UICollectionViewFlowLayout) { 
    flow.headerReferenceSize = CGSize(50,50) 
    flow.sectionInset = UIEdgeInsetsMake(0, 10, 10, 10) 
    (flow as? MyFlowLayout)?.title = "States" // * 
} 

Llegamos ahora a un rompecabezas curiosa.Nuestro diseño tiene una propiedad title, cuyo valor debe comunicarse de alguna manera a nuestra instancia MyTitleView. Pero, ¿cuándo puede suceder eso? No estamos a cargo de instanciar MyTitleView; sucede automáticamente, cuando la vista de colección pregunta por la instancia detrás de las escenas. No hay un momento en el que coincidan la instancia de MyFlowLayout y la instancia de MyTitleView.

La solución es utilizar los atributos de diseño como un mensajero. MyFlowLayout nunca cumple con MyTitleView, pero sí crea el objeto de atributos de diseño que se pasa a la vista de colección para configurar MyFlowLayout. Entonces, el atributo de atributos de diseño es como un sobre. Subclasificando UICollectionViewLayoutAttributes, podemos incluir en el sobre cualquier información que nos gusta - tales como título:

class MyTitleViewLayoutAttributes : UICollectionViewLayoutAttributes { 
    var title = "" 
} 

Hay nuestro sobre! Ahora reescribimos nuestra implementación de layoutAttributesForDecorationView. Cuando una instancia de la disposición de los atributos del objeto, creamos una instancia nuestra subclase y establezca su propiedad title:

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) -> 
    UICollectionViewLayoutAttributes? { 
     if elementKind == self.titleKind { 
      let atts = MyTitleViewLayoutAttributes(// * 
       forDecorationViewOfKind:self.titleKind, with:indexPath) 
      atts.title = self.title // * 
      atts.frame = self.titleRect 
      return atts 
     } 
     return nil 
} 

Por último, en MyTitleView, ponemos en práctica el método apply(_:). Se llamará a esto cuando la vista de colección configure la vista de decoración, ¡con el objeto atributos de disposición como su parámetro! Así que saque el title y lo usamos como el texto de nuestra etiqueta:

class MyTitleView : UICollectionReusableView { 
    weak var lab : UILabel! 
    // ... the rest as before ... 
    override func apply(_ atts: UICollectionViewLayoutAttributes) { 
     if let atts = atts as? MyTitleViewLayoutAttributes { 
      self.lab.text = atts.title 
     } 
    } 
} 

Es fácil ver cómo se puede ampliar el ejemplo, para realizar funciones tales como la etiqueta de fuente y la altura personalizable. Como estamos subclasificando UICollectionViewFlowLayout, es posible que se necesiten algunas modificaciones adicionales para dejar espacio para la vista de decoración presionando hacia abajo los otros elementos. Además, técnicamente, debemos anular isEqual(_:) en MyTitleView para diferenciar entre diferentes títulos. Todo eso queda como un ejercicio para el lector.

+0

¡No entiendo por qué esta no es la mejor respuesta! Buena cosa. –

+0

@GazLong ¡Gracias! Hay más de donde vino eso; es cortar y pegar directamente de mi libro. – matt

+0

@matt ¡Muchas gracias por la sección sobre el uso de los atributos de diseño como un mensajero para comunicarse con la vista de decoración! Es una API interesante: no crea una instancia directa ni tiene una referencia a la vista de decoración. –