Viste della raccolta in Xamarin.iOS
Le visualizzazioni raccolta consentono di visualizzare il contenuto usando layout arbitrari. Consentono di creare facilmente layout simili a griglia predefiniti, supportando anche layout personalizzati.
Le visualizzazioni della raccolta, disponibili nella UICollectionView
classe , sono un nuovo concetto in iOS 6 che introduce la presentazione di più elementi sullo schermo usando layout. I modelli per fornire dati a un UICollectionView
oggetto per creare elementi e interagire con tali elementi seguono gli stessi modelli di delega e origine dati comunemente usati nello sviluppo iOS.
Tuttavia, le visualizzazioni della raccolta funzionano con un sottosistema di layout indipendente dall'oggetto UICollectionView
stesso. Pertanto, semplicemente fornendo un layout diverso può facilmente modificare la presentazione di una visualizzazione raccolta.
iOS fornisce una classe di layout denominata UICollectionViewFlowLayout
che consente la creazione di layout basati su linea, ad esempio una griglia senza lavoro aggiuntivo. Inoltre, è possibile creare layout personalizzati che consentano qualsiasi presentazione che si possa immaginare.
Nozioni di base su UICollectionView
La UICollectionView
classe è costituita da tre elementi diversi:
- Celle : viste guidate dai dati per ogni elemento
- Visualizzazioni supplementari: viste guidate dai dati associate a una sezione.
- Visualizzazioni di decorazione: viste non guidate dai dati create da un layout
Cells
Le celle sono oggetti che rappresentano un singolo elemento nel set di dati presentato dalla visualizzazione raccolta. Ogni cella è un'istanza della UICollectionViewCell
classe , composta da tre visualizzazioni diverse, come illustrato nella figura seguente:
La UICollectionViewCell
classe ha le proprietà seguenti per ognuna di queste visualizzazioni:
ContentView
: questa visualizzazione contiene il contenuto presentato dalla cella. Viene eseguito il rendering nell'ordine z più alto sullo schermo.SelectedBackgroundView
- Le celle hanno il supporto predefinito per la selezione. Questa visualizzazione viene utilizzata per indicare visivamente che è selezionata una cella. Viene eseguito il rendering appena sotto l'oggettoContentView
quando viene selezionata una cella.BackgroundView
– Le celle possono anche visualizzare uno sfondo, presentato daBackgroundView
. Il rendering di questa vista viene eseguito sotto l'oggettoSelectedBackgroundView
.
Impostando in ContentView
modo che sia minore di BackgroundView
e SelectedBackgroundView
, può BackgroundView
essere usato per incorniciare visivamente il contenuto, mentre SelectedBackgroundView
verrà visualizzato quando viene selezionata una cella, come illustrato di seguito:
Le celle nello screenshot precedente vengono create ereditando da UICollectionViewCell
e impostando rispettivamente le ContentView
proprietà , SelectedBackgroundView
e BackgroundView
, come illustrato nel codice seguente:
public class AnimalCell : UICollectionViewCell
{
UIImageView imageView;
[Export ("initWithFrame:")]
public AnimalCell (CGRect frame) : base (frame)
{
BackgroundView = new UIView{BackgroundColor = UIColor.Orange};
SelectedBackgroundView = new UIView{BackgroundColor = UIColor.Green};
ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
ContentView.Layer.BorderWidth = 2.0f;
ContentView.BackgroundColor = UIColor.White;
ContentView.Transform = CGAffineTransform.MakeScale (0.8f, 0.8f);
imageView = new UIImageView (UIImage.FromBundle ("placeholder.png"));
imageView.Center = ContentView.Center;
imageView.Transform = CGAffineTransform.MakeScale (0.7f, 0.7f);
ContentView.AddSubview (imageView);
}
public UIImage Image {
set {
imageView.Image = value;
}
}
}
Visualizzazioni supplementari
Le visualizzazioni supplementari sono viste che presentano informazioni associate a ogni sezione di un oggetto UICollectionView
. Come le celle, le visualizzazioni supplementari sono guidate dai dati. Dove Cells presenta i dati dell'elemento da un'origine dati, Le visualizzazioni supplementari presentano i dati della sezione, ad esempio le categorie di libri in una libreria o il genere di musica in una raccolta musicale.
Ad esempio, è possibile usare una visualizzazione supplementare per presentare un'intestazione per una sezione specifica, come illustrato nella figura seguente:
Per usare una visualizzazione supplementare, prima di tutto deve essere registrata nel ViewDidLoad
metodo :
CollectionView.RegisterClassForSupplementaryView (typeof(Header), UICollectionElementKindSection.Header, headerId);
La vista deve quindi essere restituita usando , creata tramite GetViewForSupplementaryElement
DequeueReusableSupplementaryView
e eredita da UICollectionReusableView
. Il frammento di codice seguente produrrà il controllo SupplementaryView illustrato nello screenshot precedente:
public override UICollectionReusableView GetViewForSupplementaryElement (UICollectionView collectionView, NSString elementKind, NSIndexPath indexPath)
{
var headerView = (Header)collectionView.DequeueReusableSupplementaryView (elementKind, headerId, indexPath);
headerView.Text = "Supplementary View";
return headerView;
}
Le visualizzazioni supplementari sono più generiche di intestazioni e piè di pagina. Possono essere posizionati ovunque nella visualizzazione raccolta e possono essere costituiti da qualsiasi visualizzazione, rendendo il loro aspetto completamente personalizzabile.
Viste di decorazione
Le visualizzazioni delle decorazioni sono viste puramente visive che possono essere visualizzate in un oggetto UICollectionView
. A differenza delle celle e delle visualizzazioni supplementari, non sono guidate dai dati. Vengono sempre creati all'interno della sottoclasse di un layout e successivamente possono cambiare come layout del contenuto. Ad esempio, una visualizzazione decorazione può essere usata per presentare una visualizzazione di sfondo che scorre con il contenuto in UICollectionView
, come illustrato di seguito:
Il frammento di codice seguente modifica lo sfondo in rosso nella classe samples CircleLayout
:
public class MyDecorationView : UICollectionReusableView
{
[Export ("initWithFrame:")]
public MyDecorationView (CGRect frame) : base (frame)
{
BackgroundColor = UIColor.Red;
}
}
Origine dati
Come per altre parti di iOS, ad esempio UITableView
e MKMapView
, UICollectionView
ottiene i dati da un'origine dati, esposta in Xamarin.iOS tramite la UICollectionViewDataSource
classe . Questa classe è responsabile della fornitura di contenuto a UICollectionView
, ad esempio:
- Celle : restituite dal
GetCell
metodo . - Visualizzazioni supplementari: restituite dal
GetViewForSupplementaryElement
metodo . - Numero di sezioni : restituite dal
NumberOfSections
metodo . Il valore predefinito è 1 se non è implementato. - Numero di elementi per sezione : restituiti dal
GetItemsCount
metodo .
UICollectionViewController
Per praticità, la UICollectionViewController
classe è disponibile. Viene configurato automaticamente come delegato, descritto nella sezione successiva, e l'origine dati per la relativa UICollectionView
vista.
Come con UITableView
, la classe chiamerà solo l'origine UICollectionView
dati per ottenere celle per gli elementi presenti sullo schermo.
Le celle che scorrono dallo schermo vengono inserite in una coda per il riutilizzo, come illustrato nell'immagine seguente:
Il riutilizzo delle celle è stato semplificato con UICollectionView
e UITableView
. Non è più necessario creare una cella direttamente nell'origine dati se non ne è disponibile una nella coda di riutilizzo, perché le celle vengono registrate nel sistema. Se una cella non è disponibile quando si effettua la chiamata a de-accodare la cella dalla coda di riutilizzo, iOS lo creerà automaticamente in base al tipo o al nib registrato.
La stessa tecnica è disponibile anche per le visualizzazioni supplementari.
Si consideri ad esempio il codice seguente che registra la AnimalCell
classe :
static NSString animalCellId = new NSString ("AnimalCell");
CollectionView.RegisterClassForCell (typeof(AnimalCell), animalCellId);
Quando è UICollectionView
necessaria una cella perché l'elemento è sullo schermo, chiama il UICollectionView
metodo dell'origine GetCell
dati. Analogamente a come funziona con UITableView, questo metodo è responsabile della configurazione di una cella dai dati di supporto, che in questo caso sarebbe una AnimalCell
classe.
Il codice seguente illustra un'implementazione di GetCell
che restituisce un'istanza AnimalCell
di :
public override UICollectionViewCell GetCell (UICollectionView collectionView, Foundation.NSIndexPath indexPath)
{
var animalCell = (AnimalCell)collectionView.DequeueReusableCell (animalCellId, indexPath);
var animal = animals [indexPath.Row];
animalCell.Image = animal.Image;
return animalCell;
}
La chiamata a DequeReusableCell
è la posizione in cui la cella verrà de codata dalla coda di riutilizzo o, se una cella non è disponibile nella coda, creata in base al tipo registrato nella chiamata a CollectionView.RegisterClassForCell
.
In questo caso, registrando la AnimalCell
classe , iOS creerà un nuovo AnimalCell
elemento internamente e lo restituirà quando viene eseguita una chiamata alla de-coda di una cella, dopo la quale è configurato con l'immagine contenuta nella classe animale e restituita per la visualizzazione all'oggetto UICollectionView
.
Delega
La UICollectionView
classe usa un delegato di tipo UICollectionViewDelegate
per supportare l'interazione con il contenuto in UICollectionView
. Ciò consente il controllo di:
- Selezione cella: determina se è selezionata una cella.
- Evidenziazione cella: determina se una cella è attualmente toccato.
- Menu cella: menu visualizzato per una cella in risposta a un movimento di pressione prolungata.
Come per l'origine dati, per UICollectionViewController
impostazione predefinita è configurato come delegato per .UICollectionView
HighLighting delle celle
Quando si preme una cella, la cella passa a uno stato evidenziato e non viene selezionata finché l'utente non solleva il dito dalla cella. Ciò consente una modifica temporanea nell'aspetto della cella prima che venga effettivamente selezionata. Dopo la selezione, viene visualizzata la cella SelectedBackgroundView
. La figura seguente mostra lo stato evidenziato poco prima che si verifichi la selezione:
Per implementare l'evidenziazione, è possibile usare i ItemHighlighted
metodi e ItemUnhighlighted
di UICollectionViewDelegate
. Ad esempio, il codice seguente applicherà uno sfondo giallo di ContentView
quando la cella è evidenziata e uno sfondo bianco quando non evidenziato, come illustrato nell'immagine precedente:
public override void ItemHighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem(indexPath);
cell.ContentView.BackgroundColor = UIColor.Yellow;
}
public override void ItemUnhighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.CellForItem(indexPath);
cell.ContentView.BackgroundColor = UIColor.White;
}
Disabilitazione della selezione
La selezione è abilitata per impostazione predefinita in UICollectionView
. Per disabilitare la selezione, eseguire l'override ShouldHighlightItem
e restituire false, come illustrato di seguito:
public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath)
{
return false;
}
Quando l'evidenziazione è disabilitata, anche il processo di selezione di una cella è disabilitato. Inoltre, esiste anche un ShouldSelectItem
metodo che controlla direttamente la selezione, anche se ShouldHighlightItem
viene implementato e restituisce false, ShouldSelectItem
non viene chiamato.
ShouldSelectItem
consente di attivare o disattivare la selezione in base all'elemento, quando ShouldHighlightItem
non viene implementata. Consente inoltre l'evidenziazione senza selezione, se ShouldHighlightItem
viene implementata e restituisce true, mentre ShouldSelectItem
restituisce false.
Menu cella
Ogni cella di un oggetto UICollectionView
è in grado di visualizzare un menu che consente di tagliare, copiare e incollare in modo facoltativo. Per creare un menu di modifica in una cella:
- Eseguire l'override e restituire true se l'elemento deve visualizzare
ShouldShowMenu
un menu. - Eseguire l'override
CanPerformAction
e restituire true per ogni azione che l'elemento può eseguire, che sarà una delle operazioni taglia, copia o incolla. - Eseguire l'override
PerformAction
per eseguire l'operazione di modifica, copia dell'operazione incolla.
Lo screenshot seguente mostra il menu quando viene premuta una cella:
Layout
UICollectionView
supporta un sistema di layout che consente di posizionare tutti i relativi elementi, Celle, Visualizzazioni supplementari e Viste di decorazione, di essere gestiti indipendentemente da UICollectionView
se stesso.
Usando il sistema di layout, un'applicazione può supportare layout come la griglia illustrata in questo articolo, oltre a fornire layout personalizzati.
Nozioni di base sul layout
I layout in un UICollectionView
oggetto sono definiti in una classe che eredita da UICollectionViewLayout
. L'implementazione del layout è responsabile della creazione degli attributi di layout per ogni elemento in UICollectionView
. Esistono due modi per creare un layout:
- Usare l'oggetto
UICollectionViewFlowLayout
predefinito . - Fornire un layout personalizzato ereditando da
UICollectionViewLayout
.
Layout di flusso
La UICollectionViewFlowLayout
classe fornisce un layout basato su linea adatto per disporre il contenuto in una griglia di celle come si è visto.
Per usare un layout di flusso:
- Creare un'istanza di
UICollectionViewFlowLayout
:
var layout = new UICollectionViewFlowLayout ();
- Passare l'istanza al costruttore di
UICollectionView
:
simpleCollectionViewController = new SimpleCollectionViewController (layout);
Questo è tutto ciò che è necessario per il layout del contenuto in una griglia. Inoltre, quando l'orientamento cambia, gestisce la UICollectionViewFlowLayout
riorganizzazione del contenuto in modo appropriato, come illustrato di seguito:
Set di sezioni
Per fornire spazio intorno a , i UIContentView
layout hanno una SectionInset
proprietà di tipo UIEdgeInsets
. Ad esempio, il codice seguente fornisce un buffer di 50 pixel intorno a ogni sezione di UIContentView
quando disposto da un oggetto UICollectionViewFlowLayout
:
var layout = new UICollectionViewFlowLayout ();
layout.SectionInset = new UIEdgeInsets (50,50,50,50);
Ciò comporta la spaziatura intorno alla sezione, come illustrato di seguito:
Creazione di sottoclassi UICollectionViewFlowLayout
Nell'edizione da usare UICollectionViewFlowLayout
direttamente, può anche essere sottoclassata per personalizzare ulteriormente il layout del contenuto lungo una linea. Ad esempio, questo può essere usato per creare un layout che non esegue il wrapping delle celle in una griglia, ma crea invece una singola riga con un effetto di scorrimento orizzontale, come illustrato di seguito:
Per implementare questa funzionalità tramite la sottoclasse UICollectionViewFlowLayout
, è necessario:
- Inizializzazione di tutte le proprietà di layout applicabili al layout stesso o a tutti gli elementi del layout nel costruttore.
- Override di , restituendo
ShouldInvalidateLayoutForBoundsChange
true in modo che, quando i limiti delleUICollectionView
modifiche, il layout delle celle verrà ricalcolato. Questa operazione viene usata in questo caso per assicurarsi che il codice per la trasformazione applicato alla cella al centro venga applicato durante lo scorrimento. - Override di
TargetContentOffset
per fare in modo che lo snap della cella al centro dell'oggetto venga interrotto durante loUICollectionView
scorrimento. - Override di
LayoutAttributesForElementsInRect
per restituire una matrice diUICollectionViewLayoutAttributes
. OgnunoUICollectionViewLayoutAttribute
contiene informazioni su come creare il layout dell'elemento specifico, incluse le proprietà, adCenter
esempio ,ZIndex
Size
eTransform3D
.
Il codice seguente illustra un'implementazione di questo tipo:
using System;
using CoreGraphics;
using Foundation;
using UIKit;
using CoreGraphics;
using CoreAnimation;
namespace SimpleCollectionView
{
public class LineLayout : UICollectionViewFlowLayout
{
public const float ITEM_SIZE = 200.0f;
public const int ACTIVE_DISTANCE = 200;
public const float ZOOM_FACTOR = 0.3f;
public LineLayout ()
{
ItemSize = new CGSize (ITEM_SIZE, ITEM_SIZE);
ScrollDirection = UICollectionViewScrollDirection.Horizontal;
SectionInset = new UIEdgeInsets (400,0,400,0);
MinimumLineSpacing = 50.0f;
}
public override bool ShouldInvalidateLayoutForBoundsChange (CGRect newBounds)
{
return true;
}
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (CGRect rect)
{
var array = base.LayoutAttributesForElementsInRect (rect);
var visibleRect = new CGRect (CollectionView.ContentOffset, CollectionView.Bounds.Size);
foreach (var attributes in array) {
if (attributes.Frame.IntersectsWith (rect)) {
float distance = (float)(visibleRect.GetMidX () - attributes.Center.X);
float normalizedDistance = distance / ACTIVE_DISTANCE;
if (Math.Abs (distance) < ACTIVE_DISTANCE) {
float zoom = 1 + ZOOM_FACTOR * (1 - Math.Abs (normalizedDistance));
attributes.Transform3D = CATransform3D.MakeScale (zoom, zoom, 1.0f);
attributes.ZIndex = 1;
}
}
}
return array;
}
public override CGPoint TargetContentOffset (CGPoint proposedContentOffset, CGPoint scrollingVelocity)
{
float offSetAdjustment = float.MaxValue;
float horizontalCenter = (float)(proposedContentOffset.X + (this.CollectionView.Bounds.Size.Width / 2.0));
CGRect targetRect = new CGRect (proposedContentOffset.X, 0.0f, this.CollectionView.Bounds.Size.Width, this.CollectionView.Bounds.Size.Height);
var array = base.LayoutAttributesForElementsInRect (targetRect);
foreach (var layoutAttributes in array) {
float itemHorizontalCenter = (float)layoutAttributes.Center.X;
if (Math.Abs (itemHorizontalCenter - horizontalCenter) < Math.Abs (offSetAdjustment)) {
offSetAdjustment = itemHorizontalCenter - horizontalCenter;
}
}
return new CGPoint (proposedContentOffset.X + offSetAdjustment, proposedContentOffset.Y);
}
}
}
Layout personalizzato
Oltre a usare UICollectionViewFlowLayout
, i layout possono anche essere completamente personalizzati ereditando direttamente da UICollectionViewLayout
.
I metodi chiave di cui eseguire l'override sono:
PrepareLayout
– Usato per eseguire calcoli geometrici iniziali che verranno usati durante il processo di layout.CollectionViewContentSize
: restituisce le dimensioni dell'area utilizzata per visualizzare il contenuto.LayoutAttributesForElementsInRect
: come nell'esempio UICollectionViewFlowLayout illustrato in precedenza, questo metodo viene usato per fornire informazioni su come eseguire ilUICollectionView
layout di ogni elemento. Tuttavia, a differenza diUICollectionViewFlowLayout
, quando si crea un layout personalizzato, è possibile posizionare gli elementi, ma si sceglie.
Ad esempio, lo stesso contenuto può essere presentato in un layout circolare, come illustrato di seguito:
La cosa importante dei layout è che per passare dal layout simile alla griglia, a un layout di scorrimento orizzontale e successivamente a questo layout circolare richiede solo la classe di layout fornita per essere UICollectionView
modificata. Nulla in UICollectionView
, il relativo delegato o il codice sorgente dati cambia affatto.
Modifiche in iOS 9
In iOS 9, la visualizzazione raccolta (UICollectionView
) supporta ora il riordino di trascinamento degli elementi predefiniti aggiungendo un nuovo riconoscimento movimento predefinito e diversi nuovi metodi di supporto.
Usando questi nuovi metodi, è possibile implementare facilmente il trascinamento per riordinare nella visualizzazione raccolta e avere la possibilità di personalizzare l'aspetto degli elementi durante qualsiasi fase del processo di riordinamento.
In questo articolo si esaminerà l'implementazione dell'implementazione del trascinamento del riordinamento in un'applicazione Xamarin.iOS e di alcune delle altre modifiche apportate da iOS 9 al controllo visualizzazione raccolta:
Riordinamento degli elementi
Come indicato in precedenza, una delle modifiche più significative alla visualizzazione raccolta in iOS 9 è stata l'aggiunta di una semplice funzionalità di ordinamento da trascinare a riordinare.
In iOS 9, il modo più rapido per aggiungere il riordinamento a una visualizzazione raccolta consiste nell'usare un oggetto UICollectionViewController
.
Il controller di visualizzazione raccolta dispone ora di una InstallsStandardGestureForInteractiveMovement
proprietà che aggiunge un riconoscimento movimento standard che supporta il trascinamento per riordinare gli elementi nella raccolta.
Poiché il valore predefinito è true
, è necessario implementare solo il MoveItem
metodo della UICollectionViewDataSource
classe per supportare il trascinamento verso il riordinamento. Ad esempio:
public override void MoveItem (UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
// Reorder our list of items
...
}
Esempio di riordinamento semplice
Come esempio rapido, avviare un nuovo progetto Xamarin.iOS e modificare il file Main.storyboard . Trascinare un oggetto UICollectionViewController
nell'area di progettazione:
Selezionare la visualizzazione Raccolta (potrebbe essere più semplice eseguire questa operazione dalla struttura del documento). Nella scheda layout del riquadro proprietà impostare le dimensioni seguenti, come illustrato nello screenshot seguente:
- Dimensione cella: larghezza – 60 | Altezza - 60
- Dimensioni intestazione: larghezza - 0 | Altezza - 0
- Dimensioni piè di pagina: larghezza - 0 | Altezza - 0
- Spaziatura minima: per le celle - 8 | Per linee - 8
- Set di sezioni: Top – 16 | Bottom – 16 | Left – 16 | Destra - 16
Modificare quindi la cella predefinita:
- Modificare il colore di sfondo in blu
- Aggiungere un'etichetta per fungere da titolo per la cella
- Impostare l'identificatore di riutilizzo sulla cella
Aggiungere vincoli per mantenere l'etichetta centrata all'interno della cella man mano che cambia le dimensioni:
Nel riquadro delle proprietà per CollectionViewCell e impostare La classe su TextCollectionViewCell
:
Impostare la visualizzazione riutilizzabile raccolta su Cell
:
Infine, selezionare l'etichetta e denominarla TextLabel
:
Modificare la TextCollectionViewCell
classe e aggiungere le proprietà seguenti:
using System;
using Foundation;
using UIKit;
namespace CollectionView
{
public partial class TextCollectionViewCell : UICollectionViewCell
{
#region Computed Properties
public string Title {
get { return TextLabel.Text; }
set { TextLabel.Text = value; }
}
#endregion
#region Constructors
public TextCollectionViewCell (IntPtr handle) : base (handle)
{
}
#endregion
}
}
In questo caso la Text
proprietà dell'etichetta viene esposta come titolo della cella, in modo che possa essere impostata dal codice.
Aggiungere una nuova classe C# al progetto e chiamarla WaterfallCollectionSource
. Modificare il file e renderlo simile al seguente:
using System;
using Foundation;
using UIKit;
using System.Collections.Generic;
namespace CollectionView
{
public class WaterfallCollectionSource : UICollectionViewDataSource
{
#region Computed Properties
public WaterfallCollectionView CollectionView { get; set;}
public List<int> Numbers { get; set; } = new List<int> ();
#endregion
#region Constructors
public WaterfallCollectionSource (WaterfallCollectionView collectionView)
{
// Initialize
CollectionView = collectionView;
// Init numbers collection
for (int n = 0; n < 100; ++n) {
Numbers.Add (n);
}
}
#endregion
#region Override Methods
public override nint NumberOfSections (UICollectionView collectionView) {
// We only have one section
return 1;
}
public override nint GetItemsCount (UICollectionView collectionView, nint section) {
// Return the number of items
return Numbers.Count;
}
public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
// Get a reusable cell and set {~~it's~>its~~} title from the item
var cell = collectionView.DequeueReusableCell ("Cell", indexPath) as TextCollectionViewCell;
cell.Title = Numbers [(int)indexPath.Item].ToString();
return cell;
}
public override bool CanMoveItem (UICollectionView collectionView, NSIndexPath indexPath) {
// We can always move items
return true;
}
public override void MoveItem (UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath)
{
// Reorder our list of items
var item = Numbers [(int)sourceIndexPath.Item];
Numbers.RemoveAt ((int)sourceIndexPath.Item);
Numbers.Insert ((int)destinationIndexPath.Item, item);
}
#endregion
}
}
Questa classe sarà l'origine dati per la visualizzazione raccolta e fornirà le informazioni per ogni cella della raccolta.
Si noti che il MoveItem
metodo viene implementato per consentire il riordinamento degli elementi nella raccolta.
Aggiungere un'altra nuova classe C# al progetto e chiamarla WaterfallCollectionDelegate
. Modificare questo file e renderlo simile al seguente:
using System;
using Foundation;
using UIKit;
using System.Collections.Generic;
namespace CollectionView
{
public class WaterfallCollectionDelegate : UICollectionViewDelegate
{
#region Computed Properties
public WaterfallCollectionView CollectionView { get; set;}
#endregion
#region Constructors
public WaterfallCollectionDelegate (WaterfallCollectionView collectionView)
{
// Initialize
CollectionView = collectionView;
}
#endregion
#region Overrides Methods
public override bool ShouldHighlightItem (UICollectionView collectionView, NSIndexPath indexPath) {
// Always allow for highlighting
return true;
}
public override void ItemHighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
// Get cell and change to green background
var cell = collectionView.CellForItem(indexPath);
cell.ContentView.BackgroundColor = UIColor.FromRGB(183,208,57);
}
public override void ItemUnhighlighted (UICollectionView collectionView, NSIndexPath indexPath)
{
// Get cell and return to blue background
var cell = collectionView.CellForItem(indexPath);
cell.ContentView.BackgroundColor = UIColor.FromRGB(164,205,255);
}
#endregion
}
}
Questa operazione fungerà da delegato per la visualizzazione raccolta. I metodi sono stati sottoposti a override per evidenziare una cella mentre l'utente interagisce con esso nella visualizzazione raccolta.
Aggiungere un'ultima classe C# al progetto e chiamarla WaterfallCollectionView
. Modificare questo file e renderlo simile al seguente:
using System;
using UIKit;
using System.Collections.Generic;
using Foundation;
namespace CollectionView
{
[Register("WaterfallCollectionView")]
public class WaterfallCollectionView : UICollectionView
{
#region Constructors
public WaterfallCollectionView (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Initialize
DataSource = new WaterfallCollectionSource(this);
Delegate = new WaterfallCollectionDelegate(this);
}
#endregion
}
}
Si noti che DataSource
e Delegate
che sono stati creati in precedenza vengono impostati quando la visualizzazione raccolta viene costruita dal relativo storyboard (o file xib ).
Modificare di nuovo il file Main.storyboard e selezionare la visualizzazione raccolta e passare a Proprietà. Impostare Classe sulla classe personalizzata definita in precedenzaWaterfallCollectionView
:
Salvare le modifiche apportate all'interfaccia utente ed eseguire l'app. Se l'utente seleziona un elemento dall'elenco e lo trascina in una nuova posizione, gli altri elementi verranno animati automaticamente man mano che si spostano all'esterno dell'elemento. Quando l'utente elimina l'elemento in una nuova posizione, si sporge a tale posizione. Ad esempio:
Uso di un riconoscimento movimento personalizzato
Nei casi in cui non è possibile usare un UICollectionViewController
oggetto e deve usare un normale UIViewController
o se si vuole assumere un maggiore controllo sul movimento di trascinamento della selezione, è possibile creare un riconoscimento movimento personalizzato e aggiungerlo alla visualizzazione raccolta quando viene caricata la visualizzazione. Ad esempio:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Create a custom gesture recognizer
var longPressGesture = new UILongPressGestureRecognizer ((gesture) => {
// Take action based on state
switch(gesture.State) {
case UIGestureRecognizerState.Began:
var selectedIndexPath = CollectionView.IndexPathForItemAtPoint(gesture.LocationInView(View));
if (selectedIndexPath !=null) {
CollectionView.BeginInteractiveMovementForItem(selectedIndexPath);
}
break;
case UIGestureRecognizerState.Changed:
CollectionView.UpdateInteractiveMovementTargetPosition(gesture.LocationInView(View));
break;
case UIGestureRecognizerState.Ended:
CollectionView.EndInteractiveMovement();
break;
default:
CollectionView.CancelInteractiveMovement();
break;
}
});
// Add the custom recognizer to the collection view
CollectionView.AddGestureRecognizer(longPressGesture);
}
Di seguito vengono usati diversi nuovi metodi aggiunti alla visualizzazione raccolta per implementare e controllare l'operazione di trascinamento:
BeginInteractiveMovementForItem
- Contrassegna l'inizio di un'operazione di spostamento.UpdateInteractiveMovementTargetPosition
- Viene inviato quando viene aggiornata la posizione dell'elemento.EndInteractiveMovement
- Contrassegna la fine di uno spostamento di un elemento.CancelInteractiveMovement
- Contrassegna l'utente che annulla l'operazione di spostamento.
Quando l'applicazione viene eseguita, l'operazione di trascinamento funzionerà esattamente come lo strumento di riconoscimento movimento di trascinamento predefinito fornito con la visualizzazione raccolta.
Layout personalizzati e riordinamento
In iOS 9 sono stati aggiunti diversi nuovi metodi per lavorare con il trascinamento del riordinamento e i layout personalizzati in una visualizzazione raccolta. Per esplorare questa funzionalità, aggiungere un layout personalizzato alla raccolta.
Aggiungere prima di tutto una nuova classe C# chiamata WaterfallCollectionLayout
al progetto. Modificarlo e renderlo simile al seguente:
using System;
using Foundation;
using UIKit;
using System.Collections.Generic;
using CoreGraphics;
namespace CollectionView
{
[Register("WaterfallCollectionLayout")]
public class WaterfallCollectionLayout : UICollectionViewLayout
{
#region Private Variables
private int columnCount = 2;
private nfloat minimumColumnSpacing = 10;
private nfloat minimumInterItemSpacing = 10;
private nfloat headerHeight = 0.0f;
private nfloat footerHeight = 0.0f;
private UIEdgeInsets sectionInset = new UIEdgeInsets(0, 0, 0, 0);
private WaterfallCollectionRenderDirection itemRenderDirection = WaterfallCollectionRenderDirection.ShortestFirst;
private Dictionary<nint,UICollectionViewLayoutAttributes> headersAttributes = new Dictionary<nint, UICollectionViewLayoutAttributes>();
private Dictionary<nint,UICollectionViewLayoutAttributes> footersAttributes = new Dictionary<nint, UICollectionViewLayoutAttributes>();
private List<CGRect> unionRects = new List<CGRect>();
private List<nfloat> columnHeights = new List<nfloat>();
private List<UICollectionViewLayoutAttributes> allItemAttributes = new List<UICollectionViewLayoutAttributes>();
private List<List<UICollectionViewLayoutAttributes>> sectionItemAttributes = new List<List<UICollectionViewLayoutAttributes>>();
private nfloat unionSize = 20;
#endregion
#region Computed Properties
[Export("ColumnCount")]
public int ColumnCount {
get { return columnCount; }
set {
WillChangeValue ("ColumnCount");
columnCount = value;
DidChangeValue ("ColumnCount");
InvalidateLayout ();
}
}
[Export("MinimumColumnSpacing")]
public nfloat MinimumColumnSpacing {
get { return minimumColumnSpacing; }
set {
WillChangeValue ("MinimumColumnSpacing");
minimumColumnSpacing = value;
DidChangeValue ("MinimumColumnSpacing");
InvalidateLayout ();
}
}
[Export("MinimumInterItemSpacing")]
public nfloat MinimumInterItemSpacing {
get { return minimumInterItemSpacing; }
set {
WillChangeValue ("MinimumInterItemSpacing");
minimumInterItemSpacing = value;
DidChangeValue ("MinimumInterItemSpacing");
InvalidateLayout ();
}
}
[Export("HeaderHeight")]
public nfloat HeaderHeight {
get { return headerHeight; }
set {
WillChangeValue ("HeaderHeight");
headerHeight = value;
DidChangeValue ("HeaderHeight");
InvalidateLayout ();
}
}
[Export("FooterHeight")]
public nfloat FooterHeight {
get { return footerHeight; }
set {
WillChangeValue ("FooterHeight");
footerHeight = value;
DidChangeValue ("FooterHeight");
InvalidateLayout ();
}
}
[Export("SectionInset")]
public UIEdgeInsets SectionInset {
get { return sectionInset; }
set {
WillChangeValue ("SectionInset");
sectionInset = value;
DidChangeValue ("SectionInset");
InvalidateLayout ();
}
}
[Export("ItemRenderDirection")]
public WaterfallCollectionRenderDirection ItemRenderDirection {
get { return itemRenderDirection; }
set {
WillChangeValue ("ItemRenderDirection");
itemRenderDirection = value;
DidChangeValue ("ItemRenderDirection");
InvalidateLayout ();
}
}
#endregion
#region Constructors
public WaterfallCollectionLayout ()
{
}
public WaterfallCollectionLayout(NSCoder coder) : base(coder) {
}
#endregion
#region Public Methods
public nfloat ItemWidthInSectionAtIndex(int section) {
var width = CollectionView.Bounds.Width - SectionInset.Left - SectionInset.Right;
return (nfloat)Math.Floor ((width - ((ColumnCount - 1) * MinimumColumnSpacing)) / ColumnCount);
}
#endregion
#region Override Methods
public override void PrepareLayout ()
{
base.PrepareLayout ();
// Get the number of sections
var numberofSections = CollectionView.NumberOfSections();
if (numberofSections == 0)
return;
// Reset collections
headersAttributes.Clear ();
footersAttributes.Clear ();
unionRects.Clear ();
columnHeights.Clear ();
allItemAttributes.Clear ();
sectionItemAttributes.Clear ();
// Initialize column heights
for (int n = 0; n < ColumnCount; n++) {
columnHeights.Add ((nfloat)0);
}
// Process all sections
nfloat top = 0.0f;
var attributes = new UICollectionViewLayoutAttributes ();
var columnIndex = 0;
for (nint section = 0; section < numberofSections; ++section) {
// Calculate section specific metrics
var minimumInterItemSpacing = (MinimumInterItemSpacingForSection == null) ? MinimumColumnSpacing :
MinimumInterItemSpacingForSection (CollectionView, this, section);
// Calculate widths
var width = CollectionView.Bounds.Width - SectionInset.Left - SectionInset.Right;
var itemWidth = (nfloat)Math.Floor ((width - ((ColumnCount - 1) * MinimumColumnSpacing)) / ColumnCount);
// Calculate section header
var heightHeader = (HeightForHeader == null) ? HeaderHeight :
HeightForHeader (CollectionView, this, section);
if (heightHeader > 0) {
attributes = UICollectionViewLayoutAttributes.CreateForSupplementaryView (UICollectionElementKindSection.Header, NSIndexPath.FromRowSection (0, section));
attributes.Frame = new CGRect (0, top, CollectionView.Bounds.Width, heightHeader);
headersAttributes.Add (section, attributes);
allItemAttributes.Add (attributes);
top = attributes.Frame.GetMaxY ();
}
top += SectionInset.Top;
for (int n = 0; n < ColumnCount; n++) {
columnHeights [n] = top;
}
// Calculate Section Items
var itemCount = CollectionView.NumberOfItemsInSection(section);
List<UICollectionViewLayoutAttributes> itemAttributes = new List<UICollectionViewLayoutAttributes> ();
for (nint n = 0; n < itemCount; n++) {
var indexPath = NSIndexPath.FromRowSection (n, section);
columnIndex = NextColumnIndexForItem (n);
var xOffset = SectionInset.Left + (itemWidth + MinimumColumnSpacing) * (nfloat)columnIndex;
var yOffset = columnHeights [columnIndex];
var itemSize = (SizeForItem == null) ? new CGSize (0, 0) : SizeForItem (CollectionView, this, indexPath);
nfloat itemHeight = 0.0f;
if (itemSize.Height > 0.0f && itemSize.Width > 0.0f) {
itemHeight = (nfloat)Math.Floor (itemSize.Height * itemWidth / itemSize.Width);
}
attributes = UICollectionViewLayoutAttributes.CreateForCell (indexPath);
attributes.Frame = new CGRect (xOffset, yOffset, itemWidth, itemHeight);
itemAttributes.Add (attributes);
allItemAttributes.Add (attributes);
columnHeights [columnIndex] = attributes.Frame.GetMaxY () + MinimumInterItemSpacing;
}
sectionItemAttributes.Add (itemAttributes);
// Calculate Section Footer
nfloat footerHeight = 0.0f;
columnIndex = LongestColumnIndex();
top = columnHeights [columnIndex] - MinimumInterItemSpacing + SectionInset.Bottom;
footerHeight = (HeightForFooter == null) ? FooterHeight : HeightForFooter(CollectionView, this, section);
if (footerHeight > 0) {
attributes = UICollectionViewLayoutAttributes.CreateForSupplementaryView (UICollectionElementKindSection.Footer, NSIndexPath.FromRowSection (0, section));
attributes.Frame = new CGRect (0, top, CollectionView.Bounds.Width, footerHeight);
footersAttributes.Add (section, attributes);
allItemAttributes.Add (attributes);
top = attributes.Frame.GetMaxY ();
}
for (int n = 0; n < ColumnCount; n++) {
columnHeights [n] = top;
}
}
var i =0;
var attrs = allItemAttributes.Count;
while(i < attrs) {
var rect1 = allItemAttributes [i].Frame;
i = (int)Math.Min (i + unionSize, attrs) - 1;
var rect2 = allItemAttributes [i].Frame;
unionRects.Add (CGRect.Union (rect1, rect2));
i++;
}
}
public override CGSize CollectionViewContentSize {
get {
if (CollectionView.NumberOfSections () == 0) {
return new CGSize (0, 0);
}
var contentSize = CollectionView.Bounds.Size;
contentSize.Height = columnHeights [0];
return contentSize;
}
}
public override UICollectionViewLayoutAttributes LayoutAttributesForItem (NSIndexPath indexPath)
{
if (indexPath.Section >= sectionItemAttributes.Count) {
return null;
}
if (indexPath.Item >= sectionItemAttributes [indexPath.Section].Count) {
return null;
}
var list = sectionItemAttributes [indexPath.Section];
return list [(int)indexPath.Item];
}
public override UICollectionViewLayoutAttributes LayoutAttributesForSupplementaryView (NSString kind, NSIndexPath indexPath)
{
var attributes = new UICollectionViewLayoutAttributes ();
switch (kind) {
case "header":
attributes = headersAttributes [indexPath.Section];
break;
case "footer":
attributes = footersAttributes [indexPath.Section];
break;
}
return attributes;
}
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (CGRect rect)
{
var begin = 0;
var end = unionRects.Count;
List<UICollectionViewLayoutAttributes> attrs = new List<UICollectionViewLayoutAttributes> ();
for (int i = 0; i < end; i++) {
if (rect.IntersectsWith(unionRects[i])) {
begin = i * (int)unionSize;
}
}
for (int i = end - 1; i >= 0; i--) {
if (rect.IntersectsWith (unionRects [i])) {
end = (int)Math.Min ((i + 1) * (int)unionSize, allItemAttributes.Count);
break;
}
}
for (int i = begin; i < end; i++) {
var attr = allItemAttributes [i];
if (rect.IntersectsWith (attr.Frame)) {
attrs.Add (attr);
}
}
return attrs.ToArray();
}
public override bool ShouldInvalidateLayoutForBoundsChange (CGRect newBounds)
{
var oldBounds = CollectionView.Bounds;
return (newBounds.Width != oldBounds.Width);
}
#endregion
#region Private Methods
private int ShortestColumnIndex() {
var index = 0;
var shortestHeight = nfloat.MaxValue;
var n = 0;
// Scan each column for the shortest height
foreach (nfloat height in columnHeights) {
if (height < shortestHeight) {
shortestHeight = height;
index = n;
}
++n;
}
return index;
}
private int LongestColumnIndex() {
var index = 0;
var longestHeight = nfloat.MinValue;
var n = 0;
// Scan each column for the shortest height
foreach (nfloat height in columnHeights) {
if (height > longestHeight) {
longestHeight = height;
index = n;
}
++n;
}
return index;
}
private int NextColumnIndexForItem(nint item) {
var index = 0;
switch (ItemRenderDirection) {
case WaterfallCollectionRenderDirection.ShortestFirst:
index = ShortestColumnIndex ();
break;
case WaterfallCollectionRenderDirection.LeftToRight:
index = ColumnCount;
break;
case WaterfallCollectionRenderDirection.RightToLeft:
index = (ColumnCount - 1) - ((int)item / ColumnCount);
break;
}
return index;
}
#endregion
#region Events
public delegate CGSize WaterfallCollectionSizeDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, NSIndexPath indexPath);
public delegate nfloat WaterfallCollectionFloatDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, nint section);
public delegate UIEdgeInsets WaterfallCollectionEdgeInsetsDelegate(UICollectionView collectionView, WaterfallCollectionLayout layout, nint section);
public event WaterfallCollectionSizeDelegate SizeForItem;
public event WaterfallCollectionFloatDelegate HeightForHeader;
public event WaterfallCollectionFloatDelegate HeightForFooter;
public event WaterfallCollectionEdgeInsetsDelegate InsetForSection;
public event WaterfallCollectionFloatDelegate MinimumInterItemSpacingForSection;
#endregion
}
}
Questa classe può essere usata per fornire una colonna personalizzata a due colonne, il layout del tipo a cascata alla visualizzazione raccolta.
Il codice usa la codifica key-value (tramite i WillChangeValue
metodi e DidChangeValue
) per fornire il data binding per le proprietà calcolate in questa classe.
Modificare quindi e WaterfallCollectionSource
apportare le modifiche e le aggiunte seguenti:
private Random rnd = new Random();
...
public List<nfloat> Heights { get; set; } = new List<nfloat> ();
...
public WaterfallCollectionSource (WaterfallCollectionView collectionView)
{
// Initialize
CollectionView = collectionView;
// Init numbers collection
for (int n = 0; n < 100; ++n) {
Numbers.Add (n);
Heights.Add (rnd.Next (0, 100) + 40.0f);
}
}
Verrà creata un'altezza casuale per ognuno degli elementi che verranno visualizzati nell'elenco.
Modificare quindi la WaterfallCollectionView
classe e aggiungere la proprietà helper seguente:
public WaterfallCollectionSource Source {
get { return (WaterfallCollectionSource)DataSource; }
}
In questo modo sarà più semplice ottenere l'origine dati (e le altezze degli elementi) dal layout personalizzato.
Modificare infine il controller di visualizzazione e aggiungere il codice seguente:
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
var waterfallLayout = new WaterfallCollectionLayout ();
// Wireup events
waterfallLayout.SizeForItem += (collectionView, layout, indexPath) => {
var collection = collectionView as WaterfallCollectionView;
return new CGSize((View.Bounds.Width-40)/3,collection.Source.Heights[(int)indexPath.Item]);
};
// Attach the custom layout to the collection
CollectionView.SetCollectionViewLayout(waterfallLayout, false);
}
In questo modo viene creata un'istanza del layout personalizzato, viene impostato l'evento per fornire le dimensioni di ogni elemento e viene associato il nuovo layout alla visualizzazione raccolta.
Se si esegue di nuovo l'app Xamarin.iOS, la visualizzazione raccolta sarà simile alla seguente:
È comunque possibile trascinare per riordinare gli elementi come in precedenza, ma gli elementi cambieranno ora le dimensioni per adattarne la nuova posizione quando vengono rilasciati.
Modifiche alla visualizzazione raccolta
Nelle sezioni seguenti verranno esaminate in dettaglio le modifiche apportate a ogni classe nella visualizzazione raccolta da iOS 9.
UICollectionView
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionView
classe per iOS 9:
BeginInteractiveMovementForItem
: contrassegna l'inizio di un'operazione di trascinamento.CancelInteractiveMovement
: informa la visualizzazione raccolta che l'utente ha annullato un'operazione di trascinamento.EndInteractiveMovement
: informa la visualizzazione raccolta che l'utente ha completato un'operazione di trascinamento.GetIndexPathsForVisibleSupplementaryElements
: restituisce l'oggetto di un'intestazioneindexPath
o di un piè di pagina in una sezione della visualizzazione raccolta.GetSupplementaryView
: restituisce l'intestazione o il piè di pagina specificati.GetVisibleSupplementaryViews
: restituisce un elenco di tutte le intestazioni e i piè di pagina visibili.UpdateInteractiveMovementTargetPosition
: informa la visualizzazione raccolta che l'utente ha spostato o sta spostando un elemento durante un'operazione di trascinamento.
UICollectionViewController
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewController
classe in iOS 9:
InstallsStandardGestureForInteractiveMovement
: setrue
verrà usato il nuovo Riconoscimento movimenti che supporta automaticamente il trascinamento verso il riordinamento.CanMoveItem
: informa la visualizzazione raccolta se un determinato elemento può essere riordinato.GetTargetContentOffset
: consente di ottenere l'offset di un determinato elemento della visualizzazione raccolta.GetTargetIndexPathForMove
: ottiene l'oggettoindexPath
di un determinato elemento per un'operazione di trascinamento.MoveItem
: sposta l'ordine di un determinato elemento nell'elenco.
UICollectionViewDataSource
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewDataSource
classe in iOS 9:
CanMoveItem
: informa la visualizzazione raccolta se un determinato elemento può essere riordinato.MoveItem
: sposta l'ordine di un determinato elemento nell'elenco.
UICollectionViewDelegate
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewDelegate
classe in iOS 9:
GetTargetContentOffset
: consente di ottenere l'offset di un determinato elemento della visualizzazione raccolta.GetTargetIndexPathForMove
: ottiene l'oggettoindexPath
di un determinato elemento per un'operazione di trascinamento.
UICollectionViewFlowLayout
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewFlowLayout
classe in iOS 9:
SectionFootersPinToVisibleBounds
: attacca i piè di pagina della sezione ai limiti della visualizzazione raccolta visibile.SectionHeadersPinToVisibleBounds
: consente di associare le intestazioni di sezione ai limiti della visualizzazione raccolta visibile.
UICollectionViewLayout
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayout
classe in iOS 9:
GetInvalidationContextForEndingInteractiveMovementOfItems
: restituisce il contesto di invalidazione alla fine di un'operazione di trascinamento quando l'utente termina il trascinamento o lo annulla.GetInvalidationContextForInteractivelyMovingItems
: restituisce il contesto di invalidazione all'inizio di un'operazione di trascinamento.GetLayoutAttributesForInteractivelyMovingItem
: ottiene gli attributi di layout per un determinato elemento durante il trascinamento di un elemento.GetTargetIndexPathForInteractivelyMovingItem
: restituisce l'oggettoindexPath
dell'elemento in corrispondenza del punto specificato durante il trascinamento di un elemento.
UICollectionViewLayoutAttributes
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayoutAttributes
classe in iOS 9:
CollisionBoundingPath
: restituisce il percorso di collisione di due elementi durante un'operazione di trascinamento.CollisionBoundsType
: restituisce il tipo di collisione (comeUIDynamicItemCollisionBoundsType
) che si è verificato durante un'operazione di trascinamento.
UICollectionViewLayoutInvalidationContext
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewLayoutInvalidationContext
classe in iOS 9:
InteractiveMovementTarget
: restituisce l'elemento di destinazione di un'operazione di trascinamento.PreviousIndexPathsForInteractivelyMovingItems
: restituisce l'oggettoindexPaths
di altri elementi coinvolti in un'operazione di trascinamento per riordinare.TargetIndexPathsForInteractivelyMovingItems
: restituisce l'oggettoindexPaths
degli elementi che verranno riordinati in seguito a un'operazione di trascinamento del riordino.
UICollectionViewSource
Sono state apportate le modifiche o le aggiunte seguenti alla UICollectionViewSource
classe in iOS 9:
CanMoveItem
: informa la visualizzazione raccolta se un determinato elemento può essere riordinato.GetTargetContentOffset
: restituisce gli offset degli elementi che verranno spostati tramite un'operazione di trascinamento del riordinamento.GetTargetIndexPathForMove
: restituisce l'oggettoindexPath
di un elemento che verrà spostato durante un'operazione di trascinamento del riordino.MoveItem
: sposta l'ordine di un determinato elemento nell'elenco.
Riepilogo
Questo articolo ha illustrato le modifiche apportate alle visualizzazioni della raccolta in iOS 9 e ha descritto come implementarle in Xamarin.iOS. Ha trattato l'implementazione di una semplice azione di trascinamento del riordinamento in una visualizzazione raccolta; utilizzando un riconoscimento movimento personalizzato con trascinamento verso il riordinamento; e il modo in cui il trascinamento del riordinamento influisce su un layout di visualizzazione raccolta personalizzato.