Заполнение таблицы данными в Xamarin.iOS

Чтобы добавить строки в UITableViewUITableViewSource подкласс, необходимо переопределить методы, которые вызывает представление таблицы для заполнения.

В этом руководстве рассматриваются следующие сведения:

  • Подкласс пользовательского интерфейса UITableViewSource
  • Повторное использование ячеек
  • Добавление индекса
  • Добавление верхних и нижних колонтитулов

Подклассирование UITableViewSource

Подкласс UITableViewSource назначается каждому UITableView. Представление таблицы запрашивает исходный класс, чтобы определить, как отрисовка себя (например, сколько строк требуется и высота каждой строки, если она отличается от значения по умолчанию). Самое главное, источник предоставляет каждое представление ячеек, заполненное данными.

Существует только два обязательных метода, необходимых для создания данных отображения таблицы:

  • RowsInSection — возвращает nint количество общих строк данных, которое должно отображаться в таблице.
  • GetCell — возвращает заполненные UITableViewCell данные для соответствующего индекса строки, переданного методу.

Пример файла BasicTable TableSource.cs имеет простую реализацию UITableViewSource. В фрагменте кода ниже показано, что он принимает массив строк для отображения в таблице и возвращает стиль ячейки по умолчанию, содержащий каждую строку:

public class TableSource : UITableViewSource {

        string[] TableItems;
        string CellIdentifier = "TableCell";

        public TableSource (string[] items)
        {
            TableItems = items;
        }

        public override nint RowsInSection (UITableView tableview, nint section)
        {
            return TableItems.Length;
        }

        public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
        {
            UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
            string item = TableItems[indexPath.Row];

            //if there are no cells to reuse, create a new one
            if (cell == null)
            { 
                cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier); 
            }

            cell.TextLabel.Text = item;

            return cell;
        }
}

Можно UITableViewSource использовать любую структуру данных из простого массива строк (как показано в этом примере) в список <> или другую коллекцию. Реализация UITableViewSource методов изолирует таблицу от базовой структуры данных.

Чтобы использовать этот подкласс, создайте массив строк для создания источника, а затем назначьте его экземпляру UITableView:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    table = new UITableView(View.Bounds); // defaults to Plain style
    string[] tableItems = new string[] {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"};
    table.Source = new TableSource(tableItems);
    Add (table);
}

Результирующая таблица выглядит следующим образом:

Sample table running

Большинство таблиц позволяют пользователю касаться строки, чтобы выбрать ее и выполнить другое действие (например, воспроизведение песни или вызов контакта или отображение другого экрана). Для этого необходимо выполнить несколько действий. Сначала создадим AlertController для отображения сообщения, когда пользователь щелкает строку, добавив следующий RowSelected метод:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    UIAlertController okAlertController = UIAlertController.Create ("Row Selected", tableItems[indexPath.Row], UIAlertControllerStyle.Alert);
    okAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
    ...

    tableView.DeselectRow (indexPath, true);
}

Затем создайте экземпляр контроллера представления:

HomeScreen owner;

Добавьте конструктор в класс UITableViewSource, который принимает контроллер представления в качестве параметра и сохраняет его в поле:

public TableSource (string[] items, HomeScreen owner)
{
    ...
    this.owner = owner;

}

Измените метод ViewDidLoad, в котором создается класс UITableViewSource для передачи this ссылки:

table.Source = new TableSource(tableItems, this);

Наконец, снова в RowSelected методе вызовите PresentViewController кэшированное поле:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    ...
    owner.PresentViewController (okAlertController, true, null);

    ...
}

Теперь пользователь может коснуться строки и появится оповещение:

The row selected alert

Повторное использование ячеек

В этом примере есть только шесть элементов, поэтому не требуется повторное использование ячеек. При отображении сотен или тысяч строк, однако, это была бы трата памяти, чтобы создать сотни или тысячи UITableViewCell объектов, когда только несколько раз помещаются на экране.

Чтобы избежать этой ситуации, когда ячейка исчезает с экрана, его представление помещается в очередь для повторного использования. По мере прокрутки пользователя таблица вызывает GetCell запрос на отображение новых представлений , чтобы повторно использовать существующую ячейку (которая в настоящее время не отображается) просто вызовите DequeueReusableCell метод. Если ячейка доступна для повторного использования, она будет возвращена, в противном случае возвращается значение NULL, и код должен создать новый экземпляр ячейки.

Этот фрагмент кода из примера демонстрирует шаблон:

// request a recycled cell to save memory
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
// if there are no cells to reuse, create a new one
if (cell == null)
    cell = new UITableViewCell (UITableViewCellStyle.Default, cellIdentifier);

Фактически создаются cellIdentifier отдельные очереди для разных типов ячеек. В этом примере все ячейки выглядят одинаково, поэтому используется только один жестко закодированный идентификатор. Если у каждой ячейки были разные типы, каждая из них должна иметь другую строку идентификатора, как при создании экземпляра, так и при запросе из очереди повторного использования.

Повторное использование ячеек в iOS 6+

IOS 6 добавил шаблон повторного использования ячеек, аналогичный одному введение в представления коллекции. Хотя существующий шаблон повторного использования, показанный выше, по-прежнему поддерживается для обратной совместимости, этот новый шаблон предпочтительнее, так как он удаляет потребность в значении NULL проверка в ячейке.

С помощью нового шаблона приложение регистрирует класс ячеек или xib для использования путем вызова либо RegisterClassForCellReuseRegisterNibForCellReuse в конструкторе контроллера. Затем при отмене ячейки в GetCell методе просто вызовите DequeueReusableCell идентификатор, зарегистрированный для класса ячейки или xib, и путь к индексу.

Например, следующий код регистрирует пользовательский класс ячеек в UITableViewController:

public class MyTableViewController : UITableViewController
{
  static NSString MyCellId = new NSString ("MyCellId");

  public MyTableViewController ()
  {
    TableView.RegisterClassForCellReuse (typeof(MyCell), MyCellId);
  }
  ...
}

С зарегистрированным классом MyCell ячейку можно отклонить в GetCell методе UITableViewSource без необходимости дополнительного значения NULL проверка, как показано ниже:

class MyTableSource : UITableViewSource
{
  public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
  {
    // if cell is not available in reuse pool, iOS will create one automatically
    // no need to do null check and create cell manually
    var cell = (MyCell) tableView.DequeueReusableCell (MyCellId, indexPath);

    // do whatever you need to with cell, such as assigning properties, etc.

    return cell;
  }
}

Помните, что при использовании нового шаблона повторного использования с пользовательским классом ячеек необходимо реализовать конструктор, принимающий фрагмент IntPtrкода, как показано в фрагменте ниже, в противном случае Objective-C не удастся создать экземпляр класса ячейки:

public class MyCell : UITableViewCell
{
  public MyCell (IntPtr p):base(p)
  {
  }
  ...
}

Примеры тем, описанных выше, см. в примере BasicTable, связанном с этой статьей.

Добавление индекса

Индекс помогает пользователю прокручивать длинные списки, обычно упорядоченно в алфавитном порядке, хотя можно индексировать по любым критериям, которые вы хотите. Пример BasicTableIndex загружает гораздо более длинный список элементов из файла, чтобы продемонстрировать индекс. Каждый элемент в индексе соответствует разделу таблицы.

The Index display

Для поддержки разделов данные, лежащие в таблице, необходимо сгруппировать, поэтому пример BasicTableIndex создает Dictionary<> из массива строк, используя первую букву каждого элемента в качестве ключа словаря:

indexedTableItems = new Dictionary<string, List<string>>();
foreach (var t in items) {
    if (indexedTableItems.ContainsKey (t[0].ToString ())) {
        indexedTableItems[t[0].ToString ()].Add(t);
    } else {
        indexedTableItems.Add (t[0].ToString (), new List<string>() {t});
    }
}
keys = indexedTableItems.Keys.ToArray ();

Затем UITableViewSource подклассу требуются следующие методы, добавленные или измененные для использования Dictionary<> :

  • NumberOfSections — этот метод является необязательным, по умолчанию в таблице предполагается один раздел. При отображении индекса этот метод должен возвращать количество элементов в индексе (например, 26, если индекс содержит все буквы английского алфавита).
  • RowsInSection — возвращает количество строк в определенном разделе.
  • SectionIndexTitles — возвращает массив строк, которые будут использоваться для отображения индекса. Пример кода возвращает массив букв.

Обновленные методы в примере файла BasicTableIndex/TableSource.cs выглядят следующим образом:

public override nint NumberOfSections (UITableView tableView)
{
    return keys.Length;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
    return indexedTableItems[keys[section]].Count;
}
public override string[] SectionIndexTitles (UITableView tableView)
{
    return keys;
}

Индексы обычно используются только с стилем таблицы "Обычный".

Добавление верхних и нижних колонтитулов

Верхние и нижние колонтитулы можно использовать для визуальной группировки строк в таблице. Структура данных, требуемая, очень похожа на добавление индекса — Dictionary<> работает очень хорошо. Вместо использования алфавита для группировки ячеек этот пример будет группировать овощи по ботаническому типу. Выходные данные выглядят следующим образом.

Sample Headers and Footers

Для отображения верхних и нижних колонтитулов UITableViewSource подклассу требуются следующие дополнительные методы:

  • TitleForHeader — возвращает текст, используемый в качестве заголовка.
  • TitleForFooter — возвращает текст, используемый в качестве нижнего колонтитула.

Обновленные методы в примере файла BasicTableHeaderFooter/Code/TableSource.cs выглядят следующим образом:

public override string TitleForHeader (UITableView tableView, nint section)
{
    return keys[section];
}
public override string TitleForFooter (UITableView tableView, nint section)
{
    return indexedTableItems[keys[section]].Count + " items";
}

Вы можете дополнительно настроить внешний вид верхнего и нижнего колонтитулов с помощью объекта View, используя GetViewForHeader переопределения UITableViewSourceметодаGetViewForFooter.