Zobrazení kolekcí v Xamarin.iOS

Zobrazení kolekcí umožňují zobrazení obsahu pomocí libovolných rozložení. Umožňují snadno vytvářet rozložení podobná mřížce a zároveň podporovat vlastní rozložení.

Zobrazení kolekcí, která jsou k dispozici ve UICollectionView třídě, jsou novým konceptem v iOSu 6, který představuje prezentaci více položek na obrazovce pomocí rozložení. Vzory pro poskytování dat k UICollectionView vytváření položek a interakci s těmito položkami se řídí stejnými vzory delegování a zdroje dat, které se běžně používají při vývoji pro iOS.

Zobrazení kolekcí ale pracují se subsystémem rozložení, který je nezávislý na UICollectionView sobě. Proto jednoduše poskytnutí jiného rozložení může snadno změnit prezentaci zobrazení kolekce.

iOS poskytuje třídu rozložení, UICollectionViewFlowLayout která umožňuje vytvářet rozložení založená na řádcích, jako je mřížka, bez další práce. Můžete také vytvořit vlastní rozložení, která umožňují libovolnou prezentaci, kterou si můžete představit.

Základy UICollectionView

Třída UICollectionView se skládá ze tří různých položek:

  • Buňky – zobrazení řízená daty pro každou položku
  • Doplňková zobrazení – zobrazení založená na datech přidružená k oddílu.
  • Zobrazení dekorace – nedefinovaná zobrazení vytvořená rozložením

Cells

Buňky jsou objekty, které představují jednu položku v sadě dat, která je prezentována zobrazením kolekce. Každá buňka je instance UICollectionViewCell třídy, která se skládá ze tří různých zobrazení, jak je znázorněno na následujícím obrázku:

Each cell is composed of three different views, as shown here

Třída UICollectionViewCell má pro každé z těchto zobrazení následující vlastnosti:

  • ContentView – Toto zobrazení obsahuje obsah, který buňka prezentuje. Vykreslí se v nejvyšším pořadí vykreslování na obrazovce.
  • SelectedBackgroundView – Buňky mají integrovanou podporu výběru. Toto zobrazení slouží k vizuálnímu označení vybrané buňky. Zobrazí se těsně pod vybranou ContentView buňkou.
  • BackgroundView – Buňky mohou také zobrazit pozadí, které je prezentováno BackgroundView . Toto zobrazení je vykresleno pod položkou SelectedBackgroundView .

ContentView Nastavením tak, aby byla menší než BackgroundView a SelectedBackgroundView, BackgroundView lze použít k vizuálnímu rámečku obsahu, zatímco SelectedBackgroundView se zobrazí při výběru buňky, jak je znázorněno níže:

The different cell elements

Buňky na výše uvedeném snímku obrazovky jsou vytvořeny zděděním UICollectionViewCell a nastavením SelectedBackgroundViewContentViewa BackgroundView vlastností, jak je znázorněno v následujícím kódu:

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

Doplňková zobrazení

Doplňková zobrazení jsou zobrazení, která představují informace spojené s každou částí oddílu UICollectionView. Podobně jako buňky jsou doplňková zobrazení řízená daty. Kde buňky prezentují data položky ze zdroje dat, doplňková zobrazení prezentují data oddílu, jako jsou kategorie knihy v knihovně knih nebo žánr hudby v hudební knihovně.

K prezentaci záhlaví konkrétního oddílu můžete použít například doplňkové zobrazení, jak je znázorněno na následujícím obrázku:

A Supplementary View used to present a header for a particular section, as shown here

Chcete-li použít doplňkové zobrazení, musí být nejprve registrován v ViewDidLoad metodě:

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

Pak je potřeba zobrazení vrátit pomocí , vytvořené pomocí GetViewForSupplementaryElementDequeueReusableSupplementaryView, a dědí z UICollectionReusableView. Následující fragment kódu vytvoří doplněk SupplementaryView zobrazený na snímku obrazovky výše:

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

Doplňková zobrazení jsou obecnější než jen záhlaví a zápatí. Dají se umístit kdekoli v zobrazení kolekce a můžou se skládat z libovolných zobrazení, takže jejich vzhled je plně přizpůsobitelný.

Dekorační zobrazení

Dekorační zobrazení jsou čistě vizuální zobrazení, která lze zobrazit v objektu UICollectionView. Na rozdíl od buněk a doplňkových zobrazení nejsou řízené daty. Vždy se vytvoří v rámci podtřídy rozložení a následně se můžou změnit jako rozložení obsahu. Například zobrazení dekorace lze použít k prezentaci zobrazení na pozadí, které se posunuje s obsahem v zobrazení UICollectionView, jak je znázorněno níže:

Decoration View with a red background

Následující fragment kódu změní pozadí na červenou ve třídě ukázek CircleLayout :

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

Zdroj dat

Stejně jako u jiných částí iOSu, například UITableView a MKMapView, UICollectionView získává data ze zdroje dat, který je vystaven v Xamarin.iOS prostřednictvím UICollectionViewDataSource třídy. Tato třída zodpovídá za poskytování obsahu UICollectionView , například:

  • Buňky – Vráceno z GetCell metody.
  • Doplňková zobrazení – Vráceno z GetViewForSupplementaryElement metody.
  • Počet oddílů – Vráceno z NumberOfSections metody. Výchozí hodnota je 1, pokud není implementována.
  • Počet položek na oddíl – Vráceno z GetItemsCount metody.

UICollectionViewController

Pro usnadnění UICollectionViewController práce je třída k dispozici. Tato možnost je automaticky nakonfigurovaná tak, aby byla delegátem, což je popsáno v další části a zdroj dat pro jeho UICollectionView zobrazení.

UITableViewStejně jako v UICollectionView případě , třída bude volat pouze zdroj dat získat buňky pro položky, které jsou na obrazovce. Buňky, které se posunou mimo obrazovku, se umístí do fronty pro opakované použití, jak znázorňuje následující obrázek:

Cells that scroll off the screen are placed in to a queue for reuse as shown here

Opakované použití buněk bylo zjednodušeno pomocí UICollectionView a UITableView. Buňku už nemusíte vytvářet přímo ve zdroji dat, pokud není k dispozici ve frontě pro opakované použití, protože buňky jsou zaregistrované v systému. Pokud není buňka při volání zrušení fronty buňka z opakované fronty k dispozici, iOS ji automaticky vytvoří na základě typu nebo nib, který byl zaregistrován. Stejná technika je také k dispozici pro doplňková zobrazení.

Představte si například následující kód, který zaregistruje AnimalCell třídu:

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

UICollectionView Když potřebuje buňku, protože je její položka na obrazovce, UICollectionView volá metodu GetCell zdroje dat. Podobně jako to funguje s UITableView, tato metoda je zodpovědná za konfiguraci buňky z backingových dat, což by v tomto případě byla AnimalCell třída.

Následující kód ukazuje implementaci GetCell , která vrací AnimalCell instanci:

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

Volání DequeReusableCell je místo, kde bude buňka z fronty znovu zařazena do fronty, nebo pokud buňka není k dispozici ve frontě, vytvořena na základě typu registrovaného v volání CollectionView.RegisterClassForCell.

V tomto případě, když zaregistrujete AnimalCell třídu, iOS vytvoří novou AnimalCell interně a vrátí ji při volání de-queue buňky, po které je nakonfigurována s obrázkem obsaženým ve třídě zvířat a vrácen pro zobrazení na UICollectionView.

Delegát

Třída UICollectionView používá delegáta typu UICollectionViewDelegate k podpoře interakce s obsahem v objektu UICollectionView. To umožňuje řídit:

  • Výběr buňky – Určuje, zda je buňka vybrána.
  • Zvýraznění buněk – Určuje, jestli se buňka právě dotkne.
  • Nabídky buněk – Nabídka zobrazená pro buňku v reakci na dlouhé stisknutí gesta

Stejně jako u zdroje dat je ve výchozím nastavení nakonfigurovaný tak, UICollectionViewController aby byl delegátem pro .UICollectionView

Zvýraznění buněk

Když je buňka stisknuta, buňka přejde do zvýrazněného stavu a není vybraná, dokud uživatel nezvedne prst z buňky. To umožňuje dočasnou změnu vzhledu buňky předtím, než je skutečně vybrána. Při výběru se zobrazí buňka SelectedBackgroundView . Následující obrázek znázorňuje zvýrazněný stav těsně před výběrem:

This figure shows the highlighted state just before the selection occurs

K implementaci zvýrazňování ItemHighlighted je možné použít metody a ItemUnhighlighted metody UICollectionViewDelegate . Například následující kód použije žluté pozadí ContentView zvýrazněné buňky a bílé pozadí při zrušení zvýraznění, jak je znázorněno na obrázku výše:

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

Zakázání výběru

Ve výchozím nastavení je ve výchozím nastavení UICollectionViewpovolen výběr . Pokud chcete zakázat výběr, přepsat ShouldHighlightItem a vrátit hodnotu false, jak je znázorněno níže:

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

Když je zvýraznění zakázané, proces výběru buňky je také zakázán. Kromě toho existuje také ShouldSelectItem metoda, která řídí výběr přímo, ačkoli pokud ShouldHighlightItem je implementována a vrací false, ShouldSelectItem není volána.

ShouldSelectItem umožňuje zapnout nebo vypnout výběr na základě položky po položce, pokud ShouldHighlightItem není implementována. Umožňuje také zvýraznění bez výběru, pokud ShouldHighlightItem je implementováno a vrací hodnotu true, zatímco ShouldSelectItem vrátí hodnotu false.

Nabídky buněk

Každá buňka v buňce UICollectionView je schopná zobrazit nabídku, která umožňuje volitelnou podporu vyjmutí, kopírování a vložení. Vytvoření nabídky pro úpravy v buňce:

  1. Přepsat ShouldShowMenu a vrátit hodnotu true, pokud by položka měla zobrazit nabídku.
  2. Přepsat CanPerformAction a vrátit true pro každou akci, kterou může položka provést, což bude libovolná operace vyjmutí, kopírování nebo vložení.
  3. Přepište PerformAction , aby se provedla operace úprav, kopírování vložení.

Následující snímek obrazovky ukazuje nabídku, když je buňka dlouho stisknuta:

This screenshot show the menu when a cell is long pressed

Rozložení

UICollectionView podporuje systém rozložení, který umožňuje umístění všech jeho prvků, buněk, doplňkových zobrazení a dekoračních zobrazení, spravovat nezávisle na UICollectionView sobě. Pomocí systému rozložení může aplikace podporovat rozložení, jako je mřížka, která jsme viděli v tomto článku, a také vlastní rozložení.

Základy rozložení

Rozložení v objektu UICollectionView jsou definována ve třídě, která dědí z UICollectionViewLayout. Implementace rozložení je zodpovědná za vytvoření atributů rozložení pro každou položku v objektu UICollectionView. Rozložení můžete vytvořit dvěma způsoby:

  • Použijte předdefinovaný soubor UICollectionViewFlowLayout .
  • Zadejte vlastní rozložení zděděním z UICollectionViewLayout .

Rozložení toku

Třída UICollectionViewFlowLayout poskytuje rozložení založené na řádcích, které je vhodné pro uspořádání obsahu v mřížce buněk, jak jsme viděli.

Použití rozložení toku:

  • Vytvořte instanci UICollectionViewFlowLayout :
var layout = new UICollectionViewFlowLayout ();
  • Předejte instanci konstruktoru UICollectionView :
simpleCollectionViewController = new SimpleCollectionViewController (layout);

To je vše, co je potřeba k rozložení obsahu v mřížce. Při změně UICollectionViewFlowLayout orientace také úchyty odpovídajícím způsobem mění uspořádání obsahu, jak je znázorněno níže:

Example of the orientation changes

Sada oddílů

Pro poskytnutí nějakého místa kolem UIContentView, rozložení mají SectionInset vlastnost typu UIEdgeInsets. Například následující kód poskytuje vyrovnávací paměť o 50 pixelech kolem každé části UIContentView v případě, že je rozložena pomocí UICollectionViewFlowLayout:

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

Výsledkem je mezera kolem oddílu, jak je znázorněno níže:

Spacing around the section as shown here

Podtřídy UICollectionViewFlowLayout

V edici, která se má použít UICollectionViewFlowLayout přímo, může být také podtříděna pro další přizpůsobení rozložení obsahu podél řádku. Můžete to například použít k vytvoření rozložení, které nezabalí buňky do mřížky, ale místo toho vytvoří jeden řádek s vodorovným posunovacím efektem, jak je znázorněno níže:

A single row with a horizontal scrolling effect

K implementaci je potřeba provést podtřídu UICollectionViewFlowLayout :

  • Inicializace všech vlastností rozložení, které se vztahují na samotné rozložení nebo všechny položky v rozložení v konstruktoru.
  • Přepsání ShouldInvalidateLayoutForBoundsChange , vrácení true tak, aby při hranici UICollectionView změn, rozložení buněk bude přepočítáno. V tomto případě se zajistí, že se během posouvání použije kód transformace použité na buňku úplně na střed.
  • Přepsáním TargetContentOffset nastavíte, aby se buňka úplně na střed přichytila ke středu při UICollectionView posouvání.
  • Přepsání LayoutAttributesForElementsInRect pro vrácení pole .UICollectionViewLayoutAttributes Každá UICollectionViewLayoutAttribute obsahuje informace o rozložení konkrétní položky, včetně vlastností, jako je jeho Center , SizeZIndex a Transform3D .

Následující kód ukazuje takovou implementaci:

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

  }
}

Vlastní rozložení

Kromě použití UICollectionViewFlowLayout, rozložení lze také plně přizpůsobit děděním přímo z UICollectionViewLayout.

Klíčové metody přepsání jsou:

  • PrepareLayout – Slouží k provádění počátečních geometrických výpočtů, které budou použity v průběhu procesu rozložení.
  • CollectionViewContentSize – Vrátí velikost oblasti použité k zobrazení obsahu.
  • LayoutAttributesForElementsInRect – Stejně jako u příkladu UICollectionViewFlowLayout zobrazeného dříve se tato metoda používá k poskytování informací UICollectionView o tom, jak jednotlivé položky rozložení. Na rozdíl od možnosti UICollectionViewFlowLayout , při vytváření vlastního rozložení však můžete položky umístit, ale zvolíte.

Stejný obsah se například může zobrazit v kruhovém rozložení, jak je znázorněno níže:

A circular custom layout as shown here

Výkonná věc ohledně rozložení je, že změna z rozložení podobné mřížky na vodorovné posouvání a následně k tomuto kruhovém rozložení vyžaduje, aby se UICollectionView změnila pouze třída rozložení. UICollectionViewV souboru , jeho delegát nebo kód zdroje dat se vůbec nic nemění.

Změny v iOSu 9

V iOSu 9 teď zobrazení kolekce (UICollectionView) podporuje přeuspořádání položek mimo pole přidáním nového výchozího rozpoznávání gest a několika nových podpůrných metod.

Pomocí těchto nových metod můžete snadno implementovat přetažení a změnit pořadí v zobrazení kolekce a mít možnost přizpůsobit vzhled položek během jakékoli fáze procesu změny pořadí.

An example of the reordering process

V tomto článku se podíváme na implementaci změny pořadí přetažení do pořadí v aplikaci Xamarin.iOS a také na některé další změny, které systém iOS 9 provedl v ovládacím prvku zobrazení kolekce:

Změna pořadí položek

Jak jsme uvedli výše, jedním z nejvýznamnějších změn v zobrazení kolekce v iOSu 9 bylo přidání snadné funkce přetažení do pořadí.

V iOSu 9 je nejrychlejší způsob, jak do zobrazení kolekce přidat změny pořadí, použít UICollectionViewController. Kontroler zobrazení kolekce má nyní InstallsStandardGestureForInteractiveMovement vlastnost, která přidává standardní rozpoznávání gest, které podporuje přetahování položek v kolekci. Vzhledem k tomu, že výchozí hodnota je true, musíte implementovat MoveItem pouze metodu UICollectionViewDataSource třídy pro podporu přetažení do pořadí. Příklad:

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

Příklad jednoduchého změny pořadí

Jako rychlý příklad spusťte nový projekt Xamarin.iOS a upravte soubor Main.storyboard . Lze přetáhnout na návrhovou UICollectionViewController plochu:

Adding a UICollectionViewController

Vyberte zobrazení kolekce (může být nejjednodušší to udělat v osnově dokumentu). Na kartě Rozložení oblasti Vlastnosti nastavte následující velikosti, jak je znázorněno na následujícím snímku obrazovky:

  • Velikost buňky: Šířka – 60 | Výška – 60
  • Velikost záhlaví: Šířka – 0 | Výška – 0
  • Velikost zápatí: Šířka – 0 | Výška – 0
  • Minimální mezery: pro buňky – 8 | Pro linky – 8
  • Oddíly Inset: Top – 16 | Dole – 16 | Vlevo – 16 | Vpravo – 16

Set the Collection View sizes

Dále upravte výchozí buňku:

  • Změna barvy pozadí na modrou
  • Přidání popisku, který bude fungovat jako název buňky
  • Nastavení identifikátoru opakovaného použití na buňku

Edit the default Cell

Přidejte omezení pro zarovnání popisku na střed uvnitř buňky při změně velikosti:

V oblasti vlastností CollectionViewCell a nastavte třídu na TextCollectionViewCell:

Set the Class to TextCollectionViewCell

Nastavte opakovaně použitelné zobrazení kolekce naCell:

Set the Collection Reusable View to Cell

Nakonec vyberte popisek a pojmenujte ho TextLabel:

name label TextLabel

TextCollectionViewCell Upravte třídu a přidejte následující vlastnosti:

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

Text Zde je vlastnost popisku vystavena jako název buňky, takže ji lze nastavit z kódu.

Přidejte do projektu novou třídu jazyka C# a zavolejte ji WaterfallCollectionSource. Upravte soubor a udělejte ho takto:

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

Tato třída bude zdrojem dat pro zobrazení kolekce a poskytne informace pro každou buňku v kolekci. Všimněte si, že metoda je implementována tak MoveItem , aby položky v kolekci byly přeuspořádané.

Přidejte do projektu další novou třídu jazyka C# a zavolejte ji WaterfallCollectionDelegate. Upravte tento soubor a udělejte ho takto:

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

Bude to fungovat jako delegát pro naše zobrazení kolekce. Metody byly přepsány, aby zvýrazňovaly buňku, když s ní uživatel pracuje v zobrazení kolekce.

Přidejte do projektu poslední třídu jazyka C# a zavolejte ji WaterfallCollectionView. Upravte tento soubor a udělejte ho takto:

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

Všimněte si, že DataSource a Delegate že jsme vytvořili výše, jsou nastaveny při vytváření zobrazení kolekce z jeho scénáře (nebo souboru .xib ).

Znovu upravte soubor Main.storyboard a vyberte zobrazení kolekce a přepněte na vlastnosti. Nastavte třídu na vlastní WaterfallCollectionView třídu, kterou jsme definovali výše:

Uložte změny provedené v uživatelském rozhraní a spusťte aplikaci. Pokud uživatel vybere položku ze seznamu a přetáhne ji do nového umístění, ostatní položky se automaticky animují, jakmile se přesunou mimo cestu položky. Když uživatel položku zahodí do nového umístění, zůstane na daném místě. Příklad:

An example of dragging an item to a new location

Použití vlastního rozpoznávání gest

V případech, kdy nemůžete použít UICollectionViewController a musíte použít běžnou UIViewController, nebo pokud chcete převzít větší kontrolu nad gestem přetažení, můžete vytvořit vlastní rozpoznávání gest a přidat ho do zobrazení kolekce při načtení zobrazení. Příklad:

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

Zde používáme několik nových metod přidaných do zobrazení kolekce k implementaci a řízení operace přetažení:

  • BeginInteractiveMovementForItem - Označí začátek operace přesunutí.
  • UpdateInteractiveMovementTargetPosition – Odesílá se při aktualizaci umístění položky.
  • EndInteractiveMovement - Označí konec přesunutí položky.
  • CancelInteractiveMovement – Označí uživatele, který zruší operaci přesunutí.

Při spuštění aplikace bude operace přetažení fungovat stejně jako výchozí rozpoznávání gest přetažení, které je součástí zobrazení kolekce.

Vlastní rozložení a změna pořadí

V iOSu 9 bylo přidáno několik nových metod pro práci s přetahováním myší a vlastními rozloženími v zobrazení kolekce. Pokud chcete tuto funkci prozkoumat, přidáme do kolekce vlastní rozložení.

Nejprve přidejte do projektu novou třídu WaterfallCollectionLayout jazyka C#. Upravte ho a udělejte ho takto:

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

Tuto třídu lze použít k poskytnutí vlastního dvousloupce, rozložení vodopádového typu do zobrazení kolekce. Kód používá kódování klíč-hodnota (prostřednictvím WillChangeValue a DidChangeValue metod) k poskytování datové vazby pro naše vypočítané vlastnosti v této třídě.

Dále upravte WaterfallCollectionSource a proveďte následující změny a doplňky:

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

Tím se vytvoří náhodná výška pro každou položku, která se zobrazí v seznamu.

Dále upravte WaterfallCollectionView třídu a přidejte následující pomocnou vlastnost:

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

To vám usnadní přístup ke zdroji dat (a výšce položek) z vlastního rozložení.

Nakonec upravte kontroler zobrazení a přidejte následující kód:

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

Tím se vytvoří instance vlastního rozložení, nastaví událost tak, aby poskytovala velikost každé položky a připojila nové rozložení k zobrazení kolekce.

Pokud znovu spustíme aplikaci Xamarin.iOS, bude zobrazení kolekce vypadat takto:

The collection view will now look like this

Položky se stále dají přeuspořádat stejně jako předtím, ale při vyřazení se změní velikost tak, aby odpovídala jejich novému umístění.

Změny zobrazení kolekce

V následujících částech se podrobně podíváme na změny provedené v každé třídě v zobrazení kolekce v iOSu 9.

UICollectionView

Ve třídě pro iOS 9 byly provedeny UICollectionView následující změny nebo doplňky:

  • BeginInteractiveMovementForItem – Označí začátek operace přetažení.
  • CancelInteractiveMovement – Informuje zobrazení kolekce, že uživatel zrušil operaci přetažení.
  • EndInteractiveMovement – Informuje zobrazení kolekce, že uživatel dokončil operaci přetažení.
  • GetIndexPathsForVisibleSupplementaryElements – Vrátí indexPath záhlaví nebo zápatí v oddílu zobrazení kolekce.
  • GetSupplementaryView – Vrátí dané záhlaví nebo zápatí.
  • GetVisibleSupplementaryViews – Vrátí seznam všech viditelných záhlaví a zápatí.
  • UpdateInteractiveMovementTargetPosition – Informuje zobrazení kolekce, že uživatel přesunul nebo přesouvá položku během operace přetažení.

UICollectionViewController

V iOSu UICollectionViewController 9 byly provedeny následující změny nebo doplňky třídy:

  • InstallsStandardGestureForInteractiveMovement – Pokud true se použije nový rozpoznávání gest, které automaticky podporuje přeuspořádané přetažení do pořadí.
  • CanMoveItem – Informuje zobrazení kolekce, pokud lze danou položku přetáhnout.
  • GetTargetContentOffset – Slouží k získání posunu dané položky zobrazení kolekce.
  • GetTargetIndexPathForMove – Získá indexPath danou položku pro operaci přetažení.
  • MoveItem – Přesune pořadí dané položky v seznamu.

UICollectionViewDataSource

V iOSu UICollectionViewDataSource 9 byly provedeny následující změny nebo doplňky třídy:

  • CanMoveItem – Informuje zobrazení kolekce, pokud lze danou položku přetáhnout.
  • MoveItem – Přesune pořadí dané položky v seznamu.

UICollectionViewDelegate

V iOSu UICollectionViewDelegate 9 byly provedeny následující změny nebo doplňky třídy:

  • GetTargetContentOffset – Slouží k získání posunu dané položky zobrazení kolekce.
  • GetTargetIndexPathForMove – Získá indexPath danou položku pro operaci přetažení.

UICollectionViewFlowLayout

V iOSu UICollectionViewFlowLayout 9 byly provedeny následující změny nebo doplňky třídy:

  • SectionFootersPinToVisibleBounds – Připíná zápatí oddílů k viditelným hranicím zobrazení kolekce.
  • SectionHeadersPinToVisibleBounds – Přilepí záhlaví oddílů k viditelným hranicím zobrazení kolekce.

UICollectionViewLayout

V iOSu UICollectionViewLayout 9 byly provedeny následující změny nebo doplňky třídy:

  • GetInvalidationContextForEndingInteractiveMovementOfItems – Vrátí neplatný kontext na konci operace přetažení, když uživatel buď dokončí přetažení, nebo ho zruší.
  • GetInvalidationContextForInteractivelyMovingItems – Vrátí kontext zneplatnění na začátku operace přetažení.
  • GetLayoutAttributesForInteractivelyMovingItem – Získá atributy rozložení pro danou položku při přetažení položky.
  • GetTargetIndexPathForInteractivelyMovingItem – Vrátí indexPath položku, která je v daném bodě při přetažení položky.

UICollectionViewLayoutAttributes

V iOSu UICollectionViewLayoutAttributes 9 byly provedeny následující změny nebo doplňky třídy:

  • CollisionBoundingPath – Vrátí kolizní cestu dvou položek během operace přetažení.
  • CollisionBoundsType – Vrátí typ kolize (jako a UIDynamicItemCollisionBoundsType), ke které došlo během operace přetažení.

UICollectionViewLayoutInvalidationContext

V iOSu UICollectionViewLayoutInvalidationContext 9 byly provedeny následující změny nebo doplňky třídy:

  • InteractiveMovementTarget – Vrátí cílovou položku operace přetažení.
  • PreviousIndexPathsForInteractivelyMovingItems – Vrátí indexPaths ostatní položky zahrnuté do přetažení, aby se operace přeuspořádaly.
  • TargetIndexPathsForInteractivelyMovingItems – Vrátí indexPaths položky, které budou v důsledku operace přeuspořádané přetažením na pořadí.

UICollectionViewSource

V iOSu UICollectionViewSource 9 byly provedeny následující změny nebo doplňky třídy:

  • CanMoveItem – Informuje zobrazení kolekce, pokud lze danou položku přetáhnout.
  • GetTargetContentOffset – Vrátí posuny položek, které budou přesunuty pomocí operace přetažení do změny pořadí.
  • GetTargetIndexPathForMove – Vrátí indexPath položku, která bude přesunuta během operace přetažení do pořadí.
  • MoveItem – Přesune pořadí dané položky v seznamu.

Shrnutí

Tento článek se zabývá změnami zobrazení kolekcí v iOSu 9 a popisuje, jak je implementovat v Xamarin.iOS. Zahrnoval implementaci jednoduché akce přetažení do pořadí v zobrazení kolekce; pomocí vlastního rozpoznávání gest s přeuspořádaným přetažením do pořadí; a vliv změny pořadí přetažení na vlastní rozložení zobrazení kolekce.