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:
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żejContentView
, gdy zaznaczono komórkę.BackgroundView
— Komórki mogą również wyświetlać tło, które jest prezentowane przezBackgroundView
element . Ten widok jest renderowany pod elementemSelectedBackgroundView
.
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:
Komórki na powyższym zrzucie ekranu są tworzone przez dziedziczenie wartości UICollectionViewCell
i ustawienie ContentView
odpowiednio 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:
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 DequeueReusableSupplementaryView
i dziedziczonej z UICollectionReusableView
elementu . 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 UICollectionView
obiekcie , jak pokazano poniżej:
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 UITableView
klasy 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:
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ę GetCell
AnimalCell
, 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 UICollectionView
w 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:
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:
- Zastąpij
ShouldShowMenu
i zwróć wartość true, jeśli element powinien wyświetlić menu. - 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. - Zastąpij
PerformAction
, aby wykonać edycję, kopię operacji wklejania.
Poniższy 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 UICollectionViewLayout
klasy . 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:
Sekcja w zestawie
Aby zapewnić trochę miejsca wokół UIContentView
obiektu , 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:
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:
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ładuUICollectionView
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 środkaUICollectionView
, gdy przewijanie zatrzymuje się. - Zastępowanie
LayoutAttributesForElementsInRect
w celu zwrócenia tablicy .UICollectionViewLayoutAttributes
KażdyUICollectionViewLayoutAttribute
zawiera informacje na temat układu określonego elementu, w tym właściwości, takich jak ,Size
Center
ZIndex
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 UICollectionViewFlowLayout
programu układy można również w pełni dostosować, dziedzicząc bezpośrednio z UICollectionViewLayout
klasy .
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ącychUICollectionView
sposobu układu każdego elementu. Jednak w przeciwieństwie doUICollectionViewFlowLayout
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:
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 UICollectionView
lub ź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.
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ą:
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
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ę
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 widok wielokrotnego użytku kolekcji na :Cell
Na koniec wybierz etykietę i nadaj jej TextLabel
nazwę :
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:
Używanie niestandardowego rozpoznawania gestów
W przypadkach, gdy nie można użyć elementu UICollectionViewController
i musi używać zwykłego UIViewController
elementu 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:
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
— ZwracaindexPath
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ślitrue
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
— PobieraindexPath
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
— PobieraindexPath
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
— zwracaindexPath
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 (jakoUIDynamicItemCollisionBoundsType
), 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
— ZwracaindexPaths
inne elementy zaangażowane w operację przeciągania w celu zmiany kolejności.TargetIndexPathsForInteractivelyMovingItems
— ZwracaindexPaths
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
— ZwracaindexPath
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.