Teilen über


Sammlungsansichten in Xamarin.iOS

Sammlungsansichten ermöglichen die Anzeige von Inhalten mit beliebigen Layouts. Sie ermöglichen das einfache Erstellen von rasterähnlichen Layouts außerhalb des Felds und unterstützen gleichzeitig benutzerdefinierte Layouts.

Sammlungsansichten, die in der UICollectionView Klasse verfügbar sind, sind ein neues Konzept in iOS 6, das die Darstellung mehrerer Elemente auf dem Bildschirm mithilfe von Layouts eingeführt. Die Muster zum Bereitstellen von Daten für ein UICollectionView Element zum Erstellen und Interagieren mit diesen Elementen folgen den gleichen Delegierungs- und Datenquellenmustern, die in der iOS-Entwicklung häufig verwendet werden.

Sammlungsansichten funktionieren jedoch mit einem Layoutsubsystem, das von sich selbst unabhängig UICollectionView ist. Daher kann die Einfache Bereitstellung eines anderen Layouts die Darstellung einer Sammlungsansicht ganz einfach ändern.

iOS stellt eine Layoutklasse bereit, die es ermöglicht UICollectionViewFlowLayout , zeilenbasierte Layouts wie z. B. ein Raster ohne zusätzliche Arbeit zu erstellen. Außerdem können benutzerdefinierte Layouts erstellt werden, die jede Präsentation zulassen, die Sie sich vorstellen können.

UICollectionView Basics

Die UICollectionView Klasse besteht aus drei verschiedenen Elementen:

  • Zellen – Datengesteuerte Ansichten für jedes Element
  • Ergänzende Ansichten – Datengesteuerte Ansichten , die einem Abschnitt zugeordnet sind.
  • Dekorationsansichten – Nicht datengesteuerte Ansichten, die durch ein Layout erstellt wurden

Zellen

Zellen sind Objekte, die ein einzelnes Element im Dataset darstellen, das von der Auflistungsansicht dargestellt wird. Jede Zelle ist eine Instanz der UICollectionViewCell Klasse, die aus drei verschiedenen Ansichten besteht, wie in der folgenden Abbildung dargestellt:

Jede Zelle besteht aus drei verschiedenen Ansichten, wie hier gezeigt

Die UICollectionViewCell Klasse verfügt über die folgenden Eigenschaften für jede dieser Ansichten:

  • ContentView – Diese Ansicht enthält den Inhalt, den die Zelle darstellt. Sie wird in der obersten Z-Reihenfolge auf dem Bildschirm gerendert.
  • SelectedBackgroundView – Zellen haben unterstützung für die Auswahl integriert. Diese Ansicht wird verwendet, um visuell zu kennzeichnen, dass eine Zelle ausgewählt ist. Sie wird direkt unterhalb der ContentView Zelle gerendert, wenn eine Zelle ausgewählt wird.
  • BackgroundView – Zellen können auch einen Hintergrund anzeigen, der von der BackgroundView . Diese Ansicht wird unter der SelectedBackgroundView .

Durch Festlegen derArt ContentView , dass sie kleiner als die BackgroundView und SelectedBackgroundViewist, kann dies verwendet werden, um den BackgroundView Inhalt visuell zu framen, während die SelectedBackgroundView Anzeige erfolgt, wenn eine Zelle ausgewählt wird, wie unten dargestellt:

Die verschiedenen Zellelemente

Die Zellen im obigen Screenshot werden erstellt, indem sie erben UICollectionViewCell und die ContentViewEigenschaften SelectedBackgroundViewBackgroundView bzw. Eigenschaften festlegen, wie im folgenden Code gezeigt:

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

Ergänzende Ansichten

Ergänzende Ansichten sind Ansichten, die Informationen enthalten, die den einzelnen Abschnitten eines Abschnitts UICollectionViewzugeordnet sind. Wie Zellen sind ergänzende Ansichten datengesteuert. Wenn Zellen die Elementdaten aus einer Datenquelle darstellen, stellen ergänzende Ansichten die Abschnittsdaten dar, z. B. die Kategorien des Buchs in einem Bücherhelf oder das Musikgenre in einer Musikbibliothek.

Beispielsweise könnte eine ergänzende Ansicht verwendet werden, um eine Kopfzeile für einen bestimmten Abschnitt darzustellen, wie in der folgenden Abbildung dargestellt:

Eine ergänzende Ansicht, die verwendet wird, um eine Kopfzeile für einen bestimmten Abschnitt darzustellen, wie hier gezeigt

Um eine ergänzende Ansicht zu verwenden, muss sie zuerst in der ViewDidLoad Methode registriert werden:

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

Anschließend muss die Ansicht mithilfe GetViewForSupplementaryElementvon , erstellt mit DequeueReusableSupplementaryViewund erbt von UICollectionReusableView. Der folgende Codeausschnitt erzeugt die im Obigen Screenshot gezeigte Ergänzungsansicht:

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

Ergänzende Ansichten sind allgemeiner als nur Kopf- und Fußzeilen. Sie können an einer beliebigen Stelle in der Sammlungsansicht positioniert werden und können aus allen Ansichten bestehen, wodurch ihre Darstellung vollständig angepasst werden kann.

Dekoration Ansichten

Dekorationsansichten sind rein visuelle Ansichten, die in einer UICollectionView. Im Gegensatz zu Zellen und ergänzenden Ansichten sind sie nicht datengesteuert. Sie werden immer in der Unterklasse eines Layouts erstellt und können sich anschließend als Layout des Inhalts ändern. Beispielsweise könnte eine Dekorationsansicht verwendet werden, um eine Hintergrundansicht darzustellen, die mit dem Inhalt in der UICollectionViewAbbildung scrollt, wie unten dargestellt:

Dekorationsansicht mit rotem Hintergrund

Der folgende Codeausschnitt ändert den Hintergrund in der Beispielklasse CircleLayout in Rot:

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

Data source

Wie bei anderen Teilen von iOS, z UITableView . B. und MKMapView, UICollectionView ruft seine Daten aus einer Datenquelle ab, die in Xamarin.iOS über die UICollectionViewDataSource Klasse verfügbar gemacht wird. Diese Klasse ist für die Bereitstellung von Inhalten für folgendes UICollectionView verantwortlich:

  • Zellen – Wird von GetCell der Methode zurückgegeben.
  • Ergänzende Ansichten – Wird von GetViewForSupplementaryElement der Methode zurückgegeben.
  • Anzahl der Abschnitte – Von NumberOfSections der Methode zurückgegeben. Standardwert ist 1, wenn sie nicht implementiert ist.
  • Anzahl der Elemente pro Abschnitt – Von GetItemsCount der Methode zurückgegeben.

UICollectionViewController

Zur Bequemlichkeit ist die UICollectionViewController Klasse verfügbar. Dies wird automatisch so konfiguriert, dass es sich um die Stellvertretung handelt, die im nächsten Abschnitt erläutert wird, und als Datenquelle für die UICollectionView Ansicht.

Wie bei UITableViewder Klasse UICollectionView wird die Datenquelle nur aufgerufen, um Zellen für Elemente abzurufen, die sich auf dem Bildschirm befinden. Zellen, die auf dem Bildschirm scrollen, werden zur Wiederverwendung in eine Warteschlange eingefügt, wie die folgende Abbildung veranschaulicht:

Zellen, die auf dem Bildschirm scrollen, werden wie hier gezeigt in eine Warteschlange eingefügt, um sie wiederzuverwenden.

Die Zellwiederverwendung wurde vereinfacht mit UICollectionView und UITableView. Sie müssen eine Zelle nicht mehr direkt in der Datenquelle erstellen, wenn sie in der Wiederverwendungswarteschlange nicht verfügbar ist, da Zellen beim System registriert sind. Wenn eine Zelle nicht verfügbar ist, wenn der Aufruf der Zelle aus der Wiederverwendungswarteschlange entfernt wird, erstellt iOS sie automatisch basierend auf dem registrierten Typ oder Nib. Das gleiche Verfahren steht auch für ergänzende Ansichten zur Verfügung.

Betrachten Sie beispielsweise den folgenden Code, der die AnimalCell Klasse registriert:

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

Wenn eine UICollectionView Zelle benötigt wird, da sich das Element auf dem Bildschirm befindet, ruft die UICollectionView Methode der GetCell Datenquelle auf. Ähnlich wie bei UITableView ist diese Methode für die Konfiguration einer Zelle aus den Sicherungsdaten verantwortlich, was in diesem Fall eine AnimalCell Klasse wäre.

Der folgende Code zeigt eine Implementierung, die GetCell eine AnimalCell Instanz zurückgibt:

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

Der Anruf an DequeReusableCell den Ort, an dem die Zelle entweder aus der Wiederverwendungswarteschlange in die Warteschlange eingereiht wird oder, wenn eine Zelle nicht in der Warteschlange verfügbar ist, basierend auf dem Typ erstellt wird, der im Anruf CollectionView.RegisterClassForCellregistriert ist.

In diesem Fall erstellt iOS durch die Registrierung der AnimalCell Klasse eine neue AnimalCell intern und gibt sie zurück, wenn ein Aufruf der De-Warteschlange eine Zelle erfolgt, nach der sie mit dem In der Tierklasse enthaltenen Bild konfiguriert und zur Anzeige an die UICollectionViewZurückgegeben wird.

Delegieren

Die UICollectionView Klasse verwendet einen Delegat vom Typ UICollectionViewDelegate , um die Interaktion mit Inhalten in der UICollectionView. Dies ermöglicht die Steuerung von:

  • Zellauswahl – Bestimmt, ob eine Zelle ausgewählt ist.
  • Zellmarkierung – Bestimmt, ob eine Zelle gerade berührt wird.
  • Zellmenüs – Menü, das als Reaktion auf eine lange Druckgeste für eine Zelle angezeigt wird.

Wie bei der Datenquelle ist dies UICollectionViewController standardmäßig als Stellvertretung für die UICollectionView.

Zellen highLighting

Wenn eine Zelle gedrückt wird, wechselt die Zelle in einen hervorgehobenen Zustand, und sie wird erst ausgewählt, wenn der Benutzer seinen Finger von der Zelle hebt. Dies ermöglicht eine temporäre Änderung der Darstellung der Zelle, bevor sie tatsächlich ausgewählt wird. Nach auswahl wird die Zelle SelectedBackgroundView angezeigt. Die folgende Abbildung zeigt den hervorgehobenen Zustand unmittelbar vor dem Auftreten der Auswahl:

Diese Abbildung zeigt den hervorgehobenen Zustand unmittelbar vor dem Auftreten der Auswahl.

Um hervorhebungen zu implementieren, können die ItemHighlighted Methoden und ItemUnhighlighted Methoden der UICollectionViewDelegate Anwendung verwendet werden. Der folgende Code wendet z. B. einen gelben Hintergrund an ContentView , wenn die Zelle hervorgehoben ist, und einen weißen Hintergrund, wenn er nicht hervorgehoben wird, wie in der abbildung oben gezeigt:

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

Deaktivieren der Auswahl

Die Auswahl ist standardmäßig in UICollectionView. Um die Auswahl zu deaktivieren, setzen Sie "false" außer Kraft ShouldHighlightItem , und geben Sie "false" zurück, wie unten dargestellt:

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

Wenn die Hervorhebung deaktiviert ist, wird auch der Vorgang zum Auswählen einer Zelle deaktiviert. Darüber hinaus gibt es auch eine ShouldSelectItem Methode, mit der die Auswahl direkt gesteuert wird, auch wenn ShouldHighlightItem diese implementiert und "false" zurückgegeben wird, ShouldSelectItem nicht aufgerufen wird.

ShouldSelectItem ermöglicht das Aktivieren oder Deaktivieren der Auswahl auf Element-nach-Element-Basis, wenn ShouldHighlightItem sie nicht implementiert ist. Sie ermöglicht auch die Hervorhebung ohne Auswahl, wenn ShouldHighlightItem sie implementiert ist und "true" zurückgibt, während ShouldSelectItem "false" zurückgegeben wird.

Zellmenüs

Jede Zelle in einer UICollectionView Zelle kann ein Menü anzeigen, das das Ausschneiden, Kopieren und Einfügen optional unterstützt. So erstellen Sie ein Bearbeitungsmenü in einer Zelle:

  1. Überschreiben ShouldShowMenu und zurückgeben "true", wenn das Element ein Menü anzeigen soll.
  2. Überschreiben CanPerformAction sie und geben "true" für jede Aktion zurück, die das Element ausführen kann. Dabei handelt es sich um einen Ausschneiden, Kopieren oder Einfügen.
  3. Überschreiben PerformAction , um die Bearbeitung, kopieren des Einfügevorgangs auszuführen.

Der folgende Screenshot zeigt das Menü, wenn eine Zelle lang gedrückt wird:

Dieser Screenshot zeigt das Menü, wenn eine Zelle lang gedrückt ist.

Layout

UICollectionView unterstützt ein Layoutsystem, das die Positionierung aller Elemente, Zellen, Ergänzende Ansichten und Dekorationsansichten ermöglicht, unabhängig vom UICollectionView selbst verwaltet zu werden. Mithilfe des Layoutsystems kann eine Anwendung Layouts wie das rasterähnliche Layout unterstützen, das wir in diesem Artikel gesehen haben, sowie benutzerdefinierte Layouts bereitstellen.

Layoutgrundlagen

Layouts in a UICollectionView werden in einer Klasse definiert, von UICollectionViewLayoutder geerbt wird. Die Layoutimplementierung ist für das Erstellen der Layoutattribute für jedes Element in der UICollectionView. Es gibt zwei Möglichkeiten zum Erstellen eines Layouts:

  • Verwenden Sie die integrierte UICollectionViewFlowLayout .
  • Stellen Sie ein benutzerdefiniertes Layout bereit, indem Sie von UICollectionViewLayout .

Flusslayout

Die UICollectionViewFlowLayout Klasse bietet ein linienbasiertes Layout, das zum Anordnen von Inhalten in einem Raster von Zellen geeignet ist, wie wir gesehen haben.

So verwenden Sie ein Flusslayout:

  • Erstellen einer Instanz von UICollectionViewFlowLayout :
var layout = new UICollectionViewFlowLayout ();
  • Übergeben Sie die Instanz an den Konstruktor der UICollectionView :
simpleCollectionViewController = new SimpleCollectionViewController (layout);

Dies ist alles, was zum Layout von Inhalten in einem Raster erforderlich ist. Wenn sich die Ausrichtung ändert, werden die UICollectionViewFlowLayout Ziehpunkte auch entsprechend anordnen, wie unten dargestellt:

Beispiel für die Ausrichtungsänderungen

Abschnittseinbruch

Um Platz um die UIContentViewLayouts bereitzustellen, weisen sie eine SectionInset Eigenschaft vom Typ UIEdgeInsetsauf. Der folgende Code stellt beispielsweise einen 50-Pixel-Puffer um jeden Abschnitt des UIContentView durch ein UICollectionViewFlowLayout:

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

Dies führt zu Abständen um den Abschnitt, wie unten dargestellt:

Abstand um den Abschnitt, wie hier gezeigt

Unterklassen von UICollectionViewFlowLayout

In edition to using UICollectionViewFlowLayout directly, it can also be subclassed to further customize the layout of content along a line. Dies kann beispielsweise verwendet werden, um ein Layout zu erstellen, das die Zellen nicht in ein Raster umschließt, sondern stattdessen eine einzelne Zeile mit einem horizontalen Bildlaufeffekt erstellt, wie unten dargestellt:

Eine einzelne Zeile mit einem horizontalen Bildlaufeffekt

Um dies durch Unterklassen UICollectionViewFlowLayout zu implementieren, ist Folgendes erforderlich:

  • Initialisieren sie alle Layouteigenschaften, die auf das Layout selbst oder alle Elemente im Layout im Konstruktor angewendet werden.
  • Überschreiben, ShouldInvalidateLayoutForBoundsChange "true" zurückgeben, sodass beim Ändern UICollectionView das Layout der Zellen neu berechnet wird. Dies wird in diesem Fall verwendet, um sicherzustellen, dass der Code für die Transformation, die auf die zentriertste Zelle angewendet wird, während des Bildlaufs angewendet wird.
  • Überschreiben, TargetContentOffset damit die zentriertste Zelle an der Mitte des UICollectionView Bildlaufstopps ausgerichtet wird.
  • ÜberschreibenLayoutAttributesForElementsInRect, um ein Array von zurückzugeben.UICollectionViewLayoutAttributes Jede UICollectionViewLayoutAttribute enthält Informationen zum Layout des jeweiligen Elements, einschließlich Eigenschaften wie deren Center , SizeZIndex und Transform3D .

Der folgende Code zeigt eine solche Implementierung:

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);
    }

  }
}

Benutzerdefiniertes Layout

Zusätzlich zur Verwendung UICollectionViewFlowLayoutkönnen Layouts auch vollständig angepasst werden, indem sie direkt von UICollectionViewLayout.

Die wichtigsten Methoden zum Außerkraftsetzen sind:

  • PrepareLayout – Wird für die Durchführung anfänglicher geometrischer Berechnungen verwendet, die während des gesamten Layoutprozesses verwendet werden.
  • CollectionViewContentSize – Gibt die Größe des Bereichs zurück, der zum Anzeigen von Inhalten verwendet wird.
  • LayoutAttributesForElementsInRect – Wie bereits im oben gezeigten Beispiel für UICollectionViewFlowLayout wird diese Methode verwendet, um Informationen zum UICollectionView Layout der einzelnen Elemente bereitzustellen. Im Gegensatz zum UICollectionViewFlowLayout Erstellen eines benutzerdefinierten Layouts können Sie elemente jedoch bei der Auswahl positionieren.

Beispielsweise könnte der gleiche Inhalt in einem kreisförmigen Layout dargestellt werden, wie unten gezeigt:

Ein kreisförmiges benutzerdefiniertes Layout wie hier gezeigt

Das Leistungsstarke an Layouts besteht darin, dass zum Wechseln vom rasterähnlichen Layout zu einem horizontalen Bildlauflayout und anschließend zu diesem kreisförmigen Layout nur die Layoutklasse erforderlich ist, die für die UICollectionView Änderung bereitgestellt wird. Nichts im UICollectionViewStellvertretungs- oder Datenquellencode ändert sich überhaupt.

Änderungen in iOS 9

In iOS 9 unterstützt die Sammlungsansicht (UICollectionView) jetzt das Neuanordnen von Elementen aus dem Feld, indem eine neue Standardgestenerkennung und mehrere neue Unterstützende Methoden hinzugefügt werden.

Mithilfe dieser neuen Methoden können Sie das Ziehen ganz einfach implementieren, um sie in der Sammlungsansicht neu anzuordnen und die Möglichkeit haben, die Darstellung der Elemente während einer beliebigen Phase des Neuanordnungsprozesses anzupassen.

Beispiel für den Neuanordnungsprozess

In diesem Artikel befassen wir uns mit der Implementierung von Drag-to-Reorder in einer Xamarin.iOS-Anwendung sowie mit einigen der anderen Änderungen, die iOS 9 am Steuerelement für die Sammlungsansicht vorgenommen hat:

Neuanordnung von Elementen

Wie bereits erwähnt, war eine der wichtigsten Änderungen an der Sammlungsansicht in iOS 9 das Hinzufügen einfacher Funktionen zum Neuanordnen von Ziehvorgängen.

In iOS 9 besteht die schnellste Möglichkeit zum Hinzufügen der Neuanordnung zu einer Sammlungsansicht darin, eine UICollectionViewController. Der Sammlungsansichtscontroller verfügt jetzt über eine InstallsStandardGestureForInteractiveMovement Eigenschaft, die eine Standardgestenerkennung hinzufügt, die das Ziehen zum Neuanordnen von Elementen in der Auflistung unterstützt. Da der Standardwert lautet true, müssen Sie nur die MoveItem Methode der UICollectionViewDataSource Klasse implementieren, um drag-to-reorder zu unterstützen. Zum Beispiel:

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

Beispiel für einfache Neuanordnung

Beginnen Sie als schnelles Beispiel ein neues Xamarin.iOS-Projekt, und bearbeiten Sie die Datei "Main.storyboard" . Ziehen Sie ein Shape UICollectionViewController auf die Entwurfsoberfläche:

Hinzufügen eines UICollectionViewController

Wählen Sie die Sammlungsansicht aus (Dies ist möglicherweise am einfachsten in der Dokumentgliederung möglich). Legen Sie auf der Registerkarte "Layout" des Eigenschaftenpads die folgenden Größen fest, wie im folgenden Screenshot dargestellt:

  • Zellengröße: Breite – 60 | Höhe – 60
  • Kopfzeilengröße: Breite – 0 | Höhe – 0
  • Fußzeilengröße: Breite – 0 | Höhe – 0
  • Min Abstand: Für Zellen – 8 | Für Linien – 8
  • Abschnittsmengen: Top – 16 | Unten – 16 | Links – 16 | Rechts – 16

Festlegen der Größe der Sammlungsansicht

Bearbeiten Sie als Nächstes die Standardzelle:

  • Ändern der Hintergrundfarbe in Blau
  • Hinzufügen einer Beschriftung, die als Titel für die Zelle fungiert
  • Festlegen des Wiederverwendungsbezeichners auf Zelle

Bearbeiten der Standardzelle

Fügen Sie Einschränkungen hinzu, um die Beschriftung innerhalb der Zelle zentriert zu halten, während sich die Größe ändert:

Legen Sie im Property Pad für collectionViewCell die Klasse auf :TextCollectionViewCell

Festlegen der Klasse auf

Legen Sie die wiederverwendbare Sammlungsansicht auf Cell:

Festlegen der wiederverwendbaren Sammlungsansicht auf Zelle

Wählen Sie schließlich das Etikett aus, und nennen Sie es TextLabel:

Name label TextLabel

Bearbeiten Sie die TextCollectionViewCell Klasse, und fügen Sie die folgenden Eigenschaften hinzu:

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
  }
}

Hier wird die Text Eigenschaft der Beschriftung als Titel der Zelle verfügbar gemacht, sodass sie aus Code festgelegt werden kann.

Fügen Sie dem Projekt eine neue C#-Klasse hinzu, und rufen Sie sie WaterfallCollectionSourceauf. Bearbeiten Sie die Datei, und stellen Sie sicher, dass sie wie folgt aussieht:

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
  }
}

Diese Klasse ist die Datenquelle für unsere Sammlungsansicht und stellt die Informationen für jede Zelle in der Sammlung bereit. Beachten Sie, dass die MoveItem Methode implementiert ist, damit Elemente in der Auflistung neu angeordnet werden können.

Fügen Sie dem Projekt eine weitere neue C#-Klasse hinzu, und rufen Sie sie WaterfallCollectionDelegateauf. Bearbeiten Sie diese Datei, und sehen Sie wie folgt aus:

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
  }
}

Dies dient als Stellvertretung für unsere Sammlungsansicht. Methoden wurden außer Kraft gesetzt, um eine Zelle hervorzuheben, während der Benutzer in der Auflistungsansicht damit interagiert.

Fügen Sie dem Projekt eine letzte C#-Klasse hinzu, und rufen Sie sie WaterfallCollectionViewauf. Bearbeiten Sie diese Datei, und sehen Sie wie folgt aus:

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
  }
}

Beachten Sie, dass DataSource und Delegate dass wir oben erstellt werden, wenn die Sammlungsansicht aus dem Storyboard (oder XIB-Datei ) erstellt wird.

Bearbeiten Sie die Datei "Main.storyboard " erneut, und wählen Sie die Sammlungsansicht aus, und wechseln Sie zu den Eigenschaften. Legen Sie die Klasse auf die benutzerdefinierte WaterfallCollectionView Klasse fest, die wir oben definiert haben:

Speichern Sie die Änderungen, die Sie an der Benutzeroberfläche vorgenommen haben, und führen Sie die App aus. Wenn der Benutzer ein Element aus der Liste auswählt und es an eine neue Position zieht, werden die anderen Elemente automatisch animiert, wenn sie sich aus dem Weg des Elements bewegen. Wenn der Benutzer das Element an einer neuen Position abbricht, bleibt es an dieser Position. Zum Beispiel:

Beispiel für das Ziehen eines Elements an eine neue Position

Verwenden einer benutzerdefinierten Gestikerkennung

In Fällen, in denen Sie kein reguläres UICollectionViewController Steuerelement verwenden können und ein reguläres UIViewControllerElement verwenden müssen, oder wenn Sie mehr Kontrolle über die Zieh- und Drop-Geste übernehmen möchten, können Sie eine eigene benutzerdefinierte Gestenerkennung erstellen und sie der Sammlungsansicht hinzufügen, wenn die Ansicht geladen wird. Zum Beispiel:

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);
}

Hier verwenden wir mehrere neue Methoden, die der Sammlungsansicht hinzugefügt wurden, um den Ziehvorgang zu implementieren und zu steuern:

  • BeginInteractiveMovementForItem – Markiert den Anfang eines Verschiebungsvorgangs.
  • UpdateInteractiveMovementTargetPosition - Wird gesendet, wenn der Speicherort des Elements aktualisiert wird.
  • EndInteractiveMovement - Markiert das Ende einer Elementverschiebung.
  • CancelInteractiveMovement – Markiert den Benutzer, der den Verschiebungsvorgang abbricht.

Wenn die Anwendung ausgeführt wird, funktioniert der Ziehvorgang genau wie die standardmäßige Ziehgestenerkennung, die im Lieferumfang der Sammlungsansicht enthalten ist.

Benutzerdefinierte Layouts und Neuanordnung

In iOS 9 wurden mehrere neue Methoden hinzugefügt, um mit Drag-to-Reorder- und benutzerdefinierten Layouts in einer Sammlungsansicht zu arbeiten. Um dieses Feature zu erkunden, fügen wir der Sammlung ein benutzerdefiniertes Layout hinzu.

Fügen Sie zunächst eine neue C#-Klasse hinzu, die dem Projekt aufgerufen wird WaterfallCollectionLayout . Bearbeiten Sie sie, und stellen Sie sicher, dass sie wie folgt aussieht:

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
  }
}

Dies kann eine Klasse verwendet werden, um der Sammlungsansicht ein benutzerdefiniertes Zweispaltenlayout vom Wasserfalltyp bereitzustellen. Der Code verwendet Key-Value Coding (über die WillChangeValue und DidChangeValue Methoden), um datenbindung für unsere berechneten Eigenschaften in dieser Klasse bereitzustellen.

Bearbeiten Sie als Nächstes die WaterfallCollectionSource folgenden Änderungen und Ergänzungen, und nehmen Sie sie vor:

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);
  }
}

Dadurch wird für jedes element, das in der Liste angezeigt wird, eine zufällige Höhe erstellt.

Bearbeiten Sie als Nächstes die WaterfallCollectionView Klasse, und fügen Sie die folgende Hilfseigenschaft hinzu:

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

Dies erleichtert das Abrufen der Datenquelle (und der Elementhöhen) aus dem benutzerdefinierten Layout.

Bearbeiten Sie schließlich den Ansichtscontroller, und fügen Sie den folgenden Code hinzu:

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);
}

Dadurch wird eine Instanz unseres benutzerdefinierten Layouts erstellt, das Ereignis festgelegt, um die Größe jedes Elements bereitzustellen und das neue Layout an unsere Sammlungsansicht anzufügen.

Wenn wir die Xamarin.iOS-App erneut ausführen, sieht die Sammlungsansicht nun wie folgt aus:

Die Sammlungsansicht sieht nun wie folgt aus:

Wir können Elemente nach wie vor ziehen und neu anordnen, aber die Elemente ändern jetzt die Größe an ihre neue Position, wenn sie gelöscht werden.

Änderungen der Sammlungsansicht

In den folgenden Abschnitten befassen wir uns ausführlich mit den Änderungen, die an den einzelnen Klassen in der Sammlungsansicht von iOS 9 vorgenommen wurden.

UICollectionView

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionView Klasse für iOS 9 vorgenommen:

  • BeginInteractiveMovementForItem – Markiert den Anfang eines Ziehvorgangs.
  • CancelInteractiveMovement – Informiert die Sammlungsansicht, dass der Benutzer einen Ziehvorgang abgebrochen hat.
  • EndInteractiveMovement – Informiert die Sammlungsansicht, dass der Benutzer einen Ziehvorgang abgeschlossen hat.
  • GetIndexPathsForVisibleSupplementaryElements – Gibt die indexPath Kopf- oder Fußzeile in einem Sammlungsansichtsbereich zurück.
  • GetSupplementaryView – Gibt die angegebene Kopf- oder Fußzeile zurück.
  • GetVisibleSupplementaryViews – Gibt eine Liste aller sichtbaren Kopf- und Fußzeilen zurück.
  • UpdateInteractiveMovementTargetPosition – Informiert die Sammlungsansicht, dass der Benutzer während eines Ziehvorgangs ein Element verschoben oder verschoben hat.

UICollectionViewController

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewController Klasse in iOS 9 vorgenommen:

  • InstallsStandardGestureForInteractiveMovement – Wenn true die neue Gestenerkennung, die automatisch drag-to-reorder unterstützt, verwendet wird.
  • CanMoveItem – Informiert die Sammlungsansicht, wenn ein bestimmtes Element neu angeordnet werden kann.
  • GetTargetContentOffset – Wird verwendet, um den Offset eines bestimmten Sammlungsansichtselements abzurufen.
  • GetTargetIndexPathForMove – Ruft das indexPath element eines bestimmten Elements für einen Ziehvorgang ab.
  • MoveItem – Verschiebt die Reihenfolge eines bestimmten Elements in der Liste.

UICollectionViewDataSource

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewDataSource Klasse in iOS 9 vorgenommen:

  • CanMoveItem – Informiert die Sammlungsansicht, wenn ein bestimmtes Element neu angeordnet werden kann.
  • MoveItem – Verschiebt die Reihenfolge eines bestimmten Elements in der Liste.

UICollectionViewDelegate

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewDelegate Klasse in iOS 9 vorgenommen:

  • GetTargetContentOffset – Wird verwendet, um den Offset eines bestimmten Sammlungsansichtselements abzurufen.
  • GetTargetIndexPathForMove – Ruft das indexPath element eines bestimmten Elements für einen Ziehvorgang ab.

UICollectionViewFlowLayout

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewFlowLayout Klasse in iOS 9 vorgenommen:

  • SectionFootersPinToVisibleBounds – Hält die Abschnittsfußzeilen an die Grenzen der sichtbaren Sammlungsansicht fest.
  • SectionHeadersPinToVisibleBounds – Übergibt die Abschnittsüberschriften an die Grenzen der sichtbaren Sammlungsansicht.

UICollectionViewLayout

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewLayout Klasse in iOS 9 vorgenommen:

  • GetInvalidationContextForEndingInteractiveMovementOfItems – Gibt den Ungültigkeitskontext am Ende eines Ziehvorgangs zurück, wenn der Benutzer den Ziehvorgang beendet oder abbricht.
  • GetInvalidationContextForInteractivelyMovingItems – Gibt den Ungültigkeitskontext am Anfang eines Ziehvorgangs zurück.
  • GetLayoutAttributesForInteractivelyMovingItem – Ruft die Layoutattribute für ein bestimmtes Element beim Ziehen eines Elements ab.
  • GetTargetIndexPathForInteractivelyMovingItem – Gibt das indexPath Element zurück, das sich beim Ziehen eines Elements am angegebenen Punkt befindet.

UICollectionViewLayoutAttributes

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewLayoutAttributes Klasse in iOS 9 vorgenommen:

  • CollisionBoundingPath – Gibt den Kollisionspfad zweier Elemente während eines Ziehvorgangs zurück.
  • CollisionBoundsType – Gibt den Typ der Kollision (als a UIDynamicItemCollisionBoundsType) zurück, die während eines Ziehvorgangs aufgetreten ist.

UICollectionViewLayoutInvalidationContext

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewLayoutInvalidationContext Klasse in iOS 9 vorgenommen:

  • InteractiveMovementTarget – Gibt das Zielelement eines Ziehvorgangs zurück.
  • PreviousIndexPathsForInteractivelyMovingItems – Gibt die anderen Elemente zurück, die indexPaths an einer Ziehaktion beteiligt sind, um den Vorgang neu anzuordnen.
  • TargetIndexPathsForInteractivelyMovingItems – Gibt die Elemente zurück, die indexPaths aufgrund eines Vorgangs zum Neuanordnen des Ziehens neu angeordnet werden.

UICollectionViewSource

Die folgenden Änderungen oder Ergänzungen wurden an der UICollectionViewSource Klasse in iOS 9 vorgenommen:

  • CanMoveItem – Informiert die Sammlungsansicht, wenn ein bestimmtes Element neu angeordnet werden kann.
  • GetTargetContentOffset – Gibt die Offsets von Elementen zurück, die über einen Drag-to-Reorder-Vorgang verschoben werden.
  • GetTargetIndexPathForMove – Gibt das indexPath Element zurück, das während eines Vorgangs zum Neuanordnen verschoben wird.
  • MoveItem – Verschiebt die Reihenfolge eines bestimmten Elements in der Liste.

Zusammenfassung

In diesem Artikel wurden die Änderungen an Sammlungsansichten in iOS 9 behandelt und beschrieben, wie sie in Xamarin.iOS implementiert werden. Die Implementierung einer einfachen Aktion zum Neuanordnen von Drag-to-Reorder in einer Sammlungsansicht wurde behandelt. Verwenden einer benutzerdefinierten Gestenerkennung mit drag-to-reorder; und wie sich drag-to-reorder auf ein benutzerdefiniertes Sammlungsansichtslayout auswirkt.