Udostępnij za pośrednictwem


Widoki kolekcji w środowisku Xamarin.iOS

Widoki kolekcji umożliwiają wyświetlanie zawartości przy użyciu dowolnych układów. Umożliwiają one łatwe tworzenie układów przypominających siatkę poza ramkami, a także obsługę układów niestandardowych.

Widoki kolekcji, dostępne w UICollectionView klasie, to nowa koncepcja w systemie iOS 6, która przedstawia wiele elementów na ekranie przy użyciu układów. Wzorce dostarczania danych do elementu UICollectionView do tworzenia elementów i interakcji z tymi elementami są zgodne z tymi samymi wzorcami delegowania i źródła danych, które są często używane w programach dla systemu iOS.

Widoki kolekcji działają jednak z podsystemem układu, który jest niezależny od UICollectionView samego siebie. W związku z tym po prostu udostępnienie innego układu może łatwo zmienić prezentację widoku kolekcji.

System iOS udostępnia klasę układu o nazwie UICollectionViewFlowLayout , która umożliwia tworzenie układów opartych na wierszach, takich jak siatka bez dodatkowej pracy. Ponadto można tworzyć niestandardowe układy, które umożliwiają dowolną prezentację, którą można sobie wyobrazić.

UICollectionView — podstawy

Klasa UICollectionView składa się z trzech różnych elementów:

  • Komórki — widoki oparte na danych dla każdego elementu
  • Widoki dodatkowe — widoki oparte na danych skojarzone z sekcją.
  • Widoki dekoracji — widoki niezwiązane z danymi utworzone przez układ

Cells

Komórki to obiekty reprezentujące pojedynczy element w zestawie danych prezentowanym przez widok kolekcji. Każda komórka jest wystąpieniem UICollectionViewCell klasy składającej się z trzech różnych widoków, jak pokazano na poniższej ilustracji:

Każda komórka składa się z trzech różnych widoków, jak pokazano tutaj

Klasa UICollectionViewCell ma następujące właściwości dla każdego z tych widoków:

  • ContentView — Ten widok zawiera zawartość, którą przedstawia komórka. Jest renderowany w najwyższej kolejności z na ekranie.
  • SelectedBackgroundView — Komórki mają wbudowaną obsługę zaznaczenia. Ten widok służy do wizualnego oznaczania zaznaczonej komórki. Jest renderowany tuż poniżej ContentView , gdy zaznaczono komórkę.
  • BackgroundView — Komórki mogą również wyświetlać tło, które jest prezentowane przez BackgroundView element . Ten widok jest renderowany pod elementem SelectedBackgroundView .

Ustawiając takie ContentView ustawienie, że jest mniejsze niż BackgroundView i SelectedBackgroundView, BackgroundView można użyć do wizualnego ramki zawartości, podczas gdy SelectedBackgroundView zostanie wyświetlona po wybraniu komórki, jak pokazano poniżej:

Różne elementy komórki

Komórki na powyższym zrzucie ekranu są tworzone przez dziedziczenie wartości UICollectionViewCell i ustawienie ContentViewodpowiednio właściwości i BackgroundView , SelectedBackgroundView jak pokazano w poniższym kodzie:

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

Widoki dodatkowe

Widoki dodatkowe to widoki przedstawiające informacje skojarzone z każdą sekcją obiektu UICollectionView. Podobnie jak komórki, widoki dodatkowe są oparte na danych. Gdzie komórki przedstawiają dane elementu ze źródła danych, widoki dodatkowe przedstawiają dane sekcji, takie jak kategorie książki w półce książek lub gatunek muzyki w bibliotece muzycznej.

Na przykład widok dodatkowy może służyć do prezentowania nagłówka dla określonej sekcji, jak pokazano na poniższej ilustracji:

Widok dodatkowy używany do prezentowania nagłówka dla określonej sekcji, jak pokazano tutaj

Aby użyć widoku dodatkowego, należy najpierw zarejestrować go w metodzie ViewDidLoad :

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

Następnie widok musi zostać zwrócony przy użyciu metody GetViewForSupplementaryElement, utworzonej przy użyciu metody DequeueReusableSupplementaryViewi dziedziczonej z UICollectionReusableViewelementu . Poniższy fragment kodu spowoduje wygenerowanie widoku dodatkowego pokazanego na powyższym zrzucie ekranu:

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

Widoki dodatkowe są bardziej ogólne niż tylko nagłówki i stopki. Można je umieścić w dowolnym miejscu w widoku kolekcji i składać się z dowolnych widoków, dzięki czemu ich wygląd jest w pełni dostosowywalny.

Widoki dekoracji

Widoki dekoracji to wyłącznie widoki wizualne, które mogą być wyświetlane w obiekcie UICollectionView. W przeciwieństwie do komórek i widoków dodatkowych, nie są one oparte na danych. Są one zawsze tworzone w podklasie układu, a następnie mogą ulec zmianie w układzie zawartości. Na przykład widok dekoracji może służyć do prezentowania widoku tła, który przewija zawartość w UICollectionViewobiekcie , jak pokazano poniżej:

Widok dekoracji z czerwonym tłem

Poniższy fragment kodu zmienia tło na czerwony w klasie samples CircleLayout :

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

Źródło danych

Podobnie jak w przypadku innych części systemu iOS, takich jak UITableView i MKMapView, UICollectionView pobiera dane ze źródła danych, które jest uwidocznione w środowisku Xamarin.iOS za pośrednictwem UICollectionViewDataSource klasy . Ta klasa jest odpowiedzialna za dostarczanie zawartości do UICollectionView takich elementów jak:

  • Komórki — zwracane z GetCell metody .
  • Widoki dodatkowe — zwracane z GetViewForSupplementaryElement metody .
  • Liczba sekcji — zwracana z NumberOfSections metody. Wartość domyślna to 1, jeśli nie zostanie zaimplementowana.
  • Liczba elementów na sekcję — zwracana z GetItemsCount metody.

UICollectionViewController

Dla wygody UICollectionViewController klasa jest dostępna. Jest to automatycznie konfigurowane jako pełnomocnik, który jest omówiony w następnej sekcji, oraz źródło danych dla jego UICollectionView widoku.

Podobnie jak w przypadku UITableViewklasy UICollectionView , klasa wywoła źródło danych tylko w celu pobrania komórek dla elementów znajdujących się na ekranie. Komórki, które przewijają ekran, są umieszczane w kolejce do ponownego użycia, jak pokazano na poniższej ilustracji:

Komórki, które przewijają ekran, są umieszczane w kolejce do ponownego użycia, jak pokazano poniżej

Ponowne użycie komórek zostało uproszczone za pomocą polecenia UICollectionView i UITableView. Nie musisz już tworzyć komórki bezpośrednio w źródle danych, jeśli nie jest ona dostępna w kolejce ponownego użycia, ponieważ komórki są zarejestrowane w systemie. Jeśli komórka nie jest dostępna podczas tworzenia wywołania w celu anulowania kolejki komórki z kolejki ponownego użycia, system iOS utworzy ją automatycznie na podstawie typu lub nib, który został zarejestrowany. Ta sama technika jest również dostępna dla widoków dodatkowych.

Rozważmy na przykład następujący kod, który rejestruje klasę AnimalCell :

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

Gdy obiekt UICollectionView wymaga komórki, ponieważ jego element znajduje się na ekranie, UICollectionView wywołuje metodę źródła GetCell danych. Podobnie jak w przypadku interfejsu UITableView, ta metoda jest odpowiedzialna za konfigurowanie komórki z danych zapasowych, które w tym przypadku byłyby klasą AnimalCell .

Poniższy kod przedstawia implementację GetCellAnimalCell , która zwraca wystąpienie:

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

Wywołanie metody to polega na DequeReusableCell tym, że komórka zostanie odsunięta od kolejki ponownego użycia lub, jeśli komórka nie jest dostępna w kolejce, utworzona na podstawie typu zarejestrowanego w wywołaniu metody CollectionView.RegisterClassForCell.

W takim przypadku, rejestrując AnimalCell klasę, system iOS utworzy nową AnimalCell wewnętrznie i zwróci ją, gdy zostanie wykonane wywołanie w celu anulowania kolejki komórki, po czym zostanie ona skonfigurowana z obrazem zawartym w klasie zwierząt i zwróconym do wyświetlania UICollectionVieww obiekcie .

Delegat

Klasa UICollectionView używa delegata typu UICollectionViewDelegate do obsługi interakcji z zawartością w obiekcie UICollectionView. Umożliwia to kontrolę nad następującymi elementami:

  • Zaznaczenie komórki — określa, czy zaznaczono komórkę.
  • Wyróżnianie komórek — określa, czy komórka jest obecnie dotykana.
  • Menu komórek — menu wyświetlane dla komórki w odpowiedzi na długi gest naciśnięcia.

Podobnie jak w przypadku źródła danych, parametr UICollectionViewController jest domyślnie skonfigurowany jako pełnomocnik dla elementu UICollectionView.

HighLighting komórek

Po naciśnięciu komórki komórka przechodzi do wyróżnionego stanu i nie jest zaznaczona, dopóki użytkownik nie zniesie palca z komórki. Umożliwia to tymczasową zmianę wyglądu komórki, zanim zostanie ona rzeczywiście wybrana. Po zaznaczeniu zostanie wyświetlona SelectedBackgroundView komórka. Na poniższej ilustracji przedstawiono wyróżniony stan tuż przed dokonaniem zaznaczenia:

Ten rysunek przedstawia wyróżniony stan tuż przed dokonaniem zaznaczenia

Aby zaimplementować wyróżnianie, ItemHighlighted można użyć metod i ItemUnhighlighted .UICollectionViewDelegate Na przykład poniższy kod będzie stosować żółte tło po ContentView wyróżnionej komórce i białe tło, jak pokazano na powyższej ilustracji:

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

Wyłączanie zaznaczenia

Wybór jest domyślnie włączony w pliku UICollectionView. Aby wyłączyć zaznaczenie, przesłoń ShouldHighlightItem i zwróć wartość false, jak pokazano poniżej:

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

W przypadku wyłączenia wyróżniania proces wybierania komórki jest również wyłączony. Ponadto istnieje również metoda, która steruje wyborem ShouldSelectItem bezpośrednio, chociaż jeśli ShouldHighlightItem jest zaimplementowana i zwraca wartość false, ShouldSelectItem nie jest wywoływana.

ShouldSelectItem umożliwia włączenie lub wyłączenie zaznaczenia na podstawie elementu po elemencie, gdy ShouldHighlightItem nie jest implementowane. Umożliwia również wyróżnianie bez zaznaczenia, jeśli ShouldHighlightItem jest zaimplementowane i zwraca wartość true, a funkcja ShouldSelectItem zwraca wartość false.

Menu komórek

Każda komórka w obiekcie UICollectionView może wyświetlać menu, które umożliwia opcjonalne obsługiwanie wycinania, kopiowania i wklejania. Aby utworzyć menu edycji w komórce:

  1. Zastąpij ShouldShowMenu i zwróć wartość true, jeśli element powinien wyświetlić menu.
  2. Zastąpij CanPerformAction i zwróć wartość true dla każdej akcji, którą może wykonać element, który będzie dowolny z operacji wycinania, kopiowania lub wklejania.
  3. Zastąpij PerformAction , aby wykonać edycję, kopię operacji wklejania.

Poniższy zrzut ekranu przedstawia menu po długim naciśnięciu komórki:

Ten zrzut ekranu przedstawia menu po długim naciśnięciu komórki

Układ

UICollectionView obsługuje system układu, który umożliwia pozycjonowanie wszystkich elementów, komórek, widoków dodatkowych i widoków dekoracji, które mają być zarządzane niezależnie od UICollectionView siebie. Korzystając z systemu układów, aplikacja może obsługiwać układy, takie jak siatka, które widzieliśmy w tym artykule, a także udostępniać układy niestandardowe.

Podstawy układu

Układy w obiekcie UICollectionView są definiowane w klasie dziedziczonej z UICollectionViewLayoutklasy . Implementacja układu jest odpowiedzialna za tworzenie atrybutów układu dla każdego elementu w obiekcie UICollectionView. Istnieją dwa sposoby tworzenia układu:

  • Użyj wbudowanego .UICollectionViewFlowLayout
  • Podaj układ niestandardowy, dziedzicząc z UICollectionViewLayout elementu .

Układ przepływu

Klasa UICollectionViewFlowLayout udostępnia układ oparty na linii, który nadaje się do rozmieszczania zawartości w siatce komórek, jak widzieliśmy.

Aby użyć układu przepływu:

  • Utwórz wystąpienie klasy UICollectionViewFlowLayout :
var layout = new UICollectionViewFlowLayout ();
  • Przekaż wystąpienie do konstruktora klasy UICollectionView :
simpleCollectionViewController = new SimpleCollectionViewController (layout);

To wszystko, co jest potrzebne do układu zawartości w siatce. Ponadto po zmianie UICollectionViewFlowLayout orientacji uchwyty odpowiednio rozmieszczają zawartość, jak pokazano poniżej:

Przykład zmian orientacji

Sekcja w zestawie

Aby zapewnić trochę miejsca wokół UIContentViewobiektu , układy mają SectionInset właściwość typu UIEdgeInsets. Na przykład poniższy kod zawiera bufor 50 pikseli wokół każdej sekcji UIContentView obiektu, gdy został ułożony przez element UICollectionViewFlowLayout:

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

Spowoduje to odstępy między sekcją, jak pokazano poniżej:

Odstępy między sekcją, jak pokazano tutaj

Podklasowanie UICollectionViewFlowLayout

W wersji do bezpośredniego używania UICollectionViewFlowLayout można go również podklasować, aby dodatkowo dostosować układ zawartości wzdłuż wiersza. Na przykład można go użyć do utworzenia układu, który nie owija komórek w siatkę, ale zamiast tego tworzy pojedynczy wiersz z efektem przewijania poziomego, jak pokazano poniżej:

Pojedynczy wiersz z efektem przewijania poziomego

Aby zaimplementować to przez podklasę UICollectionViewFlowLayout , wymaga:

  • Inicjowanie wszystkich właściwości układu, które mają zastosowanie do samego układu lub wszystkich elementów w układzie w konstruktorze.
  • ShouldInvalidateLayoutForBoundsChange Zastąpienie wartości , zwracając wartość true, tak aby po zmianie układu UICollectionView komórek układ komórek został ponownie obliczony. Jest to używane w tym przypadku upewnij się, że podczas przewijania zostanie zastosowany kod przekształcenia zastosowanego do komórki najśrodkszej.
  • Przesłonięcie, aby wyśrodkować TargetContentOffset komórkę do środka UICollectionView , gdy przewijanie zatrzymuje się.
  • Zastępowanie LayoutAttributesForElementsInRect w celu zwrócenia tablicy .UICollectionViewLayoutAttributes Każdy UICollectionViewLayoutAttribute zawiera informacje na temat układu określonego elementu, w tym właściwości, takich jak , SizeCenterZIndex i .Transform3D

Poniższy kod przedstawia taką implementację:

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

  }
}

Układ niestandardowy

Oprócz korzystania z UICollectionViewFlowLayoutprogramu układy można również w pełni dostosować, dziedzicząc bezpośrednio z UICollectionViewLayoutklasy .

Kluczowe metody zastąpienia to:

  • PrepareLayout — służy do wykonywania początkowych obliczeń geometrycznych, które będą używane w całym procesie układu.
  • CollectionViewContentSize — zwraca rozmiar obszaru używanego do wyświetlania zawartości.
  • LayoutAttributesForElementsInRect — Podobnie jak w przypadku przedstawionego wcześniej przykładu UICollectionViewFlowLayout, ta metoda służy do przekazywania informacji dotyczących UICollectionView sposobu układu każdego elementu. Jednak w przeciwieństwie do UICollectionViewFlowLayout elementu , podczas tworzenia układu niestandardowego można jednak ustawić elementy.

Na przykład tę samą zawartość można przedstawić w układzie okrągłym, jak pokazano poniżej:

Okrągły układ niestandardowy, jak pokazano tutaj

Zaawansowaną rzeczą w układach jest to, że aby zmienić układ przypominający siatkę, na układ przewijania poziomego, a następnie do tego układu cyklicznego wymaga tylko klasy układu dostarczonej UICollectionView do zmiany. Nic w ogóle nie zmienia się w kodzie delegowanym UICollectionViewlub źródłowym danych.

Zmiany w systemie iOS 9

W systemie iOS 9 widok kolekcji (UICollectionView) obsługuje teraz przeciąganie kolejności elementów poza polem przez dodanie nowego domyślnego rozpoznawania gestów i kilku nowych metod pomocniczych.

Za pomocą tych nowych metod można łatwo zaimplementować przeciąganie w celu zmiany kolejności w widoku kolekcji i możliwość dostosowywania wyglądu elementów podczas dowolnego etapu procesu zmiany kolejności.

Przykład procesu zmiany kolejności

W tym artykule przyjrzymy się implementacji przeciągania do zmiany kolejności w aplikacji platformy Xamarin.iOS, a także niektóre inne zmiany wprowadzone w kontrolce widoku kolekcji w systemie iOS 9:

Zmiana kolejności elementów

Jak wspomniano powyżej, jedną z najważniejszych zmian w widoku kolekcji w systemie iOS 9 było dodanie łatwej funkcji przeciągania do zmiany kolejności z pudełka.

W systemie iOS 9 najszybszym sposobem dodania zmiany kolejności do widoku kolekcji jest użycie elementu UICollectionViewController. Kontroler widoku kolekcji ma InstallsStandardGestureForInteractiveMovement teraz właściwość, która dodaje standardowy aparat rozpoznawania gestów, który obsługuje przeciąganie w celu zmiany kolejności elementów w kolekcji. Ponieważ wartość domyślna to true, należy zaimplementować MoveItem tylko metodę UICollectionViewDataSource klasy w celu obsługi przeciągania do zmiany kolejności. Na przykład:

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

Prosty przykład zmiany kolejności

W ramach szybkiego przykładu rozpocznij nowy projekt platformy Xamarin.iOS i zmodyfikuj plik Main.storyboard . Przeciągnij element UICollectionViewController na powierzchnię projektową:

Dodawanie kontrolki UICollectionViewController

Wybierz widok kolekcji (najłatwiej jest to zrobić w konspekcie dokumentu). Na karcie układu okienka właściwości ustaw następujące rozmiary, jak pokazano na poniższym zrzucie ekranu:

  • Rozmiar komórki: szerokość – 60 | Wysokość – 60
  • Rozmiar nagłówka: szerokość – 0 | Wysokość – 0
  • Rozmiar stopki: szerokość – 0 | Wysokość – 0
  • Minimalna odstępy: dla komórek – 8 | W przypadku linii — 8
  • Zestawy sekcji: wierzchołki – 16 | Dół – 16 | Od lewej do 16 | Prawo – 16

Ustawianie rozmiarów widoku kolekcji

Następnie zmodyfikuj domyślną komórkę:

  • Zmień kolor tła na niebieski
  • Dodaj etykietę, która będzie działać jako tytuł komórki
  • Ustawianie identyfikatora ponownego użycia na komórkę

Edytuj domyślną komórkę

Dodaj ograniczenia, aby zachować wyśrodkowanie etykiety wewnątrz komórki w miarę zmieniania rozmiaru:

W okienku właściwości dla kontrolki CollectionViewCell ustaw klasęna :TextCollectionViewCell

Ustaw klasę na TextCollectionViewCell

Ustaw widok wielokrotnego użytku kolekcji na :Cell

Ustaw widok wielokrotnego użytku kolekcji na komórkę

Na koniec wybierz etykietę i nadaj jej TextLabelnazwę :

etykieta nazwy TextLabel

Edytuj klasę TextCollectionViewCell i dodaj następujące właściwości:

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 Tutaj właściwość etykiety jest uwidoczniona jako tytuł komórki, więc można ją ustawić z kodu.

Dodaj nową klasę języka C# do projektu i wywołaj ją WaterfallCollectionSource. Zmodyfikuj plik i utwórz go w następujący sposób:

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

Ta klasa będzie źródłem danych dla naszego widoku kolekcji i będzie dostarczać informacje dla każdej komórki w kolekcji. Zwróć uwagę, że MoveItem metoda jest implementowana, aby umożliwić przeciąganie elementów w kolekcji.

Dodaj kolejną nową klasę języka C# do projektu i wywołaj ją WaterfallCollectionDelegate. Zmodyfikuj ten plik i zmień jego wygląd na następujący:

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

Będzie to pełnić rolę delegata dla naszego widoku kolekcji. Metody zostały zastąpione, aby wyróżnić komórkę, gdy użytkownik wchodzi z nią w interakcję w widoku kolekcji.

Dodaj ostatnią klasę języka C# do projektu i wywołaj ją WaterfallCollectionView. Zmodyfikuj ten plik i zmień jego wygląd na następujący:

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

Zwróć uwagę, że DataSource utworzone Delegate powyżej ustawienia są ustawiane podczas konstruowania widoku kolekcji z jego scenorysu (lub pliku xib ).

Ponownie edytuj plik Main.storyboard i wybierz widok kolekcji i przejdź do właściwości. Ustaw klasę na klasę niestandardową WaterfallCollectionView zdefiniowaną powyżej:

Zapisz zmiany wprowadzone w interfejsie użytkownika i uruchom aplikację. Jeśli użytkownik wybierze element z listy i przeciągnie go do nowej lokalizacji, inne elementy będą animowane automatycznie, gdy wyjdą z drogi elementu. Gdy użytkownik usunie element w nowej lokalizacji, pozostanie w tej lokalizacji. Na przykład:

Przykład przeciągania elementu do nowej lokalizacji

Używanie niestandardowego rozpoznawania gestów

W przypadkach, gdy nie można użyć elementu UICollectionViewController i musi używać zwykłego UIViewControllerelementu lub jeśli chcesz przejąć większą kontrolę nad gestem przeciągania i upuszczania, możesz utworzyć własny niestandardowy aparat rozpoznawania gestów i dodać go do widoku kolekcji podczas ładowania widoku. Na przykład:

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

W tym miejscu używamy kilku nowych metod dodanych do widoku kolekcji w celu zaimplementowania i sterowania operacją przeciągania:

  • BeginInteractiveMovementForItem — Oznacza początek operacji przenoszenia.
  • UpdateInteractiveMovementTargetPosition — jest wysyłany w miarę aktualizowania lokalizacji elementu.
  • EndInteractiveMovement — Oznacza koniec przenoszenia elementu.
  • CancelInteractiveMovement — Oznacza użytkownika anulując operację przenoszenia.

Po uruchomieniu aplikacji operacja przeciągania będzie działać dokładnie tak jak domyślny aparat rozpoznawania gestów przeciągania, który jest dostarczany z widokiem kolekcji.

Układy niestandardowe i zmiana kolejności

W systemie iOS 9 dodano kilka nowych metod do pracy z układami przeciągania do zmiany kolejności i niestandardowymi w widoku kolekcji. Aby zapoznać się z tą funkcją, dodajmy układ niestandardowy do kolekcji.

Najpierw dodaj nową klasę języka C# o nazwie WaterfallCollectionLayout do projektu. Zmodyfikuj go i zmień jego wygląd na następujący:

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

Ta klasa może służyć do udostępniania niestandardowej dwukolumny układu typu kaskadowego do widoku kolekcji. Kod używa kodowania klucz-wartość (za pośrednictwem WillChangeValue metod i DidChangeValue ), aby zapewnić powiązanie danych dla naszych obliczonych właściwości w tej klasie.

Następnie zmodyfikuj i WaterfallCollectionSource wprowadź następujące zmiany i dodatki:

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

Spowoduje to utworzenie losowej wysokości dla każdego z elementów, które będą wyświetlane na liście.

Następnie zmodyfikuj klasę WaterfallCollectionView i dodaj następującą właściwość pomocnika:

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

Ułatwi to uzyskanie dostępu do naszego źródła danych (i wysokości elementów) z układu niestandardowego.

Na koniec zmodyfikuj kontroler widoku i dodaj następujący kod:

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

Spowoduje to utworzenie wystąpienia naszego niestandardowego układu, ustawienie zdarzenia w celu udostępnienia rozmiaru każdego elementu i dołączenie nowego układu do widoku kolekcji.

Jeśli ponownie uruchomimy aplikację Xamarin.iOS, widok kolekcji będzie wyglądać podobnie do następującego:

Widok kolekcji będzie teraz wyglądać następująco

Nadal możemy przeciągać elementy do zmiany kolejności, tak jak poprzednio, ale elementy zmienią rozmiar, aby dopasować ich nową lokalizację, gdy zostaną porzucone.

Zmiany widoku kolekcji

W poniższych sekcjach przyjrzymy się szczegółowym zmianom wprowadzonych w każdej klasie w widoku kolekcji przez system iOS 9.

UICollectionView

W klasie dla systemu iOS 9 wprowadzono UICollectionView następujące zmiany lub dodatki:

  • BeginInteractiveMovementForItem — oznacza początek operacji przeciągania.
  • CancelInteractiveMovement — informuje widok kolekcji, że użytkownik anulował operację przeciągania.
  • EndInteractiveMovement — informuje widok kolekcji, że użytkownik zakończył operację przeciągania.
  • GetIndexPathsForVisibleSupplementaryElements — Zwraca indexPath nagłówek lub stopkę w sekcji widoku kolekcji.
  • GetSupplementaryView — zwraca dany nagłówek lub stopkę.
  • GetVisibleSupplementaryViews — Zwraca listę wszystkich widocznych nagłówków i stopek.
  • UpdateInteractiveMovementTargetPosition — informuje widok kolekcji, że użytkownik przeniósł lub przenosi element podczas operacji przeciągania.

UICollectionViewController

W klasie w systemie iOS 9 wprowadzono UICollectionViewController następujące zmiany lub dodatki:

  • InstallsStandardGestureForInteractiveMovement — Jeśli true zostanie użyty nowy aparat rozpoznawania gestów, który automatycznie obsługuje przeciąganie do zmiany kolejności.
  • CanMoveItem — informuje widok kolekcji, jeśli dany element może zostać przeciągnięty ponownie.
  • GetTargetContentOffset — służy do pobierania przesunięcia danego elementu widoku kolekcji.
  • GetTargetIndexPathForMove — Pobiera indexPath element danego elementu dla operacji przeciągania.
  • MoveItem — przenosi kolejność danego elementu na liście.

UICollectionViewDataSource

W klasie w systemie iOS 9 wprowadzono UICollectionViewDataSource następujące zmiany lub dodatki:

  • CanMoveItem — informuje widok kolekcji, jeśli dany element może zostać przeciągnięty ponownie.
  • MoveItem — przenosi kolejność danego elementu na liście.

UICollectionViewDelegate

W klasie w systemie iOS 9 wprowadzono UICollectionViewDelegate następujące zmiany lub dodatki:

  • GetTargetContentOffset — służy do pobierania przesunięcia danego elementu widoku kolekcji.
  • GetTargetIndexPathForMove — Pobiera indexPath element danego elementu dla operacji przeciągania.

UICollectionViewFlowLayout

W klasie w systemie iOS 9 wprowadzono UICollectionViewFlowLayout następujące zmiany lub dodatki:

  • SectionFootersPinToVisibleBounds — przykleja stopkę sekcji do widocznych granic widoku kolekcji.
  • SectionHeadersPinToVisibleBounds — przykleja nagłówki sekcji do widocznych granic widoku kolekcji.

UICollectionViewLayout

W klasie w systemie iOS 9 wprowadzono UICollectionViewLayout następujące zmiany lub dodatki:

  • GetInvalidationContextForEndingInteractiveMovementOfItems — Zwraca kontekst unieważnienia na końcu operacji przeciągania, gdy użytkownik zakończy przeciąganie lub anuluje go.
  • GetInvalidationContextForInteractivelyMovingItems — Zwraca kontekst unieważniania na początku operacji przeciągania.
  • GetLayoutAttributesForInteractivelyMovingItem — Pobiera atrybuty układu dla danego elementu podczas przeciągania elementu.
  • GetTargetIndexPathForInteractivelyMovingItem — zwraca indexPath element, który znajduje się w danym punkcie podczas przeciągania elementu.

UICollectionViewLayoutAttributes

W klasie w systemie iOS 9 wprowadzono UICollectionViewLayoutAttributes następujące zmiany lub dodatki:

  • CollisionBoundingPath — Zwraca ścieżkę kolizji dwóch elementów podczas operacji przeciągania.
  • CollisionBoundsType — Zwraca typ kolizji (jako UIDynamicItemCollisionBoundsType), który wystąpił podczas operacji przeciągania.

UICollectionViewLayoutInvalidationContext

W klasie w systemie iOS 9 wprowadzono UICollectionViewLayoutInvalidationContext następujące zmiany lub dodatki:

  • InteractiveMovementTarget — Zwraca element docelowy operacji przeciągania.
  • PreviousIndexPathsForInteractivelyMovingItems — Zwraca indexPaths inne elementy zaangażowane w operację przeciągania w celu zmiany kolejności.
  • TargetIndexPathsForInteractivelyMovingItems — Zwraca indexPaths elementy, które zostaną ponownie uporządkowane w wyniku operacji przeciągania do zmiany kolejności.

UICollectionViewSource

W klasie w systemie iOS 9 wprowadzono UICollectionViewSource następujące zmiany lub dodatki:

  • CanMoveItem — informuje widok kolekcji, jeśli dany element może zostać przeciągnięty ponownie.
  • GetTargetContentOffset — Zwraca przesunięcia elementów, które zostaną przeniesione za pomocą operacji przeciągania do zmiany kolejności.
  • GetTargetIndexPathForMove — Zwraca indexPath element, który zostanie przeniesiony podczas operacji przeciągania do zmiany kolejności.
  • MoveItem — przenosi kolejność danego elementu na liście.

Podsumowanie

W tym artykule omówiono zmiany w widokach kolekcji w systemie iOS 9 i opisano sposób ich implementowania w środowisku Xamarin.iOS. Opisano w nim implementację prostej akcji przeciągania do zmiany kolejności w widoku kolekcji; przy użyciu niestandardowego rozpoznawania gestów z przeciągnięciem do zmiany kolejności; i sposób, w jaki przeciąganie do zmiany kolejności wpływa na niestandardowy układ widoku kolekcji.