Updates and selection
Keep collection snapshots current and respond to selection and scroll position.
Use ObservableCollection<T> when collection membership changes after the view appears:
SkeleKit coalesces source changes into animated native snapshots. Mutate the collection on the main thread. Bind item properties through INotifyPropertyChanged when the row stays in place but its displayed values change.
See data and templates for the distinction between item identity, membership changes, and bound property changes.
Virtualized sources
Implement IVirtualizedList<T> for a large or computed logical list that should be read by index instead of copied into a native snapshot:
An array, list, or observable collection keeps the snapshot path. A virtualized source uses a direct native data source, so the collection reads items only for the index paths that UIKit displays.
Expose a bound source as IVirtualizedList<T> so Bind preserves its virtualized type. For a direct assignment, wrap it with BindableList.Virtualized(source).
A virtualized source alone does not limit the layout work. The default grid layout still asks for every section when it solves the content. Pair the source with CollectionLayout.FixedGrid:
FixedGrid computes the content size and the item frames from the visible rectangle only. It reads one count per section when the geometry changes and never reads an offscreen section. It also keeps the visible section in place after a geometry change.
FixedGrid measures the first item, section header, and section footer template one time at each geometry build. All sections use the measured sizes. Give itemAspectRatio when the item keeps a shape, such as a square day cell. The collection measures again after a window resize or an accessibility text size change, and it keeps the visible section in place.
For a grouped source, implement IVirtualizedSectionList<TItem, TSection>. Its GetItemCount(section) and GetItem(section, item) methods must be cheap:
The section model still implements ISection<TItem>. If a realized section's item collection implements INotifyCollectionChanged, CollectionView observes it. Replacing an item with the same template rebinds only its visible cell; structural changes refresh the indexed layout. A virtualized top-level source can also implement INotifyCollectionChanged when its section or item structure changes.
FixedGrid does not support self-sizing items, SectionLayout, the collection Header and Footer, item accessories, selection checkmarks, swipe actions, drag reorder, or the native list style. The collection throws when a selection checkmark, a reorder command, or a swipe action is configured. Use CollectionLayout.Grid or CollectionLayout.List for those.
Taps and highlights
ItemCommand runs with the tapped item:
HighlightBackground belongs to the item template and defaults to the system gray. Set it to null for an item with no visual highlight. Without a selection binding, RetainsHighlight controls whether the tapped row stays highlighted until the page appears again; disable it for cards or controls where a retained highlight would be misleading.
The collection checks ItemCommand.CanExecute(item) before UIKit highlights or selects the item. Returning false prevents the pressed appearance and command execution for the collection. Set or bind IsEnabled on an individual ItemView to independently disable that rendered row, including its pressed appearance.
Selection
Bind exactly one of SelectedItem or SelectedItems:
SelectedItem is single selection: a tap replaces the selection, and a two-way binding pushes the tapped item to the source. Setting it in the ViewModel selects the matching row on first paint, and the selection survives snapshots, scrolling, and page appearances.
SelectedItems is multiple selection: taps toggle rows, and mutating the bound collection moves the selection:
Give SelectedItems an ObservableCollection so taps can write back. Bind either selection property, never both.
By default taps select whenever they press a row. Set SelectsOnlyWhileEditing = true when a normal tap should only activate and selection belongs to an edit button:
With that, normal taps run ItemCommand without selecting or highlighting the row, while edit mode toggles the selection circles and applies HighlightBackground. The bound selection is preserved when editing ends; clear it in your own Done command if the page should forget it.
Reference identity applies, like everywhere else in collections.
ShowsSelectionCheckmark draws a trailing checkmark on selected rows while not editing, tinted with the app tint; the edit-mode circles take over while editing.
Accessories
Item accessories are native affordances driven by the item, separate from collection selection:
CheckmarkAccessory and DisclosureAccessory need no configuration. DetailAccessory adds the info button and runs its command with the row's item when tapped:
LabelAccessory shows a trailing string such as a count:
Every accessory accepts IsVisible, Tint, and Display. IsVisible binds like any control property; a hidden accessory takes no layout space, and the row reflows like a native list row. Display picks Always, WhenEditing, or WhenNotEditing. A checkmark is green unless Tint overrides it.
Empty state
EmptyView replaces the collection content while the active source has no items. It is a SkeleKit view, so it can contain a retry command or progress state rather than only text.
Scrolling
Scrolled reports the vertical offset. Call ScrollTo(item, position, animated) to bring an existing item into view:
The requested object must be the same instance present in the source. ScrollPosition.Top, Center, and Bottom choose its alignment.
Virtualized sources can scroll directly by index without searching the logical list:
Set DefaultScrollPosition = ScrollPosition.Bottom when the newest content is at the end, such as a calendar or a chat. It is where the collection starts after the first layout, and where the system lands when the user taps the status bar, the selected tab, or the navigation bar. To run the same scroll from code, call ScrollTo(DefaultScrollPosition, animated: true). To move to any content boundary, call ScrollTo(ScrollPosition.Top) or ScrollTo(ScrollPosition.Bottom).
Loading near the end
Set LoadMoreCommand and LoadMoreThreshold to request another page before the user reaches the final item. The command fires once for a given item count. Adding results enables another request when the new end approaches; leaving the count unchanged prevents an immediate loop.
Keep a ViewModel loading flag and make the command's CanExecute reflect it so concurrent requests cannot overlap.
Pull-to-refresh, swipe actions, context menus, and edit mode are covered in refresh and editing.