Share via


Viste della raccolta in Xamarin.iOS

Le visualizzazioni raccolta consentono di visualizzare il contenuto usando layout arbitrari. Consentono di creare facilmente layout simili a griglia predefiniti, supportando anche layout personalizzati.

Le visualizzazioni della raccolta, disponibili nella UICollectionView classe , sono un nuovo concetto in iOS 6 che introduce la presentazione di più elementi sullo schermo usando layout. I modelli per fornire dati a un UICollectionView oggetto per creare elementi e interagire con tali elementi seguono gli stessi modelli di delega e origine dati comunemente usati nello sviluppo iOS.

Tuttavia, le visualizzazioni della raccolta funzionano con un sottosistema di layout indipendente dall'oggetto UICollectionView stesso. Pertanto, semplicemente fornendo un layout diverso può facilmente modificare la presentazione di una visualizzazione raccolta.

iOS fornisce una classe di layout denominata UICollectionViewFlowLayout che consente la creazione di layout basati su linea, ad esempio una griglia senza lavoro aggiuntivo. Inoltre, è possibile creare layout personalizzati che consentano qualsiasi presentazione che si possa immaginare.

Nozioni di base su UICollectionView

La UICollectionView classe è costituita da tre elementi diversi:

  • Celle : viste guidate dai dati per ogni elemento
  • Visualizzazioni supplementari: viste guidate dai dati associate a una sezione.
  • Visualizzazioni di decorazione: viste non guidate dai dati create da un layout

Cells

Le celle sono oggetti che rappresentano un singolo elemento nel set di dati presentato dalla visualizzazione raccolta. Ogni cella è un'istanza della UICollectionViewCell classe , composta da tre visualizzazioni diverse, come illustrato nella figura seguente:

Ogni cella è composta da tre visualizzazioni diverse, come illustrato di seguito

La UICollectionViewCell classe ha le proprietà seguenti per ognuna di queste visualizzazioni:

  • ContentView : questa visualizzazione contiene il contenuto presentato dalla cella. Viene eseguito il rendering nell'ordine z più alto sullo schermo.
  • SelectedBackgroundView - Le celle hanno il supporto predefinito per la selezione. Questa visualizzazione viene utilizzata per indicare visivamente che è selezionata una cella. Viene eseguito il rendering appena sotto l'oggetto ContentView quando viene selezionata una cella.
  • BackgroundView – Le celle possono anche visualizzare uno sfondo, presentato da BackgroundView . Il rendering di questa vista viene eseguito sotto l'oggetto SelectedBackgroundView .

Impostando in ContentView modo che sia minore di BackgroundView e SelectedBackgroundView, può BackgroundView essere usato per incorniciare visivamente il contenuto, mentre SelectedBackgroundView verrà visualizzato quando viene selezionata una cella, come illustrato di seguito:

Elementi di cella diversi

Le celle nello screenshot precedente vengono create ereditando da UICollectionViewCell e impostando rispettivamente le ContentViewproprietà , SelectedBackgroundView e BackgroundView , come illustrato nel codice seguente:

public class AnimalCell : UICollectionViewCell
{
        UIImageView imageView;

        [Export ("initWithFrame:")]
        public AnimalCell (CGRect frame) : base (frame)
        {
            BackgroundView = new UIView{BackgroundColor = UIColor.Orange};

            SelectedBackgroundView = new UIView{BackgroundColor = UIColor.Green};

            ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
            ContentView.Layer.BorderWidth = 2.0f;
            ContentView.BackgroundColor = UIColor.White;
            ContentView.Transform = CGAffineTransform.MakeScale (0.8f, 0.8f);

            imageView = new UIImageView (UIImage.FromBundle ("placeholder.png"));
            imageView.Center = ContentView.Center;
            imageView.Transform = CGAffineTransform.MakeScale (0.7f, 0.7f);

            ContentView.AddSubview (imageView);
        }

        public UIImage Image {
            set {
                imageView.Image = value;
            }
        }
}

Visualizzazioni supplementari

Le visualizzazioni supplementari sono viste che presentano informazioni associate a ogni sezione di un oggetto UICollectionView. Come le celle, le visualizzazioni supplementari sono guidate dai dati. Dove Cells presenta i dati dell'elemento da un'origine dati, Le visualizzazioni supplementari presentano i dati della sezione, ad esempio le categorie di libri in una libreria o il genere di musica in una raccolta musicale.

Ad esempio, è possibile usare una visualizzazione supplementare per presentare un'intestazione per una sezione specifica, come illustrato nella figura seguente:

Visualizzazione supplementare usata per presentare un'intestazione per una sezione specifica, come illustrato di seguito

Per usare una visualizzazione supplementare, prima di tutto deve essere registrata nel ViewDidLoad metodo :

CollectionView.RegisterClassForSupplementaryView (typeof(Header), UICollectionElementKindSection.Header, headerId);

La vista deve quindi essere restituita usando , creata tramite GetViewForSupplementaryElementDequeueReusableSupplementaryViewe eredita da UICollectionReusableView. Il frammento di codice seguente produrrà il controllo SupplementaryView illustrato nello screenshot precedente:

public override UICollectionReusableView GetViewForSupplementaryElement (UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
        {
            var headerView = (Header)collectionView.DequeueReusableSupplementaryView (elementKind, headerId, indexPath);
            headerView.Text = "Supplementary View";
            return headerView;
        }

Le visualizzazioni supplementari sono più generiche di intestazioni e piè di pagina. Possono essere posizionati ovunque nella visualizzazione raccolta e possono essere costituiti da qualsiasi visualizzazione, rendendo il loro aspetto completamente personalizzabile.

Viste di decorazione

Le visualizzazioni delle decorazioni sono viste puramente visive che possono essere visualizzate in un oggetto UICollectionView. A differenza delle celle e delle visualizzazioni supplementari, non sono guidate dai dati. Vengono sempre creati all'interno della sottoclasse di un layout e successivamente possono cambiare come layout del contenuto. Ad esempio, una visualizzazione decorazione può essere usata per presentare una visualizzazione di sfondo che scorre con il contenuto in UICollectionView, come illustrato di seguito:

Visualizzazione decorazione con sfondo rosso

Il frammento di codice seguente modifica lo sfondo in rosso nella classe samples CircleLayout :

public class MyDecorationView : UICollectionReusableView
 {
   [Export ("initWithFrame:")]
   public MyDecorationView (CGRect frame) : base (frame)
   {
     BackgroundColor = UIColor.Red;
   }
 }

Origine dati

Come per altre parti di iOS, ad esempio UITableView e MKMapView, UICollectionView ottiene i dati da un'origine dati, esposta in Xamarin.iOS tramite la UICollectionViewDataSource classe . Questa classe è responsabile della fornitura di contenuto a UICollectionView , ad esempio:

  • Celle : restituite dal GetCell metodo .
  • Visualizzazioni supplementari: restituite dal GetViewForSupplementaryElement metodo .
  • Numero di sezioni : restituite dal NumberOfSections metodo . Il valore predefinito è 1 se non è implementato.
  • Numero di elementi per sezione : restituiti dal GetItemsCount metodo .

UICollectionViewController

Per praticità, la UICollectionViewController classe è disponibile. Viene configurato automaticamente come delegato, descritto nella sezione successiva, e l'origine dati per la relativa UICollectionView vista.

Come con UITableView, la classe chiamerà solo l'origine UICollectionView dati per ottenere celle per gli elementi presenti sullo schermo. Le celle che scorrono dallo schermo vengono inserite in una coda per il riutilizzo, come illustrato nell'immagine seguente:

Le celle che scorrono dalla schermata vengono inserite in una coda per il riutilizzo, come illustrato di seguito

Il riutilizzo delle celle è stato semplificato con UICollectionView e UITableView. Non è più necessario creare una cella direttamente nell'origine dati se non ne è disponibile una nella coda di riutilizzo, perché le celle vengono registrate nel sistema. Se una cella non è disponibile quando si effettua la chiamata a de-accodare la cella dalla coda di riutilizzo, iOS lo creerà automaticamente in base al tipo o al nib registrato. La stessa tecnica è disponibile anche per le visualizzazioni supplementari.

Si consideri ad esempio il codice seguente che registra la AnimalCell classe :

static NSString animalCellId = new NSString ("AnimalCell");
CollectionView.RegisterClassForCell (typeof(AnimalCell), animalCellId);

Quando è UICollectionView necessaria una cella perché l'elemento è sullo schermo, chiama il UICollectionView metodo dell'origine GetCell dati. Analogamente a come funziona con UITableView, questo metodo è responsabile della configurazione di una cella dai dati di supporto, che in questo caso sarebbe una AnimalCell classe.

Il codice seguente illustra un'implementazione di GetCell che restituisce un'istanza AnimalCell di :

public override UICollectionViewCell GetCell (UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
        var animalCell = (AnimalCell)collectionView.DequeueReusableCell (animalCellId, indexPath);

        var animal = animals [indexPath.Row];

        animalCell.Image = animal.Image;

        return animalCell;
}

La chiamata a DequeReusableCell è la posizione in cui la cella verrà de codata dalla coda di riutilizzo o, se una cella non è disponibile nella coda, creata in base al tipo registrato nella chiamata a CollectionView.RegisterClassForCell.

In questo caso, registrando la AnimalCell classe , iOS creerà un nuovo AnimalCell elemento internamente e lo restituirà quando viene eseguita una chiamata alla de-coda di una cella, dopo la quale è configurato con l'immagine contenuta nella classe animale e restituita per la visualizzazione all'oggetto UICollectionView.

Delega

La UICollectionView classe usa un delegato di tipo UICollectionViewDelegate per supportare l'interazione con il contenuto in UICollectionView. Ciò consente il controllo di:

  • Selezione cella: determina se è selezionata una cella.
  • Evidenziazione cella: determina se una cella è attualmente toccato.
  • Menu cella: menu visualizzato per una cella in risposta a un movimento di pressione prolungata.

Come per l'origine dati, per UICollectionViewController impostazione predefinita è configurato come delegato per .UICollectionView

HighLighting delle celle

Quando si preme una cella, la cella passa a uno stato evidenziato e non viene selezionata finché l'utente non solleva il dito dalla cella. Ciò consente una modifica temporanea nell'aspetto della cella prima che venga effettivamente selezionata. Dopo la selezione, viene visualizzata la cella SelectedBackgroundView . La figura seguente mostra lo stato evidenziato poco prima che si verifichi la selezione:

Questa figura mostra lo stato evidenziato poco prima che si verifichi la selezione

Per implementare l'evidenziazione, è possibile usare i ItemHighlighted metodi e ItemUnhighlighted di UICollectionViewDelegate . Ad esempio, il codice seguente applicherà uno sfondo giallo di ContentView quando la cella è evidenziata e uno sfondo bianco quando non evidenziato, come illustrato nell'immagine precedente:

public override void ItemHighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
        var cell = collectionView.CellForItem(indexPath);
        cell.ContentView.BackgroundColor = UIColor.Yellow;
}

public override void ItemUnhighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
        var cell = collectionView.CellForItem(indexPath);
        cell.ContentView.BackgroundColor = UIColor.White;
}

Disabilitazione della selezione

La selezione è abilitata per impostazione predefinita in UICollectionView. Per disabilitare la selezione, eseguire l'override ShouldHighlightItem e restituire false, come illustrato di seguito:

public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath)
{
        return false;
}

Quando l'evidenziazione è disabilitata, anche il processo di selezione di una cella è disabilitato. Inoltre, esiste anche un ShouldSelectItem metodo che controlla direttamente la selezione, anche se ShouldHighlightItem viene implementato e restituisce false, ShouldSelectItem non viene chiamato.

ShouldSelectItem consente di attivare o disattivare la selezione in base all'elemento, quando ShouldHighlightItem non viene implementata. Consente inoltre l'evidenziazione senza selezione, se ShouldHighlightItem viene implementata e restituisce true, mentre ShouldSelectItem restituisce false.

Menu cella

Ogni cella di un oggetto UICollectionView è in grado di visualizzare un menu che consente di tagliare, copiare e incollare in modo facoltativo. Per creare un menu di modifica in una cella:

  1. Eseguire l'override e restituire true se l'elemento deve visualizzare ShouldShowMenu un menu.
  2. Eseguire l'override CanPerformAction e restituire true per ogni azione che l'elemento può eseguire, che sarà una delle operazioni taglia, copia o incolla.
  3. Eseguire l'override PerformAction per eseguire l'operazione di modifica, copia dell'operazione incolla.

Lo screenshot seguente mostra il menu quando viene premuta una cella:

Questo screenshot mostra il menu quando viene premuta una cella

Layout

UICollectionView supporta un sistema di layout che consente di posizionare tutti i relativi elementi, Celle, Visualizzazioni supplementari e Viste di decorazione, di essere gestiti indipendentemente da UICollectionView se stesso. Usando il sistema di layout, un'applicazione può supportare layout come la griglia illustrata in questo articolo, oltre a fornire layout personalizzati.

Nozioni di base sul layout

I layout in un UICollectionView oggetto sono definiti in una classe che eredita da UICollectionViewLayout. L'implementazione del layout è responsabile della creazione degli attributi di layout per ogni elemento in UICollectionView. Esistono due modi per creare un layout:

  • Usare l'oggetto UICollectionViewFlowLayout predefinito .
  • Fornire un layout personalizzato ereditando da UICollectionViewLayout .

Layout di flusso

La UICollectionViewFlowLayout classe fornisce un layout basato su linea adatto per disporre il contenuto in una griglia di celle come si è visto.

Per usare un layout di flusso:

  • Creare un'istanza di UICollectionViewFlowLayout :
var layout = new UICollectionViewFlowLayout ();
  • Passare l'istanza al costruttore di UICollectionView :
simpleCollectionViewController = new SimpleCollectionViewController (layout);

Questo è tutto ciò che è necessario per il layout del contenuto in una griglia. Inoltre, quando l'orientamento cambia, gestisce la UICollectionViewFlowLayout riorganizzazione del contenuto in modo appropriato, come illustrato di seguito:

Esempio di modifiche dell'orientamento

Set di sezioni

Per fornire spazio intorno a , i UIContentViewlayout hanno una SectionInset proprietà di tipo UIEdgeInsets. Ad esempio, il codice seguente fornisce un buffer di 50 pixel intorno a ogni sezione di UIContentView quando disposto da un oggetto UICollectionViewFlowLayout:

var layout = new UICollectionViewFlowLayout ();
layout.SectionInset = new UIEdgeInsets (50,50,50,50);

Ciò comporta la spaziatura intorno alla sezione, come illustrato di seguito:

Spaziatura intorno alla sezione come illustrato di seguito

Creazione di sottoclassi UICollectionViewFlowLayout

Nell'edizione da usare UICollectionViewFlowLayout direttamente, può anche essere sottoclassata per personalizzare ulteriormente il layout del contenuto lungo una linea. Ad esempio, questo può essere usato per creare un layout che non esegue il wrapping delle celle in una griglia, ma crea invece una singola riga con un effetto di scorrimento orizzontale, come illustrato di seguito:

Una singola riga con un effetto di scorrimento orizzontale

Per implementare questa funzionalità tramite la sottoclasse UICollectionViewFlowLayout , è necessario:

  • Inizializzazione di tutte le proprietà di layout applicabili al layout stesso o a tutti gli elementi del layout nel costruttore.
  • Override di , restituendo ShouldInvalidateLayoutForBoundsChange true in modo che, quando i limiti delle UICollectionView modifiche, il layout delle celle verrà ricalcolato. Questa operazione viene usata in questo caso per assicurarsi che il codice per la trasformazione applicato alla cella al centro venga applicato durante lo scorrimento.
  • Override di TargetContentOffset per fare in modo che lo snap della cella al centro dell'oggetto venga interrotto durante lo UICollectionView scorrimento.
  • Override di LayoutAttributesForElementsInRect per restituire una matrice di UICollectionViewLayoutAttributes . Ognuno UICollectionViewLayoutAttribute contiene informazioni su come creare il layout dell'elemento specifico, incluse le proprietà, ad Center esempio , ZIndexSize e Transform3D .

Il codice seguente illustra un'implementazione di questo tipo:

using System;
using CoreGraphics;
using Foundation;
using UIKit;
using CoreGraphics;
using CoreAnimation;

namespace SimpleCollectionView
{
  public class LineLayout : UICollectionViewFlowLayout
  {
    public const float ITEM_SIZE = 200.0f;
    public const int ACTIVE_DISTANCE = 200;
    public const float ZOOM_FACTOR = 0.3f;

    public LineLayout ()
    {
      ItemSize = new CGSize (ITEM_SIZE, ITEM_SIZE);
      ScrollDirection = UICollectionViewScrollDirection.Horizontal;
            SectionInset = new UIEdgeInsets (400,0,400,0);
      MinimumLineSpacing = 50.0f;
    }

    public override bool ShouldInvalidateLayoutForBoundsChange (CGRect newBounds)
    {
      return true;
    }

    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (CGRect rect)
    {
      var array = base.LayoutAttributesForElementsInRect (rect);
            var visibleRect = new CGRect (CollectionView.ContentOffset, CollectionView.Bounds.Size);

      foreach (var attributes in array) {
        if (attributes.Frame.IntersectsWith (rect)) {
          float distance = (float)(visibleRect.GetMidX () - attributes.Center.X);
          float normalizedDistance = distance / ACTIVE_DISTANCE;
          if (Math.Abs (distance) < ACTIVE_DISTANCE) {
            float zoom = 1 + ZOOM_FACTOR * (1 - Math.Abs (normalizedDistance));
            attributes.Transform3D = CATransform3D.MakeScale (zoom, zoom, 1.0f);
            attributes.ZIndex = 1;
          }
        }
      }
      return array;
    }

    public override CGPoint TargetContentOffset (CGPoint proposedContentOffset, CGPoint scrollingVelocity)
    {
      float offSetAdjustment = float.MaxValue;
      float horizontalCenter = (float)(proposedContentOffset.X + (this.CollectionView.Bounds.Size.Width / 2.0));
      CGRect targetRect = new CGRect (proposedContentOffset.X, 0.0f, this.CollectionView.Bounds.Size.Width, this.CollectionView.Bounds.Size.Height);
      var array = base.LayoutAttributesForElementsInRect (targetRect);
      foreach (var layoutAttributes in array) {
        float itemHorizontalCenter = (float)layoutAttributes.Center.X;
        if (Math.Abs (itemHorizontalCenter - horizontalCenter) < Math.Abs (offSetAdjustment)) {
          offSetAdjustment = itemHorizontalCenter - horizontalCenter;
        }
      }
            return new CGPoint (proposedContentOffset.X + offSetAdjustment, proposedContentOffset.Y);
    }

  }
}

Layout personalizzato

Oltre a usare UICollectionViewFlowLayout, i layout possono anche essere completamente personalizzati ereditando direttamente da UICollectionViewLayout.

I metodi chiave di cui eseguire l'override sono:

  • PrepareLayout – Usato per eseguire calcoli geometrici iniziali che verranno usati durante il processo di layout.
  • CollectionViewContentSize : restituisce le dimensioni dell'area utilizzata per visualizzare il contenuto.
  • LayoutAttributesForElementsInRect : come nell'esempio UICollectionViewFlowLayout illustrato in precedenza, questo metodo viene usato per fornire informazioni su come eseguire il UICollectionView layout di ogni elemento. Tuttavia, a differenza di UICollectionViewFlowLayout , quando si crea un layout personalizzato, è possibile posizionare gli elementi, ma si sceglie.

Ad esempio, lo stesso contenuto può essere presentato in un layout circolare, come illustrato di seguito:

Layout personalizzato circolare, come illustrato di seguito

La cosa importante dei layout è che per passare dal layout simile alla griglia, a un layout di scorrimento orizzontale e successivamente a questo layout circolare richiede solo la classe di layout fornita per essere UICollectionView modificata. Nulla in UICollectionView, il relativo delegato o il codice sorgente dati cambia affatto.

Modifiche in iOS 9

In iOS 9, la visualizzazione raccolta (UICollectionView) supporta ora il riordino di trascinamento degli elementi predefiniti aggiungendo un nuovo riconoscimento movimento predefinito e diversi nuovi metodi di supporto.

Usando questi nuovi metodi, è possibile implementare facilmente il trascinamento per riordinare nella visualizzazione raccolta e avere la possibilità di personalizzare l'aspetto degli elementi durante qualsiasi fase del processo di riordinamento.

Esempio del processo di riordinamento

In questo articolo si esaminerà l'implementazione dell'implementazione del trascinamento del riordinamento in un'applicazione Xamarin.iOS e di alcune delle altre modifiche apportate da iOS 9 al controllo visualizzazione raccolta:

Riordinamento degli elementi

Come indicato in precedenza, una delle modifiche più significative alla visualizzazione raccolta in iOS 9 è stata l'aggiunta di una semplice funzionalità di ordinamento da trascinare a riordinare.

In iOS 9, il modo più rapido per aggiungere il riordinamento a una visualizzazione raccolta consiste nell'usare un oggetto UICollectionViewController. Il controller di visualizzazione raccolta dispone ora di una InstallsStandardGestureForInteractiveMovement proprietà che aggiunge un riconoscimento movimento standard che supporta il trascinamento per riordinare gli elementi nella raccolta. Poiché il valore predefinito è true, è necessario implementare solo il MoveItem metodo della UICollectionViewDataSource classe per supportare il trascinamento verso il riordinamento. Ad esempio:

public override void MoveItem (UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
  // Reorder our list of items
  ...
}

Esempio di riordinamento semplice

Come esempio rapido, avviare un nuovo progetto Xamarin.iOS e modificare il file Main.storyboard . Trascinare un oggetto UICollectionViewController nell'area di progettazione:

Aggiunta di un uiCollectionViewController

Selezionare la visualizzazione Raccolta (potrebbe essere più semplice eseguire questa operazione dalla struttura del documento). Nella scheda layout del riquadro proprietà impostare le dimensioni seguenti, come illustrato nello screenshot seguente:

  • Dimensione cella: larghezza – 60 | Altezza - 60
  • Dimensioni intestazione: larghezza - 0 | Altezza - 0
  • Dimensioni piè di pagina: larghezza - 0 | Altezza - 0
  • Spaziatura minima: per le celle - 8 | Per linee - 8
  • Set di sezioni: Top – 16 | Bottom – 16 | Left – 16 | Destra - 16

Impostare le dimensioni della visualizzazione raccolta

Modificare quindi la cella predefinita:

  • Modificare il colore di sfondo in blu
  • Aggiungere un'etichetta per fungere da titolo per la cella
  • Impostare l'identificatore di riutilizzo sulla cella

Modificare la cella predefinita

Aggiungere vincoli per mantenere l'etichetta centrata all'interno della cella man mano che cambia le dimensioni:

Nel riquadro delle proprietà per CollectionViewCell e impostare La classe su TextCollectionViewCell:

Impostare La classe su TextCollectionViewCell

Impostare la visualizzazione riutilizzabile raccolta su Cell:

Impostare la visualizzazione riutilizzabile raccolta su cella

Infine, selezionare l'etichetta e denominarla TextLabel:

etichetta nome TextLabel

Modificare la TextCollectionViewCell classe e aggiungere le proprietà seguenti:

using System;
using Foundation;
using UIKit;

namespace CollectionView
{
  public partial class TextCollectionViewCell : UICollectionViewCell
  {
    #region Computed Properties
    public string Title {
      get { return TextLabel.Text; }
      set { TextLabel.Text = value; }
    }
    #endregion

    #region Constructors
    public TextCollectionViewCell (IntPtr handle) : base (handle)
    {
    }
    #endregion
  }
}

In questo caso la Text proprietà dell'etichetta viene esposta come titolo della cella, in modo che possa essere impostata dal codice.

Aggiungere una nuova classe C# al progetto e chiamarla WaterfallCollectionSource. Modificare il file e renderlo simile al seguente:

using System;
using Foundation;
using UIKit;
using System.Collections.Generic;

namespace CollectionView
{
  public class WaterfallCollectionSource : UICollectionViewDataSource
  {
    #region Computed Properties
    public WaterfallCollectionView CollectionView { get; set;}
    public List<int> Numbers { get; set; } = new List<int> ();
    #endregion

    #region Constructors
    public WaterfallCollectionSource (WaterfallCollectionView collectionView)
    {
      // Initialize
      CollectionView = collectionView;

      // Init numbers collection
      for (int n = 0; n < 100; ++n) {
        Numbers.Add (n);
      }
    }
    #endregion

    #region Override Methods
    public override nint NumberOfSections (UICollectionView collectionView) {
      // We only have one section
      return 1;
    }

    public override nint GetItemsCount (UICollectionView collectionView, nint section) {
      // Return the number of items
      return Numbers.Count;
    }

    public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
    {
      // Get a reusable cell and set {~~it's~>its~~} title from the item
      var cell = collectionView.DequeueReusableCell ("Cell", indexPath) as TextCollectionViewCell;
      cell.Title = Numbers [(int)indexPath.Item].ToString();

      return cell;
    }

    public override bool CanMoveItem (UICollectionView collectionView, NSIndexPath indexPath) {
      // We can always move items
      return true;
    }

    public override void MoveItem (UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
    {
      // Reorder our list of items
      var item = Numbers [(int)sourceIndexPath.Item];
      Numbers.RemoveAt ((int)sourceIndexPath.Item);
      Numbers.Insert ((int)destinationIndexPath.Item, item);
    }
    #endregion
  }
}

Questa classe sarà l'origine dati per la visualizzazione raccolta e fornirà le informazioni per ogni cella della raccolta. Si noti che il MoveItem metodo viene implementato per consentire il riordinamento degli elementi nella raccolta.

Aggiungere un'altra nuova classe C# al progetto e chiamarla WaterfallCollectionDelegate. Modificare questo file e renderlo simile al seguente:

using System;
using Foundation;
using UIKit;
using System.Collections.Generic;

namespace CollectionView
{
  public class WaterfallCollectionDelegate : UICollectionViewDelegate
  {
    #region Computed Properties
    public WaterfallCollectionView CollectionView { get; set;}
    #endregion

    #region Constructors
    public WaterfallCollectionDelegate (WaterfallCollectionView collectionView)
    {

      // Initialize
      CollectionView = collectionView;

    }
    #endregion

    #region Overrides Methods
    public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath) {
      // Always allow for highlighting
      return true;
    }

    public override void ItemHighlighted (UICollectionView collectionView, NSIndexPath indexPath)
    {
      // Get cell and change to green background
      var cell = collectionView.CellForItem(indexPath);
      cell.ContentView.BackgroundColor = UIColor.FromRGB(183,208,57);
    }

    public override void ItemUnhighlighted (UICollectionView collectionView, NSIndexPath indexPath)
    {
      // Get cell and return to blue background
      var cell = collectionView.CellForItem(indexPath);
      cell.ContentView.BackgroundColor = UIColor.FromRGB(164,205,255);
    }
    #endregion
  }
}

Questa operazione fungerà da delegato per la visualizzazione raccolta. I metodi sono stati sottoposti a override per evidenziare una cella mentre l'utente interagisce con esso nella visualizzazione raccolta.

Aggiungere un'ultima classe C# al progetto e chiamarla WaterfallCollectionView. Modificare questo file e renderlo simile al seguente:

using System;
using UIKit;
using System.Collections.Generic;
using Foundation;

namespace CollectionView
{
  [Register("WaterfallCollectionView")]
  public class WaterfallCollectionView : UICollectionView
  {

    #region Constructors
    public WaterfallCollectionView (IntPtr handle) : base (handle)
    {
    }
    #endregion

    #region Override Methods
    public override void AwakeFromNib ()
    {
      base.AwakeFromNib ();

      // Initialize
      DataSource = new WaterfallCollectionSource(this);
      Delegate = new WaterfallCollectionDelegate(this);

    }
    #endregion
  }
}

Si noti che DataSource e Delegate che sono stati creati in precedenza vengono impostati quando la visualizzazione raccolta viene costruita dal relativo storyboard (o file xib ).

Modificare di nuovo il file Main.storyboard e selezionare la visualizzazione raccolta e passare a Proprietà. Impostare Classe sulla classe personalizzata definita in precedenzaWaterfallCollectionView:

Salvare le modifiche apportate all'interfaccia utente ed eseguire l'app. Se l'utente seleziona un elemento dall'elenco e lo trascina in una nuova posizione, gli altri elementi verranno animati automaticamente man mano che si spostano all'esterno dell'elemento. Quando l'utente elimina l'elemento in una nuova posizione, si sporge a tale posizione. Ad esempio:

Esempio di trascinamento di un elemento in una nuova posizione

Uso di un riconoscimento movimento personalizzato

Nei casi in cui non è possibile usare un UICollectionViewController oggetto e deve usare un normale UIViewControllero se si vuole assumere un maggiore controllo sul movimento di trascinamento della selezione, è possibile creare un riconoscimento movimento personalizzato e aggiungerlo alla visualizzazione raccolta quando viene caricata la visualizzazione. Ad esempio:

public override void ViewDidLoad ()
{
  base.ViewDidLoad ();

  // Create a custom gesture recognizer
  var longPressGesture = new UILongPressGestureRecognizer ((gesture) => {

    // Take action based on state
    switch(gesture.State) {
    case UIGestureRecognizerState.Began:
      var selectedIndexPath = CollectionView.IndexPathForItemAtPoint(gesture.LocationInView(View));
      if (selectedIndexPath !=null) {
        CollectionView.BeginInteractiveMovementForItem(selectedIndexPath);
      }
      break;
    case UIGestureRecognizerState.Changed:
      CollectionView.UpdateInteractiveMovementTargetPosition(gesture.LocationInView(View));
      break;
    case UIGestureRecognizerState.Ended:
      CollectionView.EndInteractiveMovement();
      break;
    default:
      CollectionView.CancelInteractiveMovement();
      break;
    }

  });

  // Add the custom recognizer to the collection view
  CollectionView.AddGestureRecognizer(longPressGesture);
}

Di seguito vengono usati diversi nuovi metodi aggiunti alla visualizzazione raccolta per implementare e controllare l'operazione di trascinamento:

  • BeginInteractiveMovementForItem - Contrassegna l'inizio di un'operazione di spostamento.
  • UpdateInteractiveMovementTargetPosition - Viene inviato quando viene aggiornata la posizione dell'elemento.
  • EndInteractiveMovement - Contrassegna la fine di uno spostamento di un elemento.
  • CancelInteractiveMovement - Contrassegna l'utente che annulla l'operazione di spostamento.

Quando l'applicazione viene eseguita, l'operazione di trascinamento funzionerà esattamente come lo strumento di riconoscimento movimento di trascinamento predefinito fornito con la visualizzazione raccolta.

Layout personalizzati e riordinamento

In iOS 9 sono stati aggiunti diversi nuovi metodi per lavorare con il trascinamento del riordinamento e i layout personalizzati in una visualizzazione raccolta. Per esplorare questa funzionalità, aggiungere un layout personalizzato alla raccolta.

Aggiungere prima di tutto una nuova classe C# chiamata WaterfallCollectionLayout al progetto. Modificarlo e renderlo simile al seguente:

using System;
using Foundation;
using UIKit;
using System.Collections.Generic;
using CoreGraphics;

namespace CollectionView
{
  [Register("WaterfallCollectionLayout")]
  public class WaterfallCollectionLayout : UICollectionViewLayout
  {
    #region Private Variables
    private int columnCount = 2;
    private nfloat minimumColumnSpacing = 10;
    private nfloat minimumInterItemSpacing = 10;
    private nfloat headerHeight = 0.0f;
    private nfloat footerHeight = 0.0f;
    private UIEdgeInsets sectionInset = new UIEdgeInsets(0, 0, 0, 0);
    private WaterfallCollectionRenderDirection itemRenderDirection = WaterfallCollectionRenderDirection.ShortestFirst;
    private Dictionary<nint,UICollectionViewLayoutAttributes> headersAttributes = new Dictionary<nint, UICollectionViewLayoutAttributes>();
    private Dictionary<nint,UICollectionViewLayoutAttributes> footersAttributes = new Dictionary<nint, UICollectionViewLayoutAttributes>();
    private List<CGRect> unionRects = new List<CGRect>();
    private List<nfloat> columnHeights = new List<nfloat>();
    private List<UICollectionViewLayoutAttributes> allItemAttributes = new List<UICollectionViewLayoutAttributes>();
    private List<List<UICollectionViewLayoutAttributes>> sectionItemAttributes = new List<List<UICollectionViewLayoutAttributes>>();
    private nfloat unionSize = 20;
    #endregion

    #region Computed Properties
    [Export("ColumnCount")]
    public int ColumnCount {
      get { return columnCount; }
      set {
        WillChangeValue ("ColumnCount");
        columnCount = value;
        DidChangeValue ("ColumnCount");

        InvalidateLayout ();
      }
    }

    [Export("MinimumColumnSpacing")]
    public nfloat MinimumColumnSpacing {
      get { return minimumColumnSpacing; }
      set {
        WillChangeValue ("MinimumColumnSpacing");
        minimumColumnSpacing = value;
        DidChangeValue ("MinimumColumnSpacing");

        InvalidateLayout ();
      }
    }

    [Export("MinimumInterItemSpacing")]
    public nfloat MinimumInterItemSpacing {
      get { return minimumInterItemSpacing; }
      set {
        WillChangeValue ("MinimumInterItemSpacing");
        minimumInterItemSpacing = value;
        DidChangeValue ("MinimumInterItemSpacing");

        InvalidateLayout ();
      }
    }

    [Export("HeaderHeight")]
    public nfloat HeaderHeight {
      get { return headerHeight; }
      set {
        WillChangeValue ("HeaderHeight");
        headerHeight = value;
        DidChangeValue ("HeaderHeight");

        InvalidateLayout ();
      }
    }

    [Export("FooterHeight")]
    public nfloat FooterHeight {
      get { return footerHeight; }
      set {
        WillChangeValue ("FooterHeight");
        footerHeight = value;
        DidChangeValue ("FooterHeight");

        InvalidateLayout ();
      }
    }

    [Export("SectionInset")]
    public UIEdgeInsets SectionInset {
      get { return sectionInset; }
      set {
        WillChangeValue ("SectionInset");
        sectionInset = value;
        DidChangeValue ("SectionInset");

        InvalidateLayout ();
      }
    }

    [Export("ItemRenderDirection")]
    public WaterfallCollectionRenderDirection ItemRenderDirection {
      get { return itemRenderDirection; }
      set {
        WillChangeValue ("ItemRenderDirection");
        itemRenderDirection = value;
        DidChangeValue ("ItemRenderDirection");

        InvalidateLayout ();
      }
    }
    #endregion

    #region Constructors
    public WaterfallCollectionLayout ()
    {
    }

    public WaterfallCollectionLayout(NSCoder coder) : base(coder) {

    }
    #endregion

    #region Public Methods
    public nfloat ItemWidthInSectionAtIndex(int section) {

      var width = CollectionView.Bounds.Width - SectionInset.Left - SectionInset.Right;
      return (nfloat)Math.Floor ((width - ((ColumnCount - 1) * MinimumColumnSpacing)) / ColumnCount);
    }
    #endregion

    #region Override Methods
    public override void PrepareLayout ()
    {
      base.PrepareLayout ();

      // Get the number of sections
      var numberofSections = CollectionView.NumberOfSections();
      if (numberofSections == 0)
        return;

      // Reset collections
      headersAttributes.Clear ();
      footersAttributes.Clear ();
      unionRects.Clear ();
      columnHeights.Clear ();
      allItemAttributes.Clear ();
      sectionItemAttributes.Clear ();

      // Initialize column heights
      for (int n = 0; n < ColumnCount; n++) {
        columnHeights.Add ((nfloat)0);
      }

      // Process all sections
      nfloat top = 0.0f;
      var attributes = new UICollectionViewLayoutAttributes ();
      var columnIndex = 0;
      for (nint section = 0; section < numberofSections; ++section) {
        // Calculate section specific metrics
        var minimumInterItemSpacing = (MinimumInterItemSpacingForSection == null) ? MinimumColumnSpacing :
          MinimumInterItemSpacingForSection (CollectionView, this, section);

        // Calculate widths
        var width = CollectionView.Bounds.Width - SectionInset.Left - SectionInset.Right;
        var itemWidth = (nfloat)Math.Floor ((width - ((ColumnCount - 1) * MinimumColumnSpacing)) / ColumnCount);

        // Calculate section header
        var heightHeader = (HeightForHeader == null) ? HeaderHeight :
          HeightForHeader (CollectionView, this, section);

        if (heightHeader > 0) {
          attributes = UICollectionViewLayoutAttributes.CreateForSupplementaryView (UICollectionElementKindSection.Header, NSIndexPath.FromRowSection (0, section));
          attributes.Frame = new CGRect (0, top, CollectionView.Bounds.Width, heightHeader);
          headersAttributes.Add (section, attributes);
          allItemAttributes.Add (attributes);

          top = attributes.Frame.GetMaxY ();
        }

        top += SectionInset.Top;
        for (int n = 0; n < ColumnCount; n++) {
          columnHeights [n] = top;
        }

        // Calculate Section Items
        var itemCount = CollectionView.NumberOfItemsInSection(section);
        List<UICollectionViewLayoutAttributes> itemAttributes = new List<UICollectionViewLayoutAttributes> ();

        for (nint n = 0; n < itemCount; n++) {
          var indexPath = NSIndexPath.FromRowSection (n, section);
          columnIndex = NextColumnIndexForItem (n);
          var xOffset = SectionInset.Left + (itemWidth + MinimumColumnSpacing) * (nfloat)columnIndex;
          var yOffset = columnHeights [columnIndex];
          var itemSize = (SizeForItem == null) ? new CGSize (0, 0) : SizeForItem (CollectionView, this, indexPath);
          nfloat itemHeight = 0.0f;

          if (itemSize.Height > 0.0f && itemSize.Width > 0.0f) {
            itemHeight = (nfloat)Math.Floor (itemSize.Height * itemWidth / itemSize.Width);
          }

          attributes = UICollectionViewLayoutAttributes.CreateForCell (indexPath);
          attributes.Frame = new CGRect (xOffset, yOffset, itemWidth, itemHeight);
          itemAttributes.Add (attributes);
          allItemAttributes.Add (attributes);
          columnHeights [columnIndex] = attributes.Frame.GetMaxY () + MinimumInterItemSpacing;
        }
        sectionItemAttributes.Add (itemAttributes);

        // Calculate Section Footer
        nfloat footerHeight = 0.0f;
        columnIndex = LongestColumnIndex();
        top = columnHeights [columnIndex] - MinimumInterItemSpacing + SectionInset.Bottom;
        footerHeight = (HeightForFooter == null) ? FooterHeight : HeightForFooter(CollectionView, this, section);

        if (footerHeight > 0) {
          attributes = UICollectionViewLayoutAttributes.CreateForSupplementaryView (UICollectionElementKindSection.Footer, NSIndexPath.FromRowSection (0, section));
          attributes.Frame = new CGRect (0, top, CollectionView.Bounds.Width, footerHeight);
          footersAttributes.Add (section, attributes);
          allItemAttributes.Add (attributes);
          top = attributes.Frame.GetMaxY ();
        }

        for (int n = 0; n < ColumnCount; n++) {
          columnHeights [n] = top;
        }
      }

      var i =0;
      var attrs = allItemAttributes.Count;
      while(i < attrs) {
        var rect1 = allItemAttributes [i].Frame;
        i = (int)Math.Min (i + unionSize, attrs) - 1;
        var rect2 = allItemAttributes [i].Frame;
        unionRects.Add (CGRect.Union (rect1, rect2));
        i++;
      }

    }

    public override CGSize CollectionViewContentSize {
      get {
        if (CollectionView.NumberOfSections () == 0) {
          return new CGSize (0, 0);
        }

        var contentSize = CollectionView.Bounds.Size;
        contentSize.Height = columnHeights [0];
        return contentSize;
      }
    }

    public override UICollectionViewLayoutAttributes LayoutAttributesForItem (NSIndexPath indexPath)
    {
      if (indexPath.Section >= sectionItemAttributes.Count) {
        return null;
      }

      if (indexPath.Item >= sectionItemAttributes [indexPath.Section].Count) {
        return null;
      }

      var list = sectionItemAttributes [indexPath.Section];
      return list [(int)indexPath.Item];
    }

    public override UICollectionViewLayoutAttributes LayoutAttributesForSupplementaryView (NSString kind, NSIndexPath indexPath)
    {
      var attributes = new UICollectionViewLayoutAttributes ();

      switch (kind) {
      case "header":
        attributes = headersAttributes [indexPath.Section];
        break;
      case "footer":
        attributes = footersAttributes [indexPath.Section];
        break;
      }

      return attributes;
    }

    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (CGRect rect)
    {
      var begin = 0;
      var end = unionRects.Count;
      List<UICollectionViewLayoutAttributes> attrs = new List<UICollectionViewLayoutAttributes> ();

      for (int i = 0; i < end; i++) {
        if (rect.IntersectsWith(unionRects[i])) {
          begin = i * (int)unionSize;
        }
      }

      for (int i = end - 1; i >= 0; i--) {
        if (rect.IntersectsWith (unionRects [i])) {
          end = (int)Math.Min ((i + 1) * (int)unionSize, allItemAttributes.Count);
          break;
        }
      }

      for (int i = begin; i < end; i++) {
        var attr = allItemAttributes [i];
        if (rect.IntersectsWith (attr.Frame)) {
          attrs.Add (attr);
        }
      }

      return attrs.ToArray();
    }

    public override bool ShouldInvalidateLayoutForBoundsChange (CGRect newBounds)
    {
      var oldBounds = CollectionView.Bounds;
      return (newBounds.Width != oldBounds.Width);
    }
    #endregion

    #region Private Methods
    private int ShortestColumnIndex() {
      var index = 0;
      var shortestHeight = nfloat.MaxValue;
      var n = 0;

      // Scan each column for the shortest height
      foreach (nfloat height in columnHeights) {
        if (height < shortestHeight) {
          shortestHeight = height;
          index = n;
        }
        ++n;
      }

      return index;
    }

    private int LongestColumnIndex() {
      var index = 0;
      var longestHeight = nfloat.MinValue;
      var n = 0;

      // Scan each column for the shortest height
      foreach (nfloat height in columnHeights) {
        if (height > longestHeight) {
          longestHeight = height;
          index = n;
        }
        ++n;
      }

      return index;
    }

    private int NextColumnIndexForItem(nint item) {
      var index = 0;

      switch (ItemRenderDirection) {
      case WaterfallCollectionRenderDirection.ShortestFirst:
        index = ShortestColumnIndex ();
        break;
      case WaterfallCollectionRenderDirection.LeftToRight:
        index = ColumnCount;
        break;
      case WaterfallCollectionRenderDirection.RightToLeft:
        index = (ColumnCount - 1) - ((int)item / ColumnCount);
        break;
      }

      return index;
    }
    #endregion

    #region Events
    public delegate CGSize WaterfallCollectionSizeDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, NSIndexPath indexPath);
    public delegate nfloat WaterfallCollectionFloatDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, nint section);
    public delegate UIEdgeInsets WaterfallCollectionEdgeInsetsDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, nint section);

    public event WaterfallCollectionSizeDelegate SizeForItem;
    public event WaterfallCollectionFloatDelegate HeightForHeader;
    public event WaterfallCollectionFloatDelegate HeightForFooter;
    public event WaterfallCollectionEdgeInsetsDelegate InsetForSection;
    public event WaterfallCollectionFloatDelegate MinimumInterItemSpacingForSection;
    #endregion
  }
}

Questa classe può essere usata per fornire una colonna personalizzata a due colonne, il layout del tipo a cascata alla visualizzazione raccolta. Il codice usa la codifica key-value (tramite i WillChangeValue metodi e DidChangeValue ) per fornire il data binding per le proprietà calcolate in questa classe.

Modificare quindi e WaterfallCollectionSource apportare le modifiche e le aggiunte seguenti:

private Random rnd = new Random();
...

public List<nfloat> Heights { get; set; } = new List<nfloat> ();
...

public WaterfallCollectionSource (WaterfallCollectionView collectionView)
{
  // Initialize
  CollectionView = collectionView;

  // Init numbers collection
  for (int n = 0; n < 100; ++n) {
    Numbers.Add (n);
    Heights.Add (rnd.Next (0, 100) + 40.0f);
  }
}

Verrà creata un'altezza casuale per ognuno degli elementi che verranno visualizzati nell'elenco.

Modificare quindi la WaterfallCollectionView classe e aggiungere la proprietà helper seguente:

public WaterfallCollectionSource Source {
  get { return (WaterfallCollectionSource)DataSource; }
}

In questo modo sarà più semplice ottenere l'origine dati (e le altezze degli elementi) dal layout personalizzato.

Modificare infine il controller di visualizzazione e aggiungere il codice seguente:

public override void AwakeFromNib ()
{
  base.AwakeFromNib ();

  var waterfallLayout = new WaterfallCollectionLayout ();

  // Wireup events
  waterfallLayout.SizeForItem += (collectionView, layout, indexPath) => {
    var collection = collectionView as WaterfallCollectionView;
    return new CGSize((View.Bounds.Width-40)/3,collection.Source.Heights[(int)indexPath.Item]);
  };

  // Attach the custom layout to the collection
  CollectionView.SetCollectionViewLayout(waterfallLayout, false);
}

In questo modo viene creata un'istanza del layout personalizzato, viene impostato l'evento per fornire le dimensioni di ogni elemento e viene associato il nuovo layout alla visualizzazione raccolta.

Se si esegue di nuovo l'app Xamarin.iOS, la visualizzazione raccolta sarà simile alla seguente:

La visualizzazione raccolta avrà ora un aspetto simile al seguente

È comunque possibile trascinare per riordinare gli elementi come in precedenza, ma gli elementi cambieranno ora le dimensioni per adattarne la nuova posizione quando vengono rilasciati.

Modifiche alla visualizzazione raccolta

Nelle sezioni seguenti verranno esaminate in dettaglio le modifiche apportate a ogni classe nella visualizzazione raccolta da iOS 9.

UICollectionView

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionView classe per iOS 9:

  • BeginInteractiveMovementForItem : contrassegna l'inizio di un'operazione di trascinamento.
  • CancelInteractiveMovement : informa la visualizzazione raccolta che l'utente ha annullato un'operazione di trascinamento.
  • EndInteractiveMovement : informa la visualizzazione raccolta che l'utente ha completato un'operazione di trascinamento.
  • GetIndexPathsForVisibleSupplementaryElements : restituisce l'oggetto di un'intestazione indexPath o di un piè di pagina in una sezione della visualizzazione raccolta.
  • GetSupplementaryView : restituisce l'intestazione o il piè di pagina specificati.
  • GetVisibleSupplementaryViews : restituisce un elenco di tutte le intestazioni e i piè di pagina visibili.
  • UpdateInteractiveMovementTargetPosition : informa la visualizzazione raccolta che l'utente ha spostato o sta spostando un elemento durante un'operazione di trascinamento.

UICollectionViewController

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewController classe in iOS 9:

  • InstallsStandardGestureForInteractiveMovement : se true verrà usato il nuovo Riconoscimento movimenti che supporta automaticamente il trascinamento verso il riordinamento.
  • CanMoveItem : informa la visualizzazione raccolta se un determinato elemento può essere riordinato.
  • GetTargetContentOffset : consente di ottenere l'offset di un determinato elemento della visualizzazione raccolta.
  • GetTargetIndexPathForMove : ottiene l'oggetto indexPath di un determinato elemento per un'operazione di trascinamento.
  • MoveItem : sposta l'ordine di un determinato elemento nell'elenco.

UICollectionViewDataSource

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewDataSource classe in iOS 9:

  • CanMoveItem : informa la visualizzazione raccolta se un determinato elemento può essere riordinato.
  • MoveItem : sposta l'ordine di un determinato elemento nell'elenco.

UICollectionViewDelegate

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewDelegate classe in iOS 9:

  • GetTargetContentOffset : consente di ottenere l'offset di un determinato elemento della visualizzazione raccolta.
  • GetTargetIndexPathForMove : ottiene l'oggetto indexPath di un determinato elemento per un'operazione di trascinamento.

UICollectionViewFlowLayout

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewFlowLayout classe in iOS 9:

  • SectionFootersPinToVisibleBounds : attacca i piè di pagina della sezione ai limiti della visualizzazione raccolta visibile.
  • SectionHeadersPinToVisibleBounds : consente di associare le intestazioni di sezione ai limiti della visualizzazione raccolta visibile.

UICollectionViewLayout

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayout classe in iOS 9:

  • GetInvalidationContextForEndingInteractiveMovementOfItems : restituisce il contesto di invalidazione alla fine di un'operazione di trascinamento quando l'utente termina il trascinamento o lo annulla.
  • GetInvalidationContextForInteractivelyMovingItems : restituisce il contesto di invalidazione all'inizio di un'operazione di trascinamento.
  • GetLayoutAttributesForInteractivelyMovingItem : ottiene gli attributi di layout per un determinato elemento durante il trascinamento di un elemento.
  • GetTargetIndexPathForInteractivelyMovingItem : restituisce l'oggetto indexPath dell'elemento in corrispondenza del punto specificato durante il trascinamento di un elemento.

UICollectionViewLayoutAttributes

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayoutAttributes classe in iOS 9:

  • CollisionBoundingPath : restituisce il percorso di collisione di due elementi durante un'operazione di trascinamento.
  • CollisionBoundsType : restituisce il tipo di collisione (come UIDynamicItemCollisionBoundsType) che si è verificato durante un'operazione di trascinamento.

UICollectionViewLayoutInvalidationContext

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayoutInvalidationContext classe in iOS 9:

  • InteractiveMovementTarget : restituisce l'elemento di destinazione di un'operazione di trascinamento.
  • PreviousIndexPathsForInteractivelyMovingItems : restituisce l'oggetto indexPaths di altri elementi coinvolti in un'operazione di trascinamento per riordinare.
  • TargetIndexPathsForInteractivelyMovingItems : restituisce l'oggetto indexPaths degli elementi che verranno riordinati in seguito a un'operazione di trascinamento del riordino.

UICollectionViewSource

Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewSource classe in iOS 9:

  • CanMoveItem : informa la visualizzazione raccolta se un determinato elemento può essere riordinato.
  • GetTargetContentOffset : restituisce gli offset degli elementi che verranno spostati tramite un'operazione di trascinamento del riordinamento.
  • GetTargetIndexPathForMove : restituisce l'oggetto indexPath di un elemento che verrà spostato durante un'operazione di trascinamento del riordino.
  • MoveItem : sposta l'ordine di un determinato elemento nell'elenco.

Riepilogo

Questo articolo ha illustrato le modifiche apportate alle visualizzazioni della raccolta in iOS 9 e ha descritto come implementarle in Xamarin.iOS. Ha trattato l'implementazione di una semplice azione di trascinamento del riordinamento in una visualizzazione raccolta; utilizzando un riconoscimento movimento personalizzato con trascinamento verso il riordinamento; e il modo in cui il trascinamento del riordinamento influisce su un layout di visualizzazione raccolta personalizzato.